File size: 3,904 Bytes
c342d58
3f276cc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c342d58
3f276cc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import streamlit as st
import pandas as pd
import numpy as np
import joblib
import holidays
import xgboost as xgb
from datetime import date

# Load the model
@st.cache_resource
def load_artifacts():
    model = joblib.load('src/xgb_model.joblib')
    encoders = joblib.load('src/encoders.joblib')
    return model, encoders

try:
    model, encoders = load_artifacts()
except FileNotFoundError:
    st.error("Model files not found. Please upload 'xgb_model.joblib' and 'encoders.joblib'.")
    st.stop()

# Feature Engineering 
def create_features(df):
    df = df.copy()
    df['date'] = pd.to_datetime(df['date'])
    df['year'] = df['date'].dt.year
    df['month'] = df['date'].dt.month
    df['day'] = df['date'].dt.day
    df['day_of_week'] = df['date'].dt.dayofweek
    df['day_of_year'] = df['date'].dt.dayofyear
    df['week_of_year'] = df['date'].dt.isocalendar().week.astype(int)
    df['is_weekend'] = (df['day_of_week'] >= 5).astype(int)
    
    # Cyclical Encoding
    df['day_sin'] = np.sin(2 * np.pi * df['day_of_year'] / 365.0)
    df['day_cos'] = np.cos(2 * np.pi * df['day_of_year'] / 365.0)
    df['month_sin'] = np.sin(2 * np.pi * df['month'] / 12.0)
    df['month_cos'] = np.cos(2 * np.pi * df['month'] / 12.0)
    
    return df

def add_holiday_features(df, encoders):
    df = df.copy()
    # Inverse transform to get country names for holiday library
    df['country_name'] = encoders['country'].inverse_transform(df['country'])
    years = df['date'].dt.year.unique()
    country_codes = {'Canada': 'CA','Finland': 'FI','Italy': 'IT','Kenya': 'KE','Norway': 'NO','Singapore': 'SG'}
    
    df['is_holiday'] = 0
    
    for country_name, code in country_codes.items():
        try:
            country_holidays = holidays.Country(code, years=years)
            mask = (df['country_name'] == country_name) & (df['date'].isin(country_holidays))
            df.loc[mask, 'is_holiday'] = 1
        except:
            continue
    df = df.drop(columns=['country_name'])
    return df

# UI
st.title("🛒 Sticker Sales Prediction")
st.write("Enter the details below to predict the number of items sold.")

# Input
col1, col2 = st.columns(2)

with col1:
    country_options = list(encoders['country'].classes_)
    store_options = list(encoders['store'].classes_)
    product_options = list(encoders['product'].classes_)
    
    selected_date = st.date_input("Select Date", value=date(2025, 1, 1))
    selected_country = st.selectbox("Select Country", country_options)

with col2:
    selected_store = st.selectbox("Select Store", store_options)
    selected_product = st.selectbox("Select Product", product_options)

# 4. Prediction Button
if st.button("Predict Sales"):
    input_data = pd.DataFrame({
        'date': [pd.to_datetime(selected_date)],
        'country': [selected_country],
        'store': [selected_store],
        'product': [selected_product]})
    
    processed_data = create_features(input_data)
    categorical_cols = ['country', 'store', 'product']
    for col in categorical_cols:
        processed_data[col] = encoders[col].transform(processed_data[col])
    processed_data = add_holiday_features(processed_data, encoders)
    
    features = ['country', 'store', 'product', 'year', 'month', 'day', 'day_of_week', 'day_of_year', 'is_weekend', 
                'day_sin', 'day_cos', 'month_sin', 'month_cos', 'is_holiday']
    
    X_input = processed_data[features]
    
    # Prediction
    try:
        pred_log = model.predict(X_input)
        
        final_prediction = np.expm1(pred_log)[0]
        final_prediction = max(0, final_prediction) # Ensure no negative sales
        
        st.success(f"Predicted Num Sold: **{final_prediction:.2f}**")
        
        with st.expander("See processed features"):
            st.dataframe(X_input)
            
    except Exception as e:
        st.error(f"An error occurred during prediction: {e}")