NH-Prediction / app.py
yokoha's picture
Create app.py
1acd6e1 verified
raw
history blame
5.49 kB
import streamlit as st
import pandas as pd
import numpy as np
from prophet import Prophet
import plotly.express as px
import plotly.graph_objects as go
import seaborn as sns
import matplotlib.pyplot as plt
from datetime import date
# --------------------------------------------------
# 0. CONFIG & UTILS
# --------------------------------------------------
DATA_PATH = "price_data.csv" # โ–ถ๏ธŽ CSV: date(YYYY-MM-DD), item, price
@st.cache_data(show_spinner=False)
def load_data(path: str) -> pd.DataFrame:
"""Load & preprocess price data.
Expects columns: date, item, price."""
df = pd.read_csv(path, parse_dates=["date"])
df.sort_values("date", inplace=True)
return df
@st.cache_data(show_spinner=False)
def get_items(df: pd.DataFrame):
return sorted(df["item"].unique())
# Prophet helper ------------------------------------------------------------
def fit_prophet(df: pd.DataFrame, horizon_end: str):
"""Fit Prophet on df(date, price) and forecast till horizon_end (YYYY-MM-DD)."""
m = Prophet(yearly_seasonality=True, weekly_seasonality=False, daily_seasonality=False)
m.fit(df.rename(columns={"date": "ds", "price": "y"}))
future = m.make_future_dataframe(periods=(pd.Timestamp(horizon_end) - df["date"].max()).days, freq="D")
forecast = m.predict(future)
return m, forecast
# --------------------------------------------------
# 1. DATA LOAD
# --------------------------------------------------
st.title("๐Ÿ“ˆ ํ’ˆ๋ชฉ๋ณ„ ๊ฐ€๊ฒฉ ์˜ˆ์ธก ๋Œ€์‹œ๋ณด๋“œ")
raw_df = load_data(DATA_PATH)
st.sidebar.header("๐Ÿ” ํ’ˆ๋ชฉ ์„ ํƒ")
selected_item = st.sidebar.selectbox("ํ’ˆ๋ชฉ", get_items(raw_df))
current_date = date.today()
st.sidebar.markdown(f"**์˜ค๋Š˜ ๋‚ ์งœ:** {current_date}")
item_df = raw_df[raw_df["item"] == selected_item].copy()
if item_df.empty:
st.warning("์„ ํƒํ•œ ํ’ˆ๋ชฉ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
st.stop()
# --------------------------------------------------
# 2. MACRO FORECAST 1996โ€“2030
# --------------------------------------------------
st.subheader(f"๐ŸŒ ๊ฑฐ์‹œ ๊ฐ€๊ฒฉ ์ถ”์ด ์˜ˆ์ธก: 1996โ€“2030 ({selected_item})")
macro_start = "1996-01-01"
macro_end = "2030-12-31"
macro_df = item_df[item_df["date"] >= macro_start]
m_macro, fc_macro = fit_prophet(macro_df, macro_end)
fig_macro = px.line(fc_macro, x="ds", y="yhat", title="Macro Forecast (daily)")
fig_macro.add_scatter(x=macro_df["date"], y=macro_df["price"], mode="lines", name="Actual")
st.plotly_chart(fig_macro, use_container_width=True)
# --------------------------------------------------
# 3. MICRO FORECAST 2024โ€“2026 (์•„๋ž˜ ๋ฐฐ์น˜)
# --------------------------------------------------
st.subheader("๐Ÿ”Ž ๋ฏธ์‹œ ๊ฐ€๊ฒฉ ์ถ”์ด ์˜ˆ์ธก: 2024โ€“2026")
micro_start = "2020-01-01" # ๋” ์ตœ๊ทผ ๋ฐ์ดํ„ฐ๋งŒ ํ•™์Šต
micro_horizon_end = "2026-12-31"
micro_df = item_df[item_df["date"] >= micro_start]
m_micro, fc_micro = fit_prophet(micro_df, micro_horizon_end)
fig_micro = px.line(fc_micro, x="ds", y="yhat", title="Micro Forecast (daily)")
fig_micro.add_scatter(x=micro_df["date"], y=micro_df["price"], mode="lines", name="Actual")
st.plotly_chart(fig_micro, use_container_width=True)
# --------------------------------------------------
# 4. SEASONALITY COMPONENTS
# --------------------------------------------------
st.subheader("๐Ÿ“† ์‹œ์ฆˆ๋„๋ฆฌํ‹ฐ ๋ถ„์„")
with st.expander("์‹œ์ฆˆ๋„๋ฆฌํ‹ฐ ๊ทธ๋ž˜ํ”„ ์—ด๊ธฐ/๋‹ซ๊ธฐ"):
comp_fig = m_micro.plot_components(fc_micro)
st.pyplot(comp_fig)
st.markdown("""
**์„ค๋ช…**
* **Yearly seasonality**: ๊ณ„์ ˆ์  ํŒจํ„ด(์˜ˆ: ์ˆ˜ํ™•๊ธฐยท๋ช…์ ˆ ์ˆ˜์š”)
* **Trend**: ์žฅ๊ธฐ ์ถ”์„ธ.
* ์ฃผ๊ฐ„ ์„ฑ๋ถ„์€ ์ƒ๋žตํ–ˆ์Šต๋‹ˆ๋‹ค(๊ฐ€๊ฒฉ ๋ฐ์ดํ„ฐ๊ฐ€ ์ฃผ๊ฐ„ granularity๊ฐ€ ์•„๋‹ˆ๋ฏ€๋กœ).
""")
# --------------------------------------------------
# 5. CORRELATION HEATMAP (ํ’ˆ๋ชฉ ๊ฐ„)
# --------------------------------------------------
st.subheader("๐Ÿงฎ ํ’ˆ๋ชฉ ๊ฐ„ ์ƒ๊ด€๊ด€๊ณ„ ํžˆํŠธ๋งต")
# ํ”ผ๋ฒ—: ์›”๊ฐ„ ํ‰๊ท  ๊ฐ€๊ฒฉ์œผ๋กœ ๋‹จ์œ„ ๋งž์ถ”๊ธฐ
corr_df = (raw_df.assign(month=lambda d: d["date"].dt.to_period("M"))
.groupby(["month", "item"], as_index=False)["price"].mean()
.pivot(index="month", columns="item", values="price"))
corr = corr_df.corr()
fig, ax = plt.subplots(figsize=(12, 10))
mask = np.triu(np.ones_like(corr, dtype=bool))
sns.heatmap(corr, mask=mask, cmap="RdBu_r", center=0, linewidths=.5, ax=ax)
st.pyplot(fig)
st.markdown("""
**ํ•ด์„ ๊ฐ€์ด๋“œ**
- ๋นจ๊ฐ„์ƒ‰์€ ์–‘์˜ ์ƒ๊ด€ โ†’ ๋‘ ํ’ˆ๋ชฉ ๊ฐ€๊ฒฉ์ด ํ•จ๊ป˜ ์˜ค๋ฅด๋‚ด๋ฆผ.
- ํŒŒ๋ž€์ƒ‰์€ ์Œ์˜ ์ƒ๊ด€ โ†’ ๋Œ€์ฒด์žฌ/์ˆ˜์š” ์ด๋™ ๊ฐ€๋Šฅ์„ฑ.
- ์ ˆ๋Œ“๊ฐ’ โ‰ฅ 0.7 ์ธ ๊ด€๊ณ„๋Š” price elasticityยท์ˆ˜๊ธ‰ ์—ฐ๋™์„ฑ ๋ถ„์„์— ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
""")
# --------------------------------------------------
# 6. EXTRA CHART: ๊ฐ€๊ฒฉ ๋ณ€๋™์„ฑ(rolling std)
# --------------------------------------------------
st.subheader("๐Ÿ“Š 30์ผ ์ด๋™ ํ‘œ์ค€ํŽธ์ฐจ โ€“ ๊ฐ€๊ฒฉ ๋ณ€๋™์„ฑ")
vol_df = (item_df.set_index("date")["price"]
.rolling(window=30)
.std().reset_index(name="rolling_std"))
fig_vol = px.area(vol_df, x="date", y="rolling_std", title="30D Rolling Std Dev")
st.plotly_chart(fig_vol, use_container_width=True)
st.markdown("""
- **๋†’์€ ๋ณ€๋™์„ฑ ๊ตฌ๊ฐ„**์€ ์žฌ๊ณ ยท๊ณ„์•ฝ ์ „๋žต ์กฐ์ • ํ•„์š”.
- ํŠนํžˆ ๋‚ ์”จยท์ˆ˜์š” ์ด๋ฒคํŠธ(๋ช…์ ˆ, ํญ์—ผ ๋“ฑ)์™€ ๊ฒน์น˜๋Š”์ง€ ๊ต์ฐจ ๋ถ„์„ํ•ด ๋ณด์„ธ์š”.
""")
st.success("โœ… ์‹œ๊ฐํ™” ์™„๋ฃŒ! ํ’ˆ๋ชฉ์„ ๋ฐ”๊ฟ”๋ณด๋ฉฐ ์ธ์‚ฌ์ดํŠธ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.")