yokoha commited on
Commit
1acd6e1
ยท
verified ยท
1 Parent(s): 8babbb4

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +142 -0
app.py ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ from prophet import Prophet
5
+ import plotly.express as px
6
+ import plotly.graph_objects as go
7
+ import seaborn as sns
8
+ import matplotlib.pyplot as plt
9
+ from datetime import date
10
+
11
+ # --------------------------------------------------
12
+ # 0. CONFIG & UTILS
13
+ # --------------------------------------------------
14
+ DATA_PATH = "price_data.csv" # โ–ถ๏ธŽ CSV: date(YYYY-MM-DD), item, price
15
+
16
+ @st.cache_data(show_spinner=False)
17
+ def load_data(path: str) -> pd.DataFrame:
18
+ """Load & preprocess price data.
19
+ Expects columns: date, item, price."""
20
+ df = pd.read_csv(path, parse_dates=["date"])
21
+ df.sort_values("date", inplace=True)
22
+ return df
23
+
24
+ @st.cache_data(show_spinner=False)
25
+ def get_items(df: pd.DataFrame):
26
+ return sorted(df["item"].unique())
27
+
28
+ # Prophet helper ------------------------------------------------------------
29
+
30
+ def fit_prophet(df: pd.DataFrame, horizon_end: str):
31
+ """Fit Prophet on df(date, price) and forecast till horizon_end (YYYY-MM-DD)."""
32
+ m = Prophet(yearly_seasonality=True, weekly_seasonality=False, daily_seasonality=False)
33
+ m.fit(df.rename(columns={"date": "ds", "price": "y"}))
34
+ future = m.make_future_dataframe(periods=(pd.Timestamp(horizon_end) - df["date"].max()).days, freq="D")
35
+ forecast = m.predict(future)
36
+ return m, forecast
37
+
38
+ # --------------------------------------------------
39
+ # 1. DATA LOAD
40
+ # --------------------------------------------------
41
+ st.title("๐Ÿ“ˆ ํ’ˆ๋ชฉ๋ณ„ ๊ฐ€๊ฒฉ ์˜ˆ์ธก ๋Œ€์‹œ๋ณด๋“œ")
42
+
43
+ raw_df = load_data(DATA_PATH)
44
+
45
+ st.sidebar.header("๐Ÿ” ํ’ˆ๋ชฉ ์„ ํƒ")
46
+ selected_item = st.sidebar.selectbox("ํ’ˆ๋ชฉ", get_items(raw_df))
47
+
48
+ current_date = date.today()
49
+ st.sidebar.markdown(f"**์˜ค๋Š˜ ๋‚ ์งœ:** {current_date}")
50
+
51
+ item_df = raw_df[raw_df["item"] == selected_item].copy()
52
+
53
+ if item_df.empty:
54
+ st.warning("์„ ํƒํ•œ ํ’ˆ๋ชฉ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
55
+ st.stop()
56
+
57
+ # --------------------------------------------------
58
+ # 2. MACRO FORECAST 1996โ€“2030
59
+ # --------------------------------------------------
60
+ st.subheader(f"๐ŸŒ ๊ฑฐ์‹œ ๊ฐ€๊ฒฉ ์ถ”์ด ์˜ˆ์ธก: 1996โ€“2030 ({selected_item})")
61
+
62
+ macro_start = "1996-01-01"
63
+ macro_end = "2030-12-31"
64
+ macro_df = item_df[item_df["date"] >= macro_start]
65
+
66
+ m_macro, fc_macro = fit_prophet(macro_df, macro_end)
67
+
68
+ fig_macro = px.line(fc_macro, x="ds", y="yhat", title="Macro Forecast (daily)")
69
+ fig_macro.add_scatter(x=macro_df["date"], y=macro_df["price"], mode="lines", name="Actual")
70
+ st.plotly_chart(fig_macro, use_container_width=True)
71
+
72
+ # --------------------------------------------------
73
+ # 3. MICRO FORECAST 2024โ€“2026 (์•„๋ž˜ ๋ฐฐ์น˜)
74
+ # --------------------------------------------------
75
+ st.subheader("๐Ÿ”Ž ๋ฏธ์‹œ ๊ฐ€๊ฒฉ ์ถ”์ด ์˜ˆ์ธก: 2024โ€“2026")
76
+
77
+ micro_start = "2020-01-01" # ๋” ์ตœ๊ทผ ๋ฐ์ดํ„ฐ๋งŒ ํ•™์Šต
78
+ micro_horizon_end = "2026-12-31"
79
+ micro_df = item_df[item_df["date"] >= micro_start]
80
+
81
+ m_micro, fc_micro = fit_prophet(micro_df, micro_horizon_end)
82
+
83
+ fig_micro = px.line(fc_micro, x="ds", y="yhat", title="Micro Forecast (daily)")
84
+ fig_micro.add_scatter(x=micro_df["date"], y=micro_df["price"], mode="lines", name="Actual")
85
+ st.plotly_chart(fig_micro, use_container_width=True)
86
+
87
+ # --------------------------------------------------
88
+ # 4. SEASONALITY COMPONENTS
89
+ # --------------------------------------------------
90
+ st.subheader("๐Ÿ“† ์‹œ์ฆˆ๋„๋ฆฌํ‹ฐ ๋ถ„์„")
91
+
92
+ with st.expander("์‹œ์ฆˆ๋„๋ฆฌํ‹ฐ ๊ทธ๋ž˜ํ”„ ์—ด๊ธฐ/๋‹ซ๊ธฐ"):
93
+ comp_fig = m_micro.plot_components(fc_micro)
94
+ st.pyplot(comp_fig)
95
+ st.markdown("""
96
+ **์„ค๋ช…**
97
+ * **Yearly seasonality**: ๊ณ„์ ˆ์  ํŒจํ„ด(์˜ˆ: ์ˆ˜ํ™•๊ธฐยท๋ช…์ ˆ ์ˆ˜์š”)
98
+ * **Trend**: ์žฅ๊ธฐ ์ถ”์„ธ.
99
+ * ์ฃผ๊ฐ„ ์„ฑ๋ถ„์€ ์ƒ๋žตํ–ˆ์Šต๋‹ˆ๋‹ค(๊ฐ€๊ฒฉ ๋ฐ์ดํ„ฐ๊ฐ€ ์ฃผ๊ฐ„ granularity๊ฐ€ ์•„๋‹ˆ๋ฏ€๋กœ).
100
+ """)
101
+
102
+ # --------------------------------------------------
103
+ # 5. CORRELATION HEATMAP (ํ’ˆ๋ชฉ ๊ฐ„)
104
+ # --------------------------------------------------
105
+ st.subheader("๐Ÿงฎ ํ’ˆ๋ชฉ ๊ฐ„ ์ƒ๊ด€๊ด€๊ณ„ ํžˆํŠธ๋งต")
106
+
107
+ # ํ”ผ๋ฒ—: ์›”๊ฐ„ ํ‰๊ท  ๊ฐ€๊ฒฉ์œผ๋กœ ๋‹จ์œ„ ๋งž์ถ”๊ธฐ
108
+ corr_df = (raw_df.assign(month=lambda d: d["date"].dt.to_period("M"))
109
+ .groupby(["month", "item"], as_index=False)["price"].mean()
110
+ .pivot(index="month", columns="item", values="price"))
111
+
112
+ corr = corr_df.corr()
113
+
114
+ fig, ax = plt.subplots(figsize=(12, 10))
115
+ mask = np.triu(np.ones_like(corr, dtype=bool))
116
+ sns.heatmap(corr, mask=mask, cmap="RdBu_r", center=0, linewidths=.5, ax=ax)
117
+ st.pyplot(fig)
118
+
119
+ st.markdown("""
120
+ **ํ•ด์„ ๊ฐ€์ด๋“œ**
121
+ - ๋นจ๊ฐ„์ƒ‰์€ ์–‘์˜ ์ƒ๊ด€ โ†’ ๋‘ ํ’ˆ๋ชฉ ๊ฐ€๊ฒฉ์ด ํ•จ๊ป˜ ์˜ค๋ฅด๋‚ด๋ฆผ.
122
+ - ํŒŒ๋ž€์ƒ‰์€ ์Œ์˜ ์ƒ๊ด€ โ†’ ๋Œ€์ฒด์žฌ/์ˆ˜์š” ์ด๋™ ๊ฐ€๋Šฅ์„ฑ.
123
+ - ์ ˆ๋Œ“๊ฐ’ โ‰ฅ 0.7 ์ธ ๊ด€๊ณ„๋Š” price elasticityยท์ˆ˜๊ธ‰ ์—ฐ๋™์„ฑ ๋ถ„์„์— ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
124
+ """)
125
+
126
+ # --------------------------------------------------
127
+ # 6. EXTRA CHART: ๊ฐ€๊ฒฉ ๋ณ€๋™์„ฑ(rolling std)
128
+ # --------------------------------------------------
129
+ st.subheader("๐Ÿ“Š 30์ผ ์ด๋™ ํ‘œ์ค€ํŽธ์ฐจ โ€“ ๊ฐ€๊ฒฉ ๋ณ€๋™์„ฑ")
130
+
131
+ vol_df = (item_df.set_index("date")["price"]
132
+ .rolling(window=30)
133
+ .std().reset_index(name="rolling_std"))
134
+ fig_vol = px.area(vol_df, x="date", y="rolling_std", title="30D Rolling Std Dev")
135
+ st.plotly_chart(fig_vol, use_container_width=True)
136
+
137
+ st.markdown("""
138
+ - **๋†’์€ ๋ณ€๋™์„ฑ ๊ตฌ๊ฐ„**์€ ์žฌ๊ณ ยท๊ณ„์•ฝ ์ „๋žต ์กฐ์ • ํ•„์š”.
139
+ - ํŠนํžˆ ๋‚ ์”จยท์ˆ˜์š” ์ด๋ฒคํŠธ(๋ช…์ ˆ, ํญ์—ผ ๋“ฑ)์™€ ๊ฒน์น˜๋Š”์ง€ ๊ต์ฐจ ๋ถ„์„ํ•ด ๋ณด์„ธ์š”.
140
+ """)
141
+
142
+ st.success("โœ… ์‹œ๊ฐํ™” ์™„๋ฃŒ! ํ’ˆ๋ชฉ์„ ๋ฐ”๊ฟ”๋ณด๋ฉฐ ์ธ์‚ฌ์ดํŠธ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.")