Spaces:
Sleeping
Sleeping
| # utils/prediction_utils.py | |
| import warnings | |
| warnings.filterwarnings("ignore") | |
| import numpy as np | |
| # Prophet ์๋, ์คํจ ์ Holt(์ง์ ํํ) ํด๋ฐฑ | |
| try: | |
| from prophet import Prophet | |
| _HAS_PROPHET = True | |
| except Exception: | |
| _HAS_PROPHET = False | |
| try: | |
| from statsmodels.tsa.holtwinters import ExponentialSmoothing | |
| _HAS_HOLT = True | |
| except Exception: | |
| _HAS_HOLT = False | |
| import pandas as pd | |
| def prophet_emotion_forecast(scores, steps=3): | |
| """ | |
| Prophet ๊ธฐ๋ฐ ์์ธก. ์ ๋ ฅ: ์ ์ ๋ฆฌ์คํธ(0~100) | |
| ๋ฐํ: ์์ธก ์ ์ ๋ฆฌ์คํธ ๊ธธ์ด=steps (float) | |
| ์ฐธ๊ณ : Taylor & Letham (2018) 'Forecasting at Scale' | |
| """ | |
| if not _HAS_PROPHET or len(scores) < 5: | |
| return None | |
| df = pd.DataFrame({ | |
| 'ds': pd.date_range(start='2023-01-01', periods=len(scores), freq='D'), | |
| 'y': scores | |
| }) | |
| model = Prophet( | |
| daily_seasonality=False, weekly_seasonality=False, yearly_seasonality=False, | |
| changepoint_prior_scale=0.2 # ๋ณํ์ ๋ฏผ๊ฐ๋ | |
| ) | |
| model.fit(df) | |
| future = model.make_future_dataframe(periods=steps) | |
| forecast = model.predict(future) | |
| tail = forecast[['yhat']].tail(steps)['yhat'].to_list() | |
| # ์์ ํด๋ฆฌํ | |
| return [float(np.clip(x, 0, 100)) for x in tail] | |
| def holt_fallback_forecast(scores, steps=3): | |
| """ | |
| Holt ์ง์ํํ(์ถ์ธํ) ํด๋ฐฑ ์์ธก. | |
| Prophet ์ฌ์ฉ ๋ถ๊ฐ ํ๊ฒฝ์์ ๊ฐ๋จํ๊ณ ์์ ์ . | |
| """ | |
| if not _HAS_HOLT or len(scores) < 4: | |
| return None | |
| try: | |
| model = ExponentialSmoothing( | |
| np.array(scores, dtype=float), | |
| trend='add', seasonal=None | |
| ).fit() | |
| fcast = model.forecast(steps) | |
| return [float(np.clip(x, 0, 100)) for x in fcast.tolist()] | |
| except Exception: | |
| return None | |
| def forecast_scores(scores, steps=3): | |
| """ | |
| ํตํฉ ์์ธก ์์ง: | |
| 1) Prophet ์ฑ๊ณต ์ Prophet ์ฌ์ฉ | |
| 2) ์๋๋ฉด Holt ํด๋ฐฑ | |
| 3) ๊ทธ๋๋ ์ ๋๋ฉด, ๋ง์ง๋ง ๊ธฐ์ธ๊ธฐ ์ ํ ์ธ์ฝ(๊ทน๋จ ํด๋ฐฑ) | |
| """ | |
| if len(scores) < 2: | |
| return [scores[-1]] * steps if scores else [0.0]*steps | |
| yhat = prophet_emotion_forecast(scores, steps=steps) | |
| if yhat is not None: | |
| return yhat | |
| yhat = holt_fallback_forecast(scores, steps=steps) | |
| if yhat is not None: | |
| return yhat | |
| # ๋ง์ง๋ง ๋ ์ ์ ๊ธฐ์ธ๊ธฐ๋ก ์ธ์ฝ | |
| slope = scores[-1] - scores[-2] | |
| return [float(np.clip(scores[-1] + slope*(i+1), 0, 100)) for i in range(steps)] |