yokoha commited on
Commit
0e796e5
Β·
verified Β·
1 Parent(s): 7a4c9be

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +84 -17
app.py CHANGED
@@ -148,16 +148,31 @@ def _standardize_columns(df: pd.DataFrame) -> pd.DataFrame:
148
  df.reset_index(inplace=True)
149
  df.rename(columns={df.columns[0]: "date"}, inplace=True)
150
 
151
- # ── convert YYYYMM string to datetime ──────────────────────────────
 
152
  if "date" in df.columns and pd.api.types.is_object_dtype(df["date"]):
153
  if len(df) > 0:
154
- sample = str(df["date"].iloc[0])
155
- if sample.isdigit() and len(sample) == 6: # YYYYMM ν˜•μ‹ 확인
156
- # μ›” 말일둜 λ³€ν™˜ (YYYYMM -> YYYY-MM-DD)
157
- df["date"] = pd.to_datetime(df["date"].astype(str), format="%Y%m", errors="coerce")
158
- df["date"] = df["date"] + pd.offsets.MonthEnd(0) # ν•΄λ‹Ή μ›”μ˜ λ§ˆμ§€λ§‰ λ‚ λ‘œ μ„€μ •
159
- elif sample.isdigit() and len(sample) == 8: # YYYYMMDD ν˜•μ‹
160
- df["date"] = pd.to_datetime(df["date"].astype(str), format="%Y%m%d", errors="coerce")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
 
162
  # ── build item from pdlt_nm + spcs_nm if needed ────────────────────
163
  if "item" not in df.columns and {"pdlt_nm", "spcs_nm"}.issubset(df.columns):
@@ -170,6 +185,8 @@ def _standardize_columns(df: pd.DataFrame) -> pd.DataFrame:
170
 
171
  return df
172
 
 
 
173
  @st.cache_data(show_spinner=False)
174
  def load_data() -> pd.DataFrame:
175
  """Load price data from CSV file."""
@@ -178,46 +195,55 @@ def load_data() -> pd.DataFrame:
178
  st.error(f"πŸ’Ύ {CSV_PATH} νŒŒμΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.")
179
  st.stop()
180
 
181
- st.sidebar.info(f"{CSV_PATH} νŒŒμΌμ—μ„œ 데이터λ₯Ό λΆˆλŸ¬μ˜΅λ‹ˆλ‹€.")
182
-
183
  # CSV 파일 직접 λ‘œλ“œ
184
  df = pd.read_csv(CSV_PATH)
185
  st.sidebar.success(f"CSV 데이터 λ‘œλ“œ μ™„λ£Œ: {len(df)}개 ν–‰")
186
 
187
- # 원본 데이터 ν˜•νƒœ 확인
188
  st.sidebar.write("원본 데이터 컬럼:", list(df.columns))
189
 
 
 
190
  df = _standardize_columns(df)
 
 
 
 
 
191
  st.sidebar.write("ν‘œμ€€ν™” ν›„ 컬럼:", list(df.columns))
192
 
 
193
  missing = {c for c in ["date", "item", "price"] if c not in df.columns}
194
  if missing:
195
  st.error(f"ν•„μˆ˜ 컬럼 λˆ„λ½: {', '.join(missing)} β€” 파일 컬럼λͺ…을 ν™•μΈν•˜μ„Έμš”.")
196
  st.stop()
197
 
198
- # λ‚ μ§œ λ³€ν™˜
199
  before_date_convert = len(df)
200
  df["date"] = pd.to_datetime(df["date"], errors="coerce")
201
  after_date_convert = df.dropna(subset=["date"]).shape[0]
202
  if before_date_convert != after_date_convert:
203
- st.warning(f"λ‚ μ§œ λ³€ν™˜ 쀑 {before_date_convert - after_date_convert}개 행이 μ œμ™Έλ˜μ—ˆμŠ΅λ‹ˆλ‹€.")
204
 
205
- # 가격 데이터 μ •μˆ˜ν˜•μœΌλ‘œ λ³€ν™˜ (μˆ«μžκ°€ μ•„λ‹Œ κ°’ 제거)
206
  df["price"] = pd.to_numeric(df["price"], errors="coerce")
207
 
208
- # NA 데이터 처리
209
  before_na_drop = len(df)
210
  df = df.dropna(subset=["date", "item", "price"])
211
  after_na_drop = len(df)
212
  if before_na_drop != after_na_drop:
213
- st.warning(f"NA 제거 쀑 {before_na_drop - after_na_drop}개 행이 μ œμ™Έλ˜μ—ˆμŠ΅λ‹ˆλ‹€.")
214
 
 
215
  df.sort_values("date", inplace=True)
216
 
217
- # 데이터 λ‚ μ§œ λ²”μœ„ 확인
218
  if len(df) > 0:
 
219
  st.sidebar.write(f"데이터 λ‚ μ§œ λ²”μœ„: {df['date'].min().strftime('%Y-%m-%d')} ~ {df['date'].max().strftime('%Y-%m-%d')}")
220
  st.sidebar.write(f"총 ν’ˆλͺ© 수: {df['item'].nunique()}")
 
221
  else:
222
  st.error("μœ νš¨ν•œ 데이터가 μ—†μŠ΅λ‹ˆλ‹€!")
223
 
@@ -228,6 +254,47 @@ def load_data() -> pd.DataFrame:
228
  st.code(traceback.format_exc())
229
  st.stop()
230
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  @st.cache_data(show_spinner=False)
232
  def get_items(df: pd.DataFrame):
233
  return sorted(df["item"].unique())
 
148
  df.reset_index(inplace=True)
149
  df.rename(columns={df.columns[0]: "date"}, inplace=True)
150
 
151
+
152
+ # ── convert YYYYMM string to datetime ──────────────────────────────────────
153
  if "date" in df.columns and pd.api.types.is_object_dtype(df["date"]):
154
  if len(df) > 0:
155
+ # 더 μœ μ—°ν•œ λ‚ μ§œ λ³€ν™˜
156
+ try:
157
+ # μƒ˜ν”Œ 확인
158
+ sample = str(df["date"].iloc[0])
159
+
160
+ # YYYYMM ν˜•μ‹ (6자리)
161
+ if sample.isdigit() and len(sample) == 6:
162
+ df["date"] = pd.to_datetime(df["date"].astype(str), format="%Y%m", errors="coerce")
163
+ df["date"] = df["date"] + pd.offsets.MonthEnd(0) # ν•΄λ‹Ή μ›”μ˜ λ§ˆμ§€λ§‰ λ‚ λ‘œ μ„€μ •
164
+
165
+ # YYYYMMDD ν˜•μ‹ (8자리)
166
+ elif sample.isdigit() and len(sample) == 8:
167
+ df["date"] = pd.to_datetime(df["date"].astype(str), format="%Y%m%d", errors="coerce")
168
+
169
+ # 기타 ν˜•μ‹μ€ μžλ™ 감지
170
+ else:
171
+ df["date"] = pd.to_datetime(df["date"], errors="coerce")
172
+ except:
173
+ # μ‹€νŒ¨ μ‹œ 일반 λ³€ν™˜ μ‹œλ„
174
+ df["date"] = pd.to_datetime(df["date"], errors="coerce")
175
+
176
 
177
  # ── build item from pdlt_nm + spcs_nm if needed ────────────────────
178
  if "item" not in df.columns and {"pdlt_nm", "spcs_nm"}.issubset(df.columns):
 
185
 
186
  return df
187
 
188
+
189
+
190
  @st.cache_data(show_spinner=False)
191
  def load_data() -> pd.DataFrame:
192
  """Load price data from CSV file."""
 
195
  st.error(f"πŸ’Ύ {CSV_PATH} νŒŒμΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.")
196
  st.stop()
197
 
 
 
198
  # CSV 파일 직접 λ‘œλ“œ
199
  df = pd.read_csv(CSV_PATH)
200
  st.sidebar.success(f"CSV 데이터 λ‘œλ“œ μ™„λ£Œ: {len(df)}개 ν–‰")
201
 
202
+ # 데이터 ν‘œμ€€ν™” μ „ 원본 데이터 ν˜•νƒœ 확인
203
  st.sidebar.write("원본 데이터 컬럼:", list(df.columns))
204
 
205
+ # ν‘œμ€€ν™” μ „ 상세 둜그
206
+ before_std = len(df)
207
  df = _standardize_columns(df)
208
+ after_std = len(df)
209
+ if before_std != after_std:
210
+ st.sidebar.warning(f"ν‘œμ€€ν™” 쀑 {before_std - after_std}개 행이 μ œμ™Έλ˜μ—ˆμŠ΅λ‹ˆλ‹€.")
211
+
212
+ # ν‘œμ€€ν™” ν›„ 둜그
213
  st.sidebar.write("ν‘œμ€€ν™” ν›„ 컬럼:", list(df.columns))
214
 
215
+ # ν•„μˆ˜ 컬럼 확인
216
  missing = {c for c in ["date", "item", "price"] if c not in df.columns}
217
  if missing:
218
  st.error(f"ν•„μˆ˜ 컬럼 λˆ„λ½: {', '.join(missing)} β€” 파일 컬럼λͺ…을 ν™•μΈν•˜μ„Έμš”.")
219
  st.stop()
220
 
221
+ # λ‚ μ§œ λ³€ν™˜ μ „ν›„ 데이터 수 확인
222
  before_date_convert = len(df)
223
  df["date"] = pd.to_datetime(df["date"], errors="coerce")
224
  after_date_convert = df.dropna(subset=["date"]).shape[0]
225
  if before_date_convert != after_date_convert:
226
+ st.sidebar.warning(f"λ‚ μ§œ λ³€ν™˜ 쀑 {before_date_convert - after_date_convert}개 행이 μ œμ™Έλ˜μ—ˆμŠ΅λ‹ˆλ‹€.")
227
 
228
+ # 가격 데이터 숫자둜 λ³€ν™˜
229
  df["price"] = pd.to_numeric(df["price"], errors="coerce")
230
 
231
+ # NA 데이터 처리 μ „ν›„ 수 확인
232
  before_na_drop = len(df)
233
  df = df.dropna(subset=["date", "item", "price"])
234
  after_na_drop = len(df)
235
  if before_na_drop != after_na_drop:
236
+ st.sidebar.warning(f"NA 제거 쀑 {before_na_drop - after_na_drop}개 행이 μ œμ™Έλ˜μ—ˆμŠ΅λ‹ˆοΏ½οΏ½οΏ½.")
237
 
238
+ # κ²°κ³Ό μ •λ ¬
239
  df.sort_values("date", inplace=True)
240
 
241
+ # 데이터 정보 ν‘œμ‹œ
242
  if len(df) > 0:
243
+ st.sidebar.write(f"μ΅œμ’… 데이터: {len(df)}개 ν–‰")
244
  st.sidebar.write(f"데이터 λ‚ μ§œ λ²”μœ„: {df['date'].min().strftime('%Y-%m-%d')} ~ {df['date'].max().strftime('%Y-%m-%d')}")
245
  st.sidebar.write(f"총 ν’ˆλͺ© 수: {df['item'].nunique()}")
246
+ st.sidebar.write(f"ν’ˆλͺ©λ³„ 평균 데이터 수: {len(df)/df['item'].nunique():.1f}개")
247
  else:
248
  st.error("μœ νš¨ν•œ 데이터가 μ—†μŠ΅λ‹ˆλ‹€!")
249
 
 
254
  st.code(traceback.format_exc())
255
  st.stop()
256
 
257
+ # 메인 μ½”λ“œμ— λ‹€μŒ λΆ€λΆ„ μΆ”κ°€ - ν’ˆλͺ©λ³„ 데이터 수 확인
258
+ item_df = raw_df.query("item == @selected_item").copy()
259
+ if item_df.empty:
260
+ st.error(f"μ„ νƒν•œ ν’ˆλͺ© '{selected_item}' 데이터가 μ—†μŠ΅λ‹ˆλ‹€.")
261
+ st.stop()
262
+ elif len(item_df) < 2:
263
+ st.warning(f"μ„ νƒν•œ ν’ˆλͺ© '{selected_item}' 데이터가 λ„ˆλ¬΄ μ μŠ΅λ‹ˆλ‹€ (데이터 수: {len(item_df)}). 예츑이 λΆ€μ •ν™•ν•  수 μžˆμŠ΅λ‹ˆλ‹€.")
264
+ else:
265
+ st.success(f"μ„ νƒν•œ ν’ˆλͺ© '{selected_item}'에 λŒ€ν•΄ {len(item_df)}개의 데이터가 μžˆμŠ΅λ‹ˆλ‹€.")
266
+
267
+ # μ•„λž˜μ²˜λŸΌ μž₯κΈ° 예츑 λΆ€λΆ„ μˆ˜μ •
268
+ try:
269
+ # 데이터 필터링 둜직 κ°œμ„ 
270
+ macro_start_dt = pd.Timestamp("1996-01-01")
271
+
272
+ # μ΅œμ†Œ 데이터 수 확인
273
+ macro_df = item_df.copy() # 전체 데이터 μ‚¬μš©
274
+
275
+ # 데이터가 맀우 적은 경우 κ²½κ³  ν‘œμ‹œ
276
+ if len(macro_df) < 5:
277
+ st.warning(f"{selected_item}에 λŒ€ν•œ 데이터가 맀우 μ μŠ΅λ‹ˆλ‹€ (데이터 수: {len(macro_df)}). 예츑이 λΆ€μ •ν™•ν•  수 μžˆμŠ΅λ‹ˆλ‹€.")
278
+
279
+ # 진단 정보 ν‘œμ‹œ
280
+ with st.expander("데이터 진단"):
281
+ st.write(f"- 전체 데이터 수: {len(item_df)}")
282
+ st.write(f"- 뢄석 데이터 수: {len(macro_df)}")
283
+ if len(macro_df) > 0:
284
+ st.write(f"- κΈ°κ°„: {macro_df['date'].min().strftime('%Y-%m-%d')} ~ {macro_df['date'].max().strftime('%Y-%m-%d')}")
285
+ st.dataframe(macro_df.head())
286
+ else:
287
+ st.write("데이터가 μ—†μŠ΅λ‹ˆλ‹€.")
288
+
289
+ # 데이터 필터링 쑰건 μ™„ν™” - μ΅œμ†Œ 2개 이상이면 μ§„ν–‰
290
+ if len(macro_df) >= 2:
291
+ # κΈ°μ‘΄ μ½”λ“œ (λͺ¨λΈ ν•™μŠ΅ 및 μ‹œκ°ν™”)
292
+ with st.spinner("μž₯κΈ° 예츑 λͺ¨λΈ 생성 쀑..."):
293
+ if use_ensemble:
294
+ fc_macro = fit_ensemble_model(macro_df, selected_item, MACRO_END)
295
+ else:
296
+ fc_macro = fit_optimal_model(macro_df, selected_item, MACRO_END)
297
+
298
  @st.cache_data(show_spinner=False)
299
  def get_items(df: pd.DataFrame):
300
  return sorted(df["item"].unique())