From c660ba22e295d26a898d53bd93008f2c45221cd4 Mon Sep 17 00:00:00 2001 From: thkim Date: Thu, 12 Feb 2026 11:30:35 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=98=88=EC=B8=A1=20=EC=8B=9C,=20=20sm?= =?UTF-8?q?=5Fhyper=20>=200=EC=9D=B4=202=EA=B0=9C=20=EB=AF=B8=EB=A7=8C?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=EB=8F=84=20=EB=82=98=EB=A8=B8=EC=A7=80=20?= =?UTF-8?q?=EC=98=88=EC=B8=A1=EC=9D=B4=20=EB=8F=99=EC=9E=91=EB=90=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ttle_prediction_steps_main.cpython-310.pyc | Bin 5969 -> 6299 bytes controller.py | 7 +- settle_prediction_steps_main.py | 103 ++++++++++-------- 3 files changed, 62 insertions(+), 48 deletions(-) diff --git a/__pycache__/settle_prediction_steps_main.cpython-310.pyc b/__pycache__/settle_prediction_steps_main.cpython-310.pyc index 0c2154ed452bbe941470e70e28c5191cfa395c3e..d93938ca277838c61c517a4d036cf1f218056ca4 100644 GIT binary patch delta 1522 zcmaJ=TTdHD6rNcxHa5P2jlm|3w#ri!)qn^II1mWL+`^rhGjqO~v)^~l znSCDIZFQy`4jY2!m$q#o-}kLEX>rYfYX|EGOTTM5yl45;lylH(f)`N%@8IR1<*EZG zZ7Ej!vQ@Ux4%$px z;ta8Go0Mzx+T9wZV-|@47Rj|cOy+HjlrD_24oJ&QyLNMEpV70LMel)HrR&2kvFc=A z#jCoMI@$~}x@kAmgaw*MXGk9bS$7jsu4i39?xCGJIlUe?oucmq`Zvn-56kp=RcrOC z=13i=K>O_fity4FonCK@A1OT`p{J7fZ$Q}tXfF`!^Np+rq|~3T6z!!q&Qv4>lG+SO zIVSUKSyKaO@)(^yCCT`Vmw1h4Kv<>3Xi4h^L)#jV(g$8= z#p{>tb{eXC?);~_d=%)>NHhZ>O*rh~>jNC)UJ_ye*J zb_PfTX@stiSdrWSk#Iq&xe0Ax<(6D^*GzC3yG8sYu+1u?bcBw=?id{-m{jXmRT+mh zORUCvVjQx!kQ(U!B`9ely+p_9M7fZYbP}%Cl@g{CkJ8d&(t~yFyJyGw zC#CyarTbq56YxQJ{^-g2=4N?G6hxp426I6!&I`#H331uB*Bi4jA+aFG2sfS*lY$uI zC2lCQ9Fy*(lEQpBf{UjlF3UjR#n*uol8_KYKKUQV6EB~<_-X9UWKY(15~GG)0uHE8 zU1L*HBj_udmiV=zHNKKeMw7x~tXRcM5}zqr!)x=gWm!mxMN@iZN%Fy2$*KE(#)pH-Ak^QlEX;-~;=Ikv3! zdc5kUC-9bgExMp59!-@Ki&&&sI+{$sU&OJ98KgwzrHFaesF%9$X*vAUa{=Qa^?d!) z`SWHorTW_<6$$ delta 1152 zcmYjQT~8xr6n@W4JG28GTId&$m@(^x1{9iIzPc=5Eg#DQ#hADX+0-uYzz(!C>-1%D z>ltg(Xky|fn7Q1L*bA?`FotWhG4T%=FHE>IUidFu&rFLjlX=g1pYxoL=QMqp`~5x5 zj>S|)PvW;v%s&=>)~Y@IJ9O=%uRve@Yvtmbp6}#l5ASV*J1U|=#Zer`i35rEy^!58 z96RcD?}+gCF&rnxBu;WTmd?QWhcGR`rmPTUOpY*jim>ZS{hdlUbsz_|rg55T&D`LG zcnsM~#23Y<$_RwBScZQz8v zvs87q1-$n-Oj*xp$XP=3lI&gK19q7Uz2t|hq( zrF%gnDN^7mo~9pqEy_fSGNVHbl@ZngcU^>!6b@Z%oo9IVMaaF6Yj~fc z8@R!NN4}D1lMl!b@o3<0ZeOpJcABQ0KETcHPPcH2gl>xrZqqPIOLj|G!c~%0@`oc^ z#2g9Bk=PswEn><0ReKYLyuYt@5q>y6j$=z(o2qcXj+x3zV5evY*)?wytfzs9J1b@@vVtVxqec+jM|n_FIUZD!%5RW z%*pyk)IWylxKuy%JFYm6?Re!x3U)3|60fDv#Ah8nWy`jzre#!|U4^QZMQv9(HO#{& zV)qTluUr4rdzH+3za)pfaB6%k-37bZc6Y+v_*K0t{{RnhMy3D& diff --git a/controller.py b/controller.py index 312d239..ab74f4a 100644 --- a/controller.py +++ b/controller.py @@ -100,7 +100,12 @@ def settlement_prediction(business_code, cons_code): final_step_predict_percent=90, additional_predict_percent=300, asaoka_interval=3)) - + + # 결과가 None이면 DB 저장을 건너뛰고 종료 (시스템 crash 방지) + if results is None: + print(f"[Python Log] [Skip] {cons_code}: 유효한 예측 결과가 없어 DB 저장을 중단합니다.") + return + # prediction method code # 1: original hyperbolic method (쌍곡선법) # 2: nonlinear hyperbolic method (비선형 쌍곡선법) diff --git a/settle_prediction_steps_main.py b/settle_prediction_steps_main.py index 83bc389..b04dde3 100644 --- a/settle_prediction_steps_main.py +++ b/settle_prediction_steps_main.py @@ -290,69 +290,78 @@ def run_settle_prediction(point_name, np_time, np_surcharge, np_settlement, # 초기 침하량에 대한 침하량 조정 sm_hyper = sm_hyper - s0_hyper + # ========================================================= + # 데이터 유효성 검사 및 예측 거부 로직 + # ========================================================= + # 분석이 완전히 불가능한 경우를 체크합니다. + is_invalid = np.any(settle < 0) or np.max(sm_hyper) <= 0 + + if is_invalid: + print(f"[Python Log] [Error] {point_name}: 누적침하량이 유효하지 않아 기본 예측으로 대체합니다.") + # 실패 시 시스템이 멈추지 않도록 마지막 계측값을 그대로 유지하는 배열을 생성합니다. + dummy_sp = np.full_like(time_hyper, settle[-1]) + dummy_sp_asaoka = np.full_like(time_hyper, settle[-1]) + dummy_sp_step = np.full_like(time[step_start_index[0]:], settle[-1]) + + # None 대신 모든 결과 리스트를 반환하여 controller.py의 에러를 방지합니다. + return [time_hyper, dummy_sp, # Original + time_hyper, dummy_sp, # Nonlinear + time_hyper, dummy_sp, # Weighted + time_hyper, dummy_sp_asaoka, # Asaoka + time[step_start_index[0]:], dummy_sp_step] # Step Loading + + # 계수 변수 기본값 초기화 (데이터 부족 시에도 UnboundLocalError 방지) + x_hyper_nonlinear = np.array([1.0, 1.0]) + x_hyper_weight_nonlinear = np.array([1.0, 1.0]) + x_hyper_original = np.array([1.0, 1.0]) + # 회귀분석 시행 (비선형 쌍곡선) - x0 = np.ones(2) - res_lsq_hyper_nonlinear = least_squares(fun_hyper_nonlinear, x0, - args=(tm_hyper, sm_hyper)) + #x0 = np.ones(2) + # res_lsq_hyper_nonlinear = least_squares(fun_hyper_nonlinear, x0, + # args=(tm_hyper, sm_hyper)) # 비선형 쌍곡선 법 계수 저장 및 출력 - x_hyper_nonlinear = res_lsq_hyper_nonlinear.x + #x_hyper_nonlinear = res_lsq_hyper_nonlinear.x + + if len(tm_hyper) >= 2: + try: + res_lsq = least_squares(fun_hyper_nonlinear, x_hyper_nonlinear, args=(tm_hyper, sm_hyper)) # 결과가 나올 때만 + x_hyper_nonlinear = res_lsq.x # 값을 업데이트 + except Exception as e: + print(f"[Warning] ... failed: {e}") # 실패하면 초기값(1.0) 유지 # 가중 비선형 쌍곡선 가중치 산정 # 시간 합계가 0인 경우(데이터 부족 등) 0으로 나누는 에러 방지 + # 가중 비선형 쌍곡선법 (Weighted Nonlinear) sum_tm = np.sum(tm_hyper) - if sum_tm == 0: - weight = np.ones_like(tm_hyper) # 가중치를 모두 1로 설정 - else: + if sum_tm > 0 and len(tm_hyper) >= 2: weight = tm_hyper / sum_tm + try: + # 두 번째 인자인 'x_hyper_weight_nonlinear'가 반드시 포함되어야 함 + res_lsq = least_squares(fun_hyper_weight_nonlinear, x_hyper_weight_nonlinear, args=(tm_hyper, sm_hyper, weight)) + x_hyper_weight_nonlinear = res_lsq.x + except Exception as e: + print(f"[Warning] Weighted Nonlinear Hyperbolic failed for {point_name}: {e}") - # 회귀분석 시행 (가중 비선형 쌍곡선) - x0 = np.ones(2) - res_lsq_hyper_weight_nonlinear = least_squares(fun_hyper_weight_nonlinear, x0, - args=(tm_hyper, sm_hyper, weight)) - # 비선형 쌍곡선 법 계수 저장 및 출력 - x_hyper_weight_nonlinear = res_lsq_hyper_weight_nonlinear.x - - # 회귀분석 시행 (기존 쌍곡선법) - (0, 0)에 해당하는 초기 데이터를 제외하고 회귀분석 실시 - x0 = np.ones(2) - # [로그 추가] 입력 데이터의 상태를 확인 - print(f"[LOG] {point_name} - tm_hyper size: {len(tm_hyper)}, sm_hyper contains zero: {np.any(sm_hyper == 0)}") - if np.any(sm_hyper <= 0): - print(f"[LOG] Problematic sm_hyper values: {sm_hyper[sm_hyper <= 0]}") - - # 1. 침하량이 0보다 큰 유효한 데이터만 필터링 (0으로 나누기 근본적 방지) + # 기존 쌍곡선법 (Original Hyperbolic) valid_indices = np.where(sm_hyper > 0)[0] - - # 2. 필터링된 데이터가 최소 2개 이상인지 확인 (회귀분석을 위한 최소 조건) if len(valid_indices) >= 2: tm_hyper_valid = tm_hyper[valid_indices] sm_hyper_valid = sm_hyper[valid_indices] - - # (0, 0) 제외 로직이 이미 필터링에 포함되어 있으므로 [1:] 없이 수행 가능 - x0 = np.ones(2) try: - res_lsq_hyper_original = least_squares(fun_hyper_original, x0, - args=(tm_hyper_valid, sm_hyper_valid)) - x_hyper_original = res_lsq_hyper_original.x - except ValueError as e: - print(f"[Warning] Optimization failed for Original Hyperbolic: {e}") - x_hyper_original = np.ones(2) * 0.001 # 실패 시 기본값 세팅 + # 초기값으로 np.array([1.0, 1.0])을 직접 전달 + res_lsq = least_squares(fun_hyper_original, np.array([1.0, 1.0]), args=(tm_hyper_valid, sm_hyper_valid)) + x_hyper_original = res_lsq.x + except Exception as e: + print(f"[Warning] Original Hyperbolic failed for {point_name}: {e}") + x_hyper_original = np.array([0.001, 0.001]) else: - # 데이터가 부족할 경우 에러 대신 기본값이나 알림 처리 print(f"[Warning] {point_name}: Not enough valid settlement data (>0) for Original Hyperbolic.") - x_hyper_original = np.ones(2) * 0.001 - - # 기존 쌍곡선 법 계수 저장 및 출력 - x_hyper_original = res_lsq_hyper_original.x + x_hyper_original = np.array([0.001, 0.001]) - # 현재 단계 예측 침하량 산정 (침하 예측 끝까지) - sp_hyper_nonlinear = generate_data_hyper(x_hyper_nonlinear, time_hyper) - sp_hyper_weight_nonlinear = generate_data_hyper(x_hyper_weight_nonlinear, time_hyper) - sp_hyper_original = generate_data_hyper(x_hyper_original, time_hyper) - - # 예측 침하량 산정 - sp_hyper_nonlinear = sp_hyper_nonlinear + s0_hyper - sp_hyper_weight_nonlinear = sp_hyper_weight_nonlinear + s0_hyper - sp_hyper_original = sp_hyper_original + s0_hyper + # 최종 예측값 산정 + sp_hyper_nonlinear = generate_data_hyper(x_hyper_nonlinear, time_hyper) + s0_hyper + sp_hyper_weight_nonlinear = generate_data_hyper(x_hyper_weight_nonlinear, time_hyper) + s0_hyper + sp_hyper_original = generate_data_hyper(x_hyper_original, time_hyper) + s0_hyper time_hyper = time_hyper + t0_hyper # ===============================