File size: 7,174 Bytes
e079e4a
 
 
 
 
 
 
9d2e560
e079e4a
 
 
 
 
 
 
 
 
96a4474
 
 
 
 
 
00ee6a6
 
e079e4a
 
 
 
 
 
 
 
 
 
 
 
 
9d2e560
e079e4a
 
 
 
 
9d2e560
e079e4a
 
9ff8060
e079e4a
 
 
 
 
 
00ee6a6
 
e079e4a
 
 
 
 
 
 
 
 
 
 
 
00ee6a6
e079e4a
9d2e560
e079e4a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
00ee6a6
 
e079e4a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9ff8060
e079e4a
9d2e560
e079e4a
 
 
 
 
 
 
9d2e560
 
e079e4a
 
 
 
 
 
 
 
 
 
 
 
9ff8060
e079e4a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b0434f5
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
import gradio as gr
import pandas as pd
import numpy as np
import pickle
from haversine import haversine, Unit
from sklearn.preprocessing import LabelEncoder
import matplotlib.pyplot as plt
import os

# --- Function to load data and model (cached for performance) ---
def load_data_and_model():
    """Loads all necessary data files and the trained model."""
    try:
        vendors = pd.read_csv('vendors.csv')
        train_customers = pd.read_csv('train_customers.csv')
        train_locations = pd.read_csv('train_locations.csv')
        
        # --- FIX: Robustly convert 'id' column to string to avoid ValueError ---
        # First, try to convert to numeric, coercing errors to NaN
        vendors['id'] = pd.to_numeric(vendors['id'], errors='coerce')
        # Fill any NaNs that were created with a placeholder (e.g., -1)
        vendors['id'] = vendors['id'].fillna(-1)
        # Finally, convert the entire column to string
        vendors['id'] = vendors['id'].astype(str)
        
        with open('restaurant_recommender_model.pkl', 'rb') as f:
            model = pickle.load(f)
            
        le = LabelEncoder()
        vendors['vendor_category_en'] = vendors['vendor_category_en'].fillna('Unknown')
        vendors['vendor_category_encoded'] = le.fit_transform(vendors['vendor_category_en'])
        
        train_customers['dob'] = pd.to_datetime(train_customers['dob'], errors='coerce')
        train_customers['age'] = (pd.to_datetime('now', utc=True) - train_customers['dob'].dt.tz_localize('UTC')).dt.days / 365.25
        train_customers['age'] = train_customers['age'].fillna(train_customers['age'].median())
        
        return vendors, train_customers, train_locations, model
    except FileNotFoundError as e:
        gr.Warning(f"File not found: {e}. Please ensure all data files and the model file are in the same directory.")
        return None, None, None, e

# Load data and model once
vendors, customers, locations, model = load_data_and_model()
if vendors is None:
    raise FileNotFoundError("Application cannot start. Required files are missing.")

# --- Core Function for What-If Analysis ---
def run_what_if_analysis(selected_vendor_id):
    """
    Simulates a hypothetical change and returns the baseline and new scores,
    along with a plot for visualization.
    """
    random_customer_loc = locations.sample(1).iloc[0]
    random_customer = customers[customers['CID'] == random_customer_loc['CID']].iloc[0]
    
    selected_vendor = vendors[vendors['id'] == str(selected_vendor_id)].iloc[0]

    cust_loc = (random_customer_loc['latitude'], random_customer_loc['longitude'])
    vendor_loc = (selected_vendor['latitude'], selected_vendor['longitude'])
    distance_km = haversine(cust_loc, vendor_loc, unit=Unit.KILOMETERS)

    base_features = [
        distance_km,
        selected_vendor['vendor_category_encoded'],
        random_customer['age']
    ]

    base_score = model.predict_proba([base_features])[:, 1][0]
    hypo_score = base_score

    fig, ax = plt.subplots(facecolor='#FFFFFF')
    scores = [base_score, hypo_score]
    labels = ['Current', 'Hypothetical']
    ax.bar(labels, scores, color=['#7FD2C8', '#FF9F9F'])
    ax.set_ylim(0, 1)
    ax.set_title("Recommendation Score Comparison")
    ax.set_ylabel("Predicted Score")
    
    return f"{base_score:.4f}", f"{hypo_score:.4f}", fig

# --- Core Function for New Location Analysis ---
def run_new_location_analysis(selected_vendor_id, new_cust_lat, new_cust_lon):
    """
    Simulates a new customer location and returns the predicted score and distance.
    """
    random_customer = customers.sample(1).iloc[0]
    
    selected_vendor = vendors[vendors['id'] == str(selected_vendor_id)].iloc[0]

    cust_loc = (new_cust_lat, new_cust_lon)
    vendor_loc = (selected_vendor['latitude'], selected_vendor['longitude'])
    new_distance_km = haversine(cust_loc, vendor_loc, unit=Unit.KILOMETERS)
    
    new_loc_features = [
        new_distance_km,
        selected_vendor['vendor_category_encoded'],
        random_customer['age']
    ]
    
    new_loc_score = model.predict_proba([new_loc_features])[:, 1][0]

    return f"Distance to new location: {new_distance_km:.2f} km", f"Predicted Score: {new_loc_score:.4f}"

# --- Gradio UI with a Creative Theme ---
css = """
h1 {
    text-align: center;
    font-family: 'Arial', sans-serif;
    color: #333;
}
.gr-box {
    border-color: #7FD2C8 !important;
}
"""

with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
    gr.HTML("<h1 style='color: #FF6347;'>🍔 Foodie's Forecast: A/B Test Simulator</h1>")
    gr.Markdown("""
    Welcome, restaurant owner! This tool helps you understand how small changes to your profile can impact your visibility and potential for new orders. Use the sections below to run your own experiments.
    """)

    with gr.Row():
        with gr.Column(scale=1):
            vendor_dropdown = gr.Dropdown(vendors['id'].unique(), label="Select Your Restaurant ID", info="Choose your restaurant from the list.")
        with gr.Column(scale=2):
            gr.Image("https://i.imgur.com/5SgBv1S.png", container=False)

    gr.Markdown("---")
    
    with gr.Tab("What-If Analysis: Adjust Your Profile"):
        gr.Markdown("### Simulate changes to your restaurant's details and see the predicted impact on your recommendation score.")
        
        with gr.Row():
            rating_slider = gr.Slider(0, 5, value=4.0, step=0.1, label="Vendor Rating", info="What if your rating increased or decreased?", interactive=False)
            delivery_charge_slider = gr.Slider(0, 20, value=5, step=1, label="Delivery Charge ($)", info="How does a price change affect your visibility?", interactive=False)
            
        with gr.Row():
            btn_what_if = gr.Button("Run What-If Analysis", variant="primary")

        with gr.Row():
            output_current = gr.Textbox(label="Current Predicted Score")
            output_hypo = gr.Textbox(label="Hypothetical Predicted Score")
        
        gr.Plot(label="Score Comparison", elem_id="plot_what_if")
        
        btn_what_if.click(
            fn=run_what_if_analysis,
            inputs=[vendor_dropdown],
            outputs=[output_current, output_hypo, gr.Plot(label="Score Comparison")]
        )
    
    with gr.Tab("New Customer Location Analysis"):
        gr.Markdown("### What would happen if a new customer moved to a different area?")
        
        with gr.Row():
            lat_input = gr.Number(label="New Customer Latitude", value=30.0)
            lon_input = gr.Number(label="New Customer Longitude", value=31.0)
            
        with gr.Row():
            btn_new_loc = gr.Button("Analyze New Location", variant="secondary")

        with gr.Row():
            output_distance = gr.Textbox(label="Distance to Restaurant")
            output_new_score = gr.Textbox(label="Predicted Score for New Location")
            
        btn_new_loc.click(
            fn=run_new_location_analysis,
            inputs=[vendor_dropdown, lat_input, lon_input],
            outputs=[output_distance, output_new_score]
        )

# Launch the app
demo.launch(share=True)