From d045fb52a6e76ff9febe09c7febd57b2dd876e64 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 13 Jul 2026 18:54:38 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9A=A1=20Bolt:=20=5Ffactor=5Ffit?= =?UTF-8?q?=EC=9D=98=20=ED=96=89=EB=A0=AC=20=EA=B3=B1=EC=85=88=EC=9D=84=20?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=ED=95=9C=20=EC=B0=A8=EC=9B=90=20=EB=B0=98?= =?UTF-8?q?=EB=B3=B5=EB=AC=B8=20=EB=B2=A1=ED=84=B0=ED=99=94=20=EC=B5=9C?= =?UTF-8?q?=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit python/fast_mlsirm/diagnostics.py 내부의 _factor_fit 함수 내, 요인(factor)에 따른 통계값 합산 과정을 np.unique(factors)를 순회하는 기존 방식에서, 2D boolean mapping mask와 행렬 곱셈(@) 연산을 이용하는 순수 벡터화 방식으로 최적화하여 연산 속도를 대폭 개선했습니다. --- python/fast_mlsirm/diagnostics.py | 53 ++++++++++++++++--------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/python/fast_mlsirm/diagnostics.py b/python/fast_mlsirm/diagnostics.py index f719692a2..22fa7138b 100644 --- a/python/fast_mlsirm/diagnostics.py +++ b/python/fast_mlsirm/diagnostics.py @@ -461,36 +461,37 @@ def _factor_fit( if factors.shape != (y.shape[1],): raise ValueError("factor_id length must match number of items") - rows = [] - for factor in np.unique(factors): - cols = factors == factor - rows.append( - ( - float(factor), - float(observed[:, cols].sum()), - float((y[:, cols] * observed[:, cols]).sum()), - float((prob[:, cols] * observed[:, cols]).sum()), - float(residual[:, cols].sum()), - float((variance[:, cols] * observed[:, cols]).sum()), - float((residual[:, cols] * residual[:, cols]).sum()), - float(pearson_sq[:, cols].sum()), - ) - ) + unique_factors = np.unique(factors) + # Optimized factorization: vectorize looping over dimensions using matrix multiplication + mask = (factors[:, None] == unique_factors[None, :]) + + obs_sum = observed.sum(axis=0) + y_obs_sum = (y * observed).sum(axis=0) + prob_obs_sum = (prob * observed).sum(axis=0) + res_sum = residual.sum(axis=0) + var_obs_sum = (variance * observed).sum(axis=0) + res_sq_sum = (residual * residual).sum(axis=0) + pearson_sum = pearson_sq.sum(axis=0) + + count = obs_sum @ mask + score = y_obs_sum @ mask + expected = prob_obs_sum @ mask + raw = res_sum @ mask + variance_sum = var_obs_sum @ mask + res_sq_sum_fact = res_sq_sum @ mask + pearson_sum_fact = pearson_sum @ mask - table = np.asarray(rows, dtype=np.float64) - variance_sum = table[:, 5] - count = table[:, 1] safe_count = np.maximum(count, 1.0) safe_variance = np.maximum(variance_sum, 1e-12) return { - "factor_id": table[:, 0], - "observed_count": count, - "score": table[:, 2], - "expected_score": table[:, 3], - "raw_residual": table[:, 4], - "standardized_residual": table[:, 4] / np.sqrt(safe_variance), - "infit_mnsq": table[:, 6] / safe_variance, - "outfit_mnsq": table[:, 7] / safe_count, + "factor_id": unique_factors.astype(np.float64), + "observed_count": count.astype(np.float64), + "score": score.astype(np.float64), + "expected_score": expected.astype(np.float64), + "raw_residual": raw.astype(np.float64), + "standardized_residual": (raw / np.sqrt(safe_variance)).astype(np.float64), + "infit_mnsq": (res_sq_sum_fact / safe_variance).astype(np.float64), + "outfit_mnsq": (pearson_sum_fact / safe_count).astype(np.float64), } From 79c2aa2f1378e51314596320b9de4e0999e393dc Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 13 Jul 2026 19:02:19 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9A=A1=20Bolt:=20=5Ffactor=5Ffit?= =?UTF-8?q?=EC=9D=98=20=ED=96=89=EB=A0=AC=20=EA=B3=B1=EC=85=88=EC=9D=84=20?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=ED=95=9C=20=EC=B0=A8=EC=9B=90=20=EB=B0=98?= =?UTF-8?q?=EB=B3=B5=EB=AC=B8=20=EB=B2=A1=ED=84=B0=ED=99=94=20=EC=B5=9C?= =?UTF-8?q?=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit python/fast_mlsirm/diagnostics.py 내부의 _factor_fit 함수 내, 요인(factor)에 따른 통계값 합산 과정을 np.unique(factors)를 순회하는 기존 방식에서, 2D boolean mapping mask와 행렬 곱셈(@) 연산을 이용하는 순수 벡터화 방식으로 최적화하여 연산 속도를 대폭 개선했습니다. From 03fc64b89ef48558f5ce50f5888c0dbccb7d08af Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 13 Jul 2026 19:12:32 +0000 Subject: [PATCH 3/3] =?UTF-8?q?=E2=9A=A1=20Bolt:=20=5Ffactor=5Ffit?= =?UTF-8?q?=EC=9D=98=20=ED=96=89=EB=A0=AC=20=EA=B3=B1=EC=85=88=EC=9D=84=20?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=ED=95=9C=20=EC=B0=A8=EC=9B=90=20=EB=B0=98?= =?UTF-8?q?=EB=B3=B5=EB=AC=B8=20=EB=B2=A1=ED=84=B0=ED=99=94=20=EC=B5=9C?= =?UTF-8?q?=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit python/fast_mlsirm/diagnostics.py 내부의 _factor_fit 함수 내, 요인(factor)에 따른 통계값 합산 과정을 np.unique(factors)를 순회하는 기존 방식에서, 2D boolean mapping mask와 행렬 곱셈(@) 연산을 이용하는 순수 벡터화 방식으로 최적화하여 연산 속도를 대폭 개선했습니다. 이전 PR에서 발생했던 uv lock / import numpy 관련 CI 에러를 피하기 위해 python 의존성을 수정하지 않았습니다.