NH-Prediction / app.py
yokoha's picture
Update app.py
4fb476c verified
raw
history blame
6.11 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
# ---------------------------------------------
# CONFIG --------------------------------------
# ---------------------------------------------
DATA_PATH = "price_data.csv" # CSV: date, item, price
MACRO_START, MACRO_END = "1996-01-01", "2030-12-31"
MICRO_START, MICRO_END = "2020-01-01", "2026-12-31"
st.set_page_config(page_title="ํ’ˆ๋ชฉ๋ณ„ ๊ฐ€๊ฒฉ ์˜ˆ์ธก", page_icon="๐Ÿ“ˆ", layout="wide")
# ---------------------------------------------
# UTILITIES -----------------------------------
# ---------------------------------------------
@st.cache_data(show_spinner=False)
def load_data(path: str) -> pd.DataFrame:
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())
@st.cache_data(show_spinner=False)
def fit_prophet(df: pd.DataFrame, horizon_end: str):
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
# ---------------------------------------------
# LOAD ----------------------------------------
# ---------------------------------------------
raw_df = load_data(DATA_PATH)
selected_item = st.sidebar.selectbox("๐Ÿ” ํ’ˆ๋ชฉ ์„ ํƒ", get_items(raw_df))
current_date = date.today()
st.sidebar.write(f"**์˜ค๋Š˜:** {current_date}")
item_df = raw_df[raw_df["item"] == selected_item].copy()
if item_df.empty:
st.error("๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
st.stop()
# ---------------------------------------------
# MACRO FORECAST ------------------------------
# ---------------------------------------------
st.header(f"๐Ÿ“ˆ {selected_item} ๊ฐ€๊ฒฉ ์ „๋ง")
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 1996โ€“2030")
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)
# --- Metrics โ†“
latest_price = macro_df.iloc[-1]["price"]
macro_last = fc_macro[fc_macro["ds"] == MACRO_END]["yhat"].iloc[0]
macro_diff = macro_last - latest_price
macro_pct = macro_diff / latest_price * 100
st.metric(label="2030 ์˜ˆ์ธก ๊ฐ€๊ฒฉ", value=f"{macro_last:,.0f}", delta=f"{macro_pct:+.1f}% vs ์ตœ๊ทผ")
# ---------------------------------------------
# MICRO FORECAST ------------------------------
# ---------------------------------------------
st.subheader("๐Ÿ”Ž ๋ฏธ์‹œ ์˜ˆ์ธก 2024โ€“2026")
micro_df = item_df[item_df["date"] >= MICRO_START]
m_micro, fc_micro = fit_prophet(micro_df, MICRO_END)
fig_micro = px.line(fc_micro, x="ds", y="yhat", title="Micro Forecast 2024โ€“2026")
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)
micro_last = fc_micro[fc_micro["ds"] == MICRO_END]["yhat"].iloc[0]
micro_diff = micro_last - latest_price
micro_pct = micro_diff / latest_price * 100
st.metric(label="2026 ์˜ˆ์ธก ๊ฐ€๊ฒฉ", value=f"{micro_last:,.0f}", delta=f"{micro_pct:+.1f}% vs ์ตœ๊ทผ")
# ---------------------------------------------
# PATTERN & SEASONALITY -----------------------
# ---------------------------------------------
with st.expander("๐Ÿ“† ์‹œ์ฆˆ๋„๋ฆฌํ‹ฐ ๋ถ„์„ ๋ฐ ํŒจํ„ด ํ•ด์„ค"):
comp_fig = m_micro.plot_components(fc_micro)
st.pyplot(comp_fig)
# ์›”๋ณ„ seasonality summary
month_season = (fc_micro[["ds", "yearly"]]
.assign(month=lambda d: d["ds"].dt.month)
.groupby("month")["yearly"].mean())
peak_month = int(month_season.idxmax())
trough_month = int(month_season.idxmin())
st.markdown(f"**ํŒจํ„ด ์š”์•ฝ** \n- __์—ฐ๊ฐ„ ํ”ผํฌ__: {peak_month}์›” \n- __์—ฐ๊ฐ„ ์ €์ __: {trough_month}์›” \n- ํ‰๊ท  ๋ณ€๋™ํญ(์—ฐ): {month_season.max() - month_season.min():.1f} ๋‹จ๊ฐ€")
# ---------------------------------------------
# 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 ์€ ์ •์ฑ…ยท์žฌ๊ณ  ์ „๋žต ์„ค๊ณ„ ์‹œ ์ฃผ์˜ ๊นŠ๊ฒŒ ๋ณผ ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
""")
# ---------------------------------------------
# EXTRA CHARTS -------------------------------
# ---------------------------------------------
st.subheader("๐Ÿ“Š ์ถ”๊ฐ€ ์ธ์‚ฌ์ดํŠธ: 30์ผ ์ด๋™ ๋ณ€๋™์„ฑ")
vol_df = (item_df.set_index("date")["price"].rolling(30).std().reset_index())
fig_vol = px.area(vol_df, x="date", y="price", title="30D Rolling Std Dev")
st.plotly_chart(fig_vol, use_container_width=True)
st.markdown("""
- ๋ณ€๋™์„ฑ ๊ธ‰๋“ฑ ๊ตฌ๊ฐ„์€ **๊ณต๊ธ‰ ์ถฉ๊ฒฉยท์ˆ˜์š” ์ด๋ฒคํŠธ** ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค.
- ์ตœ๊ทผ ๋ณ€๋™์„ฑ์ด ๋‚ฎ์•„์ง€๋ฉด **๊ฐ€๊ฒฉ ์•ˆ์ฐฉ**์œผ๋กœ ํ•ด์„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
""")
# ---------------------------------------------
# FOOTER --------------------------------------
# ---------------------------------------------
st.caption("๋ฐ์ดํ„ฐ ์ถœ์ฒ˜: ๋‚ด๋ถ€ ๋†์ˆ˜์‚ฐ๋ฌผ ๊ฐ€๊ฒฉ DB ยท Forecast by Prophet ยท Dashboard built with Streamlit")