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()
|