Kiuyha commited on
Commit
8ad75d3
Β·
verified Β·
1 Parent(s): 7ccd29f

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +147 -98
app.py CHANGED
@@ -4,13 +4,12 @@ import numpy as np
4
  import joblib
5
  import plotly.express as px
6
  import re
7
- import string
8
- import nltk
9
  import os
10
  from datetime import datetime
11
  from nltk.corpus import stopwords
 
12
 
13
- st.set_page_config(page_title="Tokopedia Project Dashboard", layout="wide")
14
 
15
  ARTIFACTS_DIR = 'artifacts'
16
 
@@ -26,14 +25,10 @@ def load_resources():
26
  }
27
  final_stopwords = indo_stopwords.union(custom_stopwords)
28
 
29
- # Load core artifacts
30
  vectorizer = joblib.load(os.path.join(ARTIFACTS_DIR, 'tfidf_vectorizer.pkl'))
31
-
32
- # Load Price Artifacts
33
  price_preprocess = joblib.load(os.path.join(ARTIFACTS_DIR, 'price_preprocessor.pkl'))
34
  price_cols = joblib.load(os.path.join(ARTIFACTS_DIR, 'price_columns.pkl'))
35
 
36
- # Load Models
37
  price_models = {
38
  "Random Forest": joblib.load(os.path.join(ARTIFACTS_DIR, 'Random_Forest_price.pkl')),
39
  "XGBoost": joblib.load(os.path.join(ARTIFACTS_DIR, 'XGBoost_price.pkl')),
@@ -45,8 +40,7 @@ def load_resources():
45
  st.error(f"Error loading resources: {e}")
46
  return None, None, None, None, None
47
 
48
- (vectorizer, price_preprocess, price_cols,
49
- price_models, final_stopwords) = load_resources()
50
 
51
  @st.cache_data
52
  def get_categories():
@@ -54,7 +48,6 @@ def get_categories():
54
  if price_cols:
55
  cat_cols = [c for c in price_cols if c.startswith('cat_')]
56
  columns = [c.replace('cat_', '') for c in cat_cols]
57
-
58
  return sorted(columns)
59
 
60
  def clean_text(text):
@@ -75,22 +68,19 @@ def get_date_features(date_obj, prefix):
75
  def prepare_raw_dataframe(user_input, target_columns, vectorizer):
76
  input_df = pd.DataFrame(0, index=[0], columns=target_columns)
77
 
78
- # Text
79
  combined_text = clean_text(user_input.get('product_name', '')) + " " + clean_text(user_input.get('description', ''))
80
  tfidf_matrix = vectorizer.transform([combined_text])
81
  tfidf_df = pd.DataFrame(tfidf_matrix.toarray(), columns=[f"word_{w}" for w in vectorizer.get_feature_names_out()])
82
-
83
  common_text_cols = input_df.columns.intersection(tfidf_df.columns)
84
  input_df[common_text_cols] = tfidf_df[common_text_cols]
85
 
86
- # Categories
87
  cat_col = f"cat_{user_input.get('category', 'Other')}"
88
  if cat_col in input_df.columns:
89
  input_df[cat_col] = 1
90
  elif 'cat_Other' in input_df.columns:
91
  input_df['cat_Other'] = 1
92
 
93
- # Numerical
94
  for key, value in user_input.items():
95
  if key in input_df.columns:
96
  input_df[key] = value
@@ -104,134 +94,193 @@ def load_and_plot_importance(model_name, target_name):
104
  df = pd.read_csv(path)
105
  fig = px.bar(
106
  df.head(15), x='Importance', y='Feature', orientation='h', error_x='Std',
107
- title=f"Feature Importance ({model_name}) - {target_name.title()}", color='Importance'
108
  )
109
  fig.update_layout(yaxis=dict(autorange="reversed"))
110
  st.plotly_chart(fig)
111
 
112
- st.title("πŸ›οΈ Advanced E-Commerce Predictor")
113
-
114
- with st.expander("Product Metadata"):
115
- st.header("Product Info")
116
- p_name = st.text_input("Product Name", "Samsung Galaxy S24 Ultra")
117
- p_desc = st.text_area("Description", "Original SEIN, Garansi Resmi 1 Tahun")
118
-
119
- cat_options = get_categories()
120
- category = st.selectbox("Category", cat_options)
121
-
122
- condition = st.radio("Condition", ["New", "Used"])
123
- is_regular = st.checkbox("Regular Merchant?", value=True)
124
- is_discount = st.checkbox("Is Discounted?", value=False)
125
-
126
- st.header("Physical & Stock")
127
- col1, col2 = st.columns(2)
128
- with col1:
129
- weight = st.number_input("Weight (g)", value=500)
130
- min_order = st.number_input("Min Order", value=1)
131
- with col2:
132
- max_order = st.number_input("Max Order", value=100)
133
- stock = st.number_input("Stock", value=50)
134
- sold_input = st.number_input("Sold (Current)", value=0)
135
-
136
- st.header("Media")
137
- vid_count = st.number_input("Video Count", 0)
138
- img_count = st.number_input("Image Count", 1)
139
-
140
- with st.expander("Detailed Ratings", expanded=False):
141
- c1, c2, c3, c4, c5 = st.columns(5)
142
- r5 = c1.number_input("5 Star Count", value=10)
143
- r4 = c2.number_input("4 Star Count", value=2)
144
- r3 = c3.number_input("3 Star Count", value=0)
145
- r2 = c4.number_input("2 Star Count", value=0)
146
- r1 = c5.number_input("1 Star Count", value=0)
147
-
148
- total_rating = r5 + r4 + r3 + r2 + r1
149
-
150
- c_qual, c_srv, c_ship, = st.columns(3)
151
- rev_qual = c_qual.number_input("Reviews: Quality", 0)
152
- rev_srv = c_srv.number_input("Reviews: Service", 0)
153
- rev_ship = c_ship.number_input("Reviews: Shipping", 0)
154
-
155
- c_pack, c_price, c_desc = st.columns(3)
156
- rev_pack = c_pack.number_input("Reviews: Packaging", 0)
157
- rev_price = c_price.number_input("Reviews: Price", 0)
158
- rev_desc = c_desc.number_input("Reviews: Description", 0)
159
-
160
- with st.expander("Shipping & Shop Metadata", expanded=False):
161
- c1, c2 = st.columns(2)
162
  with c1:
163
- ship_sameday = st.checkbox("Same Day", True)
164
- ship_reg = st.checkbox("Regular", True)
165
- ship_cargo = st.checkbox("Cargo", False)
166
- ship_eco = st.checkbox("Economy", True)
167
  with c2:
168
- shop_rating = st.slider("Shop Rating Score", 0.0, 5.0, 4.8)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  shop_age = st.number_input("Shop Age (Days)", 365)
170
- shop_pop = st.number_input("Shop City Popularity", 0.0)
 
 
 
171
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  date_listing = st.date_input("Listing Created", datetime.now())
173
- date_shop_open = st.date_input("Shop Open Since", datetime(2020, 1, 1))
174
 
175
  feat_created = get_date_features(date_listing, "created_at")
176
  feat_shop = get_date_features(date_shop_open, "shop_open_since")
177
  listing_age_calc = (datetime.now().date() - date_listing).days
178
- total_shipping = sum([ship_sameday, ship_reg, ship_cargo, ship_eco])
 
 
179
 
180
  base_input = {
181
  'product_name': p_name, 'description': p_desc, 'category': category,
182
  'condition_encoded': 1 if condition == 'New' else 0,
183
- 'is_regular_merchant': 1 if is_regular else 0,
 
 
 
184
  'is_discount': 1 if is_discount else 0,
185
- 'weight_grams': weight,
186
- 'min_order': min_order, 'max_order': max_order,
187
- 'stock': stock,
 
188
  'video_count': vid_count, 'image_count': img_count,
189
- 'can_shipping_sameday': 1 if ship_sameday else 0,
 
 
 
190
  'can_shipping_regular': 1 if ship_reg else 0,
191
  'can_shipping_cargo': 1 if ship_cargo else 0,
192
  'can_shipping_economy': 1 if ship_eco else 0,
193
  'total_shipping_types': total_shipping,
194
- 'shop_rating_score': shop_rating,
195
- 'shop_age_days': shop_age,
196
- 'shop_city_popularity': shop_pop,
197
- 'listing_age_days': listing_age_calc,
 
 
198
  'product_rating_5_star_count': r5,
199
  'product_rating_4_star_count': r4,
200
  'product_rating_3_star_count': r3,
201
  'product_rating_2_star_count': r2,
202
  'product_rating_1_star_count': r1,
203
- 'total_product_rating': total_rating,
204
  'total_review_about_kualitas': rev_qual,
205
  'total_review_about_pelayanan': rev_srv,
206
- 'total_review_about_pengiriman': rev_ship,
207
- 'total_review_about_kemasan' : rev_pack,
208
- 'total_review_about_harga': rev_price,
209
  'total_review_about_sesuai deskripsi': rev_desc,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  **feat_created, **feat_shop
211
  }
212
 
213
- if sold_input:
214
- base_input['sold'] = sold_input
215
-
216
- # Log Transforms (Stock & Sold)
217
  base_input['Log_stock'] = np.log1p(stock)
218
- base_input['Log_sold'] = np.log1p(sold_input)
219
 
220
  st.divider()
221
-
222
  st.subheader("πŸ’° Price Prediction")
 
223
  if price_models:
224
- model_name = st.selectbox("Select Price Model", list(price_models.keys()), key="p_model")
 
 
 
 
225
 
226
- if st.button("Predict Price", type="primary"):
227
  raw_df = prepare_raw_dataframe(base_input, price_cols, vectorizer)
228
- X_scaled = price_preprocess.transform(raw_df)
 
 
 
 
229
 
 
230
  log_pred = price_models[model_name].predict(X_scaled)[0]
231
  price_pred = np.expm1(log_pred)
232
 
233
  st.success(f"Estimated Price: **Rp {price_pred:,.0f}**")
234
- st.markdown("### Feature Importance")
235
- load_and_plot_importance(model_name, "price")
 
236
  else:
237
- st.error("Price models not loaded.")
 
4
  import joblib
5
  import plotly.express as px
6
  import re
 
 
7
  import os
8
  from datetime import datetime
9
  from nltk.corpus import stopwords
10
+ import nltk
11
 
12
+ st.set_page_config(page_title="Tokopedia Price Predictor", layout="wide")
13
 
14
  ARTIFACTS_DIR = 'artifacts'
15
 
 
25
  }
26
  final_stopwords = indo_stopwords.union(custom_stopwords)
27
 
 
28
  vectorizer = joblib.load(os.path.join(ARTIFACTS_DIR, 'tfidf_vectorizer.pkl'))
 
 
29
  price_preprocess = joblib.load(os.path.join(ARTIFACTS_DIR, 'price_preprocessor.pkl'))
30
  price_cols = joblib.load(os.path.join(ARTIFACTS_DIR, 'price_columns.pkl'))
31
 
 
32
  price_models = {
33
  "Random Forest": joblib.load(os.path.join(ARTIFACTS_DIR, 'Random_Forest_price.pkl')),
34
  "XGBoost": joblib.load(os.path.join(ARTIFACTS_DIR, 'XGBoost_price.pkl')),
 
40
  st.error(f"Error loading resources: {e}")
41
  return None, None, None, None, None
42
 
43
+ (vectorizer, price_preprocess, price_cols, price_models, final_stopwords) = load_resources()
 
44
 
45
  @st.cache_data
46
  def get_categories():
 
48
  if price_cols:
49
  cat_cols = [c for c in price_cols if c.startswith('cat_')]
50
  columns = [c.replace('cat_', '') for c in cat_cols]
 
51
  return sorted(columns)
52
 
53
  def clean_text(text):
 
68
  def prepare_raw_dataframe(user_input, target_columns, vectorizer):
69
  input_df = pd.DataFrame(0, index=[0], columns=target_columns)
70
 
 
71
  combined_text = clean_text(user_input.get('product_name', '')) + " " + clean_text(user_input.get('description', ''))
72
  tfidf_matrix = vectorizer.transform([combined_text])
73
  tfidf_df = pd.DataFrame(tfidf_matrix.toarray(), columns=[f"word_{w}" for w in vectorizer.get_feature_names_out()])
74
+
75
  common_text_cols = input_df.columns.intersection(tfidf_df.columns)
76
  input_df[common_text_cols] = tfidf_df[common_text_cols]
77
 
 
78
  cat_col = f"cat_{user_input.get('category', 'Other')}"
79
  if cat_col in input_df.columns:
80
  input_df[cat_col] = 1
81
  elif 'cat_Other' in input_df.columns:
82
  input_df['cat_Other'] = 1
83
 
 
84
  for key, value in user_input.items():
85
  if key in input_df.columns:
86
  input_df[key] = value
 
94
  df = pd.read_csv(path)
95
  fig = px.bar(
96
  df.head(15), x='Importance', y='Feature', orientation='h', error_x='Std',
97
+ title=f"Feature Importance ({model_name})", color='Importance'
98
  )
99
  fig.update_layout(yaxis=dict(autorange="reversed"))
100
  st.plotly_chart(fig)
101
 
102
+ st.title("πŸ›οΈ Advanced E-Commerce Price Predictor")
103
+
104
+ with st.container():
105
+ c1, c2 = st.columns([2, 1])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  with c1:
107
+ p_name = st.text_input("Product Name", "Samsung Galaxy S24 Ultra")
108
+ p_desc = st.text_area("Description", "Original SEIN, Garansi Resmi 1 Tahun")
 
 
109
  with c2:
110
+ category = st.selectbox("Category", get_categories())
111
+ shop_tier = st.selectbox("Shop Tier", ["Regular Merchant", "Power Merchant", "Official Store"])
112
+ condition = st.radio("Condition", ["New", "Used"], horizontal=True)
113
+
114
+ c_left, c_right = st.columns(2)
115
+
116
+ with c_left:
117
+ with st.expander("πŸ“¦ Physical, Stock & Shipping", expanded=True):
118
+ col1, col2 = st.columns(2)
119
+ weight = col1.number_input("Weight (g)", value=500)
120
+ stock = col2.number_input("Stock", value=50)
121
+ min_order = col1.number_input("Min Order", value=1)
122
+ max_order = col2.number_input("Max Order", value=100)
123
+
124
+ st.caption("Shipping Options")
125
+ sc1, sc2, sc3 = st.columns(3)
126
+ ship_inst = sc1.checkbox("Instant", False)
127
+ ship_same = sc2.checkbox("Same Day", True)
128
+ ship_next = sc3.checkbox("Next Day", False)
129
+ ship_reg = sc1.checkbox("Regular", True)
130
+ ship_cargo = sc2.checkbox("Cargo", False)
131
+ ship_eco = sc3.checkbox("Economy", True)
132
+
133
+ is_preorder = st.checkbox("Preorder?", False)
134
+ is_discount = st.checkbox("Discounted?", False)
135
+
136
+ with st.expander("πŸͺ Shop Performance", expanded=False):
137
  shop_age = st.number_input("Shop Age (Days)", 365)
138
+ shop_pop = st.number_input("Shop Popularity Score", 100)
139
+ shop_city_pop = st.number_input("City Popularity", 1)
140
+ resp_time = st.number_input("Response Time (mins)", 30)
141
+ date_shop_open = st.date_input("Shop Open Since", datetime(2020, 1, 1))
142
 
143
+ with st.expander("βš™οΈ Advanced Shop Setup (Detailed Ratings)", expanded=False):
144
+ st.caption("Input exact review counts for the entire shop")
145
+ ac1, ac2, ac3, ac4, ac5 = st.columns(5)
146
+ shop_r5 = ac1.number_input("Shop 5β˜…", value=100)
147
+ shop_r4 = ac2.number_input("Shop 4β˜…", value=10)
148
+ shop_r3 = ac3.number_input("Shop 3β˜…", value=0)
149
+ shop_r2 = ac4.number_input("Shop 2β˜…", value=0)
150
+ shop_r1 = ac5.number_input("Shop 1β˜…", value=0)
151
+
152
+ shop_total_rating = shop_r5 + shop_r4 + shop_r3 + shop_r2 + shop_r1
153
+
154
+ weighted_shop_sum = (shop_r5*5 + shop_r4*4 + shop_r3*3 + shop_r2*2 + shop_r1*1)
155
+ shop_rating_score = weighted_shop_sum / shop_total_rating if shop_total_rating > 0 else 0.0
156
+
157
+ st.text(f"Calculated Total Ratings: {shop_total_rating}")
158
+ st.text(f"Calculated Avg Score: {shop_rating_score:.2f}")
159
+
160
+ with c_right:
161
+ with st.expander("⭐ Product Ratings & Reviews", expanded=True):
162
+ c1, c2, c3, c4, c5 = st.columns(5)
163
+ r5 = c1.number_input("Prod 5β˜…", value=10)
164
+ r4 = c2.number_input("Prod 4β˜…", value=2)
165
+ r3 = c3.number_input("Prod 3β˜…", value=0)
166
+ r2 = c4.number_input("Prod 2β˜…", value=0)
167
+ r1 = c5.number_input("Prod 1β˜…", value=0)
168
+
169
+ total_rating_count = r5 + r4 + r3 + r2 + r1
170
+ weighted_sum = (r5*5 + r4*4 + r3*3 + r2*2 + r1*1)
171
+ avg_rating = weighted_sum / total_rating_count if total_rating_count > 0 else 0.0
172
+ st.info(f"Avg Rating: {avg_rating:.2f} ({total_rating_count} ratings)")
173
+
174
+ total_reviews = st.number_input("Total Written Reviews", value=total_rating_count)
175
+ reviews_w_img = st.number_input("Reviews w/ Images", value=0)
176
+ satisfaction = st.slider("Buyer Satisfaction %", 0, 100, 95)
177
+
178
+ with st.expander("πŸ’¬ Review Topics", expanded=False):
179
+ tc1, tc2 = st.columns(2)
180
+ rev_qual = tc1.number_input("Quality", 0)
181
+ rev_srv = tc2.number_input("Service", 0)
182
+ rev_pack = tc1.number_input("Packaging", 0)
183
+ rev_price = tc2.number_input("Price", 0)
184
+ rev_desc = tc1.number_input("Description", 0)
185
+ rev_ship = tc2.number_input("Shipping", 0)
186
+
187
+ with st.expander("πŸ“Έ Media & Listing Date", expanded=False):
188
+ mc1, mc2 = st.columns(2)
189
+ vid_count = mc1.number_input("Videos", 0)
190
+ img_count = mc2.number_input("Images", 1)
191
  date_listing = st.date_input("Listing Created", datetime.now())
 
192
 
193
  feat_created = get_date_features(date_listing, "created_at")
194
  feat_shop = get_date_features(date_shop_open, "shop_open_since")
195
  listing_age_calc = (datetime.now().date() - date_listing).days
196
+
197
+ shipping_flags = [ship_inst, ship_same, ship_reg, ship_cargo, ship_eco, ship_next]
198
+ total_shipping = sum(shipping_flags)
199
 
200
  base_input = {
201
  'product_name': p_name, 'description': p_desc, 'category': category,
202
  'condition_encoded': 1 if condition == 'New' else 0,
203
+ 'weight_grams': weight, 'min_order': min_order, 'max_order': max_order,
204
+ 'stock': stock, 'sold': 0,
205
+
206
+ 'is_preorder': 1 if is_preorder else 0,
207
  'is_discount': 1 if is_discount else 0,
208
+ 'is_regular_merchant': 1 if shop_tier == "Regular Merchant" else 0,
209
+ 'is_power_merchant': 1 if shop_tier == "Power Merchant" else 0,
210
+ 'is_official_store': 1 if shop_tier == "Official Store" else 0,
211
+
212
  'video_count': vid_count, 'image_count': img_count,
213
+
214
+ 'can_shipping_instant': 1 if ship_inst else 0,
215
+ 'can_shipping_sameday': 1 if ship_same else 0,
216
+ 'can_shipping_next_day': 1 if ship_next else 0,
217
  'can_shipping_regular': 1 if ship_reg else 0,
218
  'can_shipping_cargo': 1 if ship_cargo else 0,
219
  'can_shipping_economy': 1 if ship_eco else 0,
220
  'total_shipping_types': total_shipping,
221
+
222
+ 'rating': avg_rating,
223
+ 'total_product_rating': total_rating_count,
224
+ 'total_product_review': total_reviews,
225
+ 'total_product_review_with_image': reviews_w_img,
226
+ 'buyer_satisfaction_percentage': satisfaction,
227
  'product_rating_5_star_count': r5,
228
  'product_rating_4_star_count': r4,
229
  'product_rating_3_star_count': r3,
230
  'product_rating_2_star_count': r2,
231
  'product_rating_1_star_count': r1,
232
+
233
  'total_review_about_kualitas': rev_qual,
234
  'total_review_about_pelayanan': rev_srv,
235
+ 'total_review_about_kemasan': rev_pack,
236
+ 'total_review_about_harga': rev_price,
 
237
  'total_review_about_sesuai deskripsi': rev_desc,
238
+ 'total_review_about_pengiriman': rev_ship,
239
+
240
+ 'shop_rating_score': shop_rating_score,
241
+ 'shop_total_rating': shop_total_rating,
242
+ 'shop_rating_5': shop_r5,
243
+ 'shop_rating_4': shop_r4,
244
+ 'shop_rating_3': shop_r3,
245
+ 'shop_rating_2': shop_r2,
246
+ 'shop_rating_1': shop_r1,
247
+ 'shop_response_time_mins': resp_time,
248
+ 'shop_age_days': shop_age,
249
+ 'listing_age_days': listing_age_calc,
250
+ 'shop_popularity': shop_pop,
251
+ 'shop_city_popularity': shop_city_pop,
252
+
253
  **feat_created, **feat_shop
254
  }
255
 
 
 
 
 
256
  base_input['Log_stock'] = np.log1p(stock)
257
+ base_input['Log_sold'] = np.log1p(0)
258
 
259
  st.divider()
 
260
  st.subheader("πŸ’° Price Prediction")
261
+
262
  if price_models:
263
+ col_model, col_btn = st.columns([1, 1])
264
+ with col_model:
265
+ model_name = st.selectbox("Select Model", list(price_models.keys()), label_visibility="collapsed")
266
+ with col_btn:
267
+ predict_btn = st.button("Predict Price", type="primary", use_container_width=True)
268
 
269
+ if predict_btn:
270
  raw_df = prepare_raw_dataframe(base_input, price_cols, vectorizer)
271
+
272
+ missing_cols = set(price_cols) - set(raw_df.columns)
273
+ for c in missing_cols:
274
+ raw_df[c] = 0
275
+ raw_df = raw_df[price_cols]
276
 
277
+ X_scaled = price_preprocess.transform(raw_df)
278
  log_pred = price_models[model_name].predict(X_scaled)[0]
279
  price_pred = np.expm1(log_pred)
280
 
281
  st.success(f"Estimated Price: **Rp {price_pred:,.0f}**")
282
+
283
+ with st.expander("Feature Importance Analysis"):
284
+ load_and_plot_importance(model_name, "price")
285
  else:
286
+ st.error("Models failed to load.")