Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import pandas as pd
|
3 |
+
import numpy as np
|
4 |
+
import json
|
5 |
+
from io import StringIO
|
6 |
+
|
7 |
+
|
8 |
+
def dynamic_huff_model(df_distances, df_attractiveness, alpha, beta, df_capacity, df_population=None, iterations=5, crowding_threshold=1.0):
|
9 |
+
"""
|
10 |
+
Iteratively calculates the distribution of people/visitors to destinations considering capacity and crowding based on an extended Huff model with linear decay function.
|
11 |
+
|
12 |
+
Parameters:
|
13 |
+
- df_distances, df_attractiveness, alpha, beta, df_capacity, df_population are the same as before.
|
14 |
+
- iterations (int): The number of iterations to distribute the population.
|
15 |
+
- crowding_threshold (float): The ratio of current visitors to capacity at which the decay of attractiveness starts.
|
16 |
+
|
17 |
+
Returns:
|
18 |
+
- pd.DataFrame: A DataFrame with the final distribution of visitors to each destination.
|
19 |
+
"""
|
20 |
+
if df_population is None:
|
21 |
+
df_population = pd.Series(np.ones(df_distances.shape[0]), index=df_distances.index)
|
22 |
+
|
23 |
+
# Initialize the visitors DataFrame
|
24 |
+
df_visitors = pd.DataFrame(0, index=df_distances.index, columns=df_distances.columns)
|
25 |
+
|
26 |
+
# Distribute the population over the iterations
|
27 |
+
df_population_per_iteration = df_population / iterations
|
28 |
+
|
29 |
+
# Run the iterative distribution process
|
30 |
+
for _ in range(iterations):
|
31 |
+
attractiveness = df_attractiveness.copy()
|
32 |
+
current_visitors = df_visitors.sum(axis=0)
|
33 |
+
|
34 |
+
# Calculate the decay based on the relative share of free capacity
|
35 |
+
relative_crowding = current_visitors / df_capacity
|
36 |
+
decay_factor = np.where(relative_crowding < crowding_threshold, 1, 1 - (relative_crowding - crowding_threshold) / (1 - crowding_threshold))
|
37 |
+
attractiveness *= decay_factor
|
38 |
+
|
39 |
+
# Calculate Huff model probabilities
|
40 |
+
distance_term = df_distances ** -beta
|
41 |
+
numerator = (attractiveness ** alpha).multiply(distance_term, axis='columns')
|
42 |
+
denominator = numerator.sum(axis='columns')
|
43 |
+
probabilities = numerator.div(denominator, axis='index').fillna(0)
|
44 |
+
|
45 |
+
# Distribute visitors based on probabilities and population
|
46 |
+
visitors_this_iteration = probabilities.multiply(df_population_per_iteration, axis='index')
|
47 |
+
|
48 |
+
# Adjust for excess visitors beyond capacity
|
49 |
+
potential_new_visitors = df_visitors + visitors_this_iteration
|
50 |
+
excess_visitors = potential_new_visitors.sum(axis=0) - df_capacity
|
51 |
+
excess_visitors[excess_visitors < 0] = 0
|
52 |
+
visitors_this_iteration -= visitors_this_iteration.multiply(excess_visitors, axis='columns') / visitors_this_iteration.sum(axis=0)
|
53 |
+
|
54 |
+
df_visitors += visitors_this_iteration
|
55 |
+
|
56 |
+
# Return the final distribution of visitors
|
57 |
+
return df_visitors
|
58 |
+
|
59 |
+
def app_function(input_json):
|
60 |
+
# Parse the input JSON string
|
61 |
+
inputs = json.loads(input_json)
|
62 |
+
|
63 |
+
# Convert stringified CSVs in JSON to DataFrames
|
64 |
+
df_distances = pd.read_csv(StringIO(inputs["df_distances"]))
|
65 |
+
df_attractiveness = pd.Series(json.loads(inputs["df_attractiveness"]))
|
66 |
+
alpha = inputs["alpha"]
|
67 |
+
beta = inputs["beta"]
|
68 |
+
df_capacity = pd.Series(json.loads(inputs["df_capacity"]))
|
69 |
+
df_population = pd.Series(json.loads(inputs["df_population"])) if "df_population" in inputs else None
|
70 |
+
iterations = inputs.get("iterations", 5)
|
71 |
+
crowding_threshold = inputs.get("crowding_threshold", 1.0)
|
72 |
+
|
73 |
+
# Call the dynamic Huff model function with these parameters
|
74 |
+
result = dynamic_huff_model(df_distances, df_attractiveness, alpha, beta, df_capacity, df_population, iterations, crowding_threshold)
|
75 |
+
|
76 |
+
# Convert the result DataFrame to a CSV string for Gradio output
|
77 |
+
return result.to_csv(index=False)
|
78 |
+
|
79 |
+
# Define the Gradio interface with a single JSON input
|
80 |
+
iface = gr.Interface(
|
81 |
+
fn=app_function,
|
82 |
+
inputs=gr.inputs.Textbox(label="Input JSON", lines=20, placeholder="Enter JSON with all parameters here..."),
|
83 |
+
outputs="file",
|
84 |
+
title="Dynamic Huff Model"
|
85 |
+
)
|
86 |
+
|
87 |
+
iface.launch()
|