Spaces:
Sleeping
Sleeping
Download app.py from Feranmii/flightdelay: direct link, hf CLI and curl.
- Browser
- Download file 16.5 kB
-
https://huggingface.co/spaces/Feranmii/flightdelay/resolve/main/app.py
- Command line
-
hf download hf://spaces/Feranmii/flightdelay/app.py
-
curl -L -o app.py https://huggingface.co/spaces/Feranmii/flightdelay/resolve/main/app.py
16.5 kB
| import hopsworks | |
| import os | |
| import joblib | |
| import pandas as pd | |
| import numpy as np | |
| import plotly.graph_objects as go | |
| from plotly.subplots import make_subplots | |
| os.environ["HOPSWORKS_API_KEY"] = "HLDlE8LYYNS4zTdX.GQvAhHVCPMDVOrXcbGK3IKrsUv2APbwSUzexJDgjhXHPRoC3aAjhOMKgkaM95UHt" | |
| project = hopsworks.login() | |
| fs = project.get_feature_store() | |
| mr = project.get_model_registry() | |
| mlp_model_meta = mr.get_model('mlp_airline_delay', version=1) | |
| mlp_model_dir = mlp_model_meta.download() | |
| mlp_model = joblib.load(f"{mlp_model_dir}/model.pkl") | |
| # Read data from Hopsworks feature store | |
| feature_group = fs.get_feature_group(name="new_flight_data", version=1) | |
| df_lagged = feature_group.read() | |
| # Infer min and max values for sliders | |
| slider_ranges = {} | |
| continuous_features = [ | |
| 'arr_flights', 'arr_diverted', 'temperature_2m', 'wind_speed_10m', | |
| 'arr_delay_lag1', 'arr_flights_lag2', 'weather_delay', 'security_delay', | |
| 'carrier_delay', 'arr_delay_lag1_lag2', 'arr_delay_lag1_lag3' | |
| ] | |
| for col in continuous_features: | |
| slider_ranges[col] = { | |
| "min": round(df_lagged[col].min(), 1), | |
| "max": round(df_lagged[col].max(), 1) | |
| } | |
| carrier_mapping = { | |
| "American Airlines": "carrier_aa", | |
| "Delta Air Lines": "carrier_dl", | |
| "United Airlines": "carrier_ua", | |
| "Southwest Airlines": "carrier_wn", | |
| "Alaska Airlines": "carrier_as", | |
| "JetBlue Airways": "carrier_b6", | |
| "Spirit Airlines": "carrier_nk", | |
| "Frontier Airlines": "carrier_f9", | |
| "Envoy Air": "carrier_mq", | |
| "SkyWest Airlines": "carrier_oo", | |
| "Mesa Airlines": "carrier_oh", | |
| "Republic Airways": "carrier_yx", | |
| "Horizon Air": "carrier_qx", | |
| "Allegiant Air": "carrier_ax", | |
| "US Airways": "carrier_us", | |
| "AirTran Airways": "carrier_fl" | |
| } | |
| def encode_month(month): | |
| month_sin = np.sin(2 * np.pi * month / 12) | |
| month_cos = np.cos(2 * np.pi * month / 12) | |
| return month_sin, month_cos | |
| def predict_delay(carrier, arr_flights, arr_diverted, temperature, wind_speed, month, | |
| arr_delay_lag1, arr_flights_lag2, weather_delay, security_delay, | |
| carrier_delay, arr_delay_lag1_lag2, arr_delay_lag1_lag3): | |
| # Encode month for cyclical features | |
| month_sin, month_cos = encode_month(month) | |
| # Create input DataFrame | |
| data = { | |
| 'arr_flights': [arr_flights], | |
| 'arr_diverted': [arr_diverted], | |
| 'temperature_2m': [temperature], | |
| 'wind_speed_10m': [wind_speed], | |
| 'month_sin': [month_sin], | |
| 'month_cos': [month_cos], | |
| 'arr_delay_lag1': [arr_delay_lag1], | |
| 'arr_flights_lag2': [arr_flights_lag2], | |
| 'weather_delay': [weather_delay], | |
| 'security_delay': [security_delay], | |
| 'carrier_delay': [carrier_delay], | |
| 'arr_delay_lag1_lag2': [arr_delay_lag1_lag2], | |
| 'arr_delay_lag1_lag3': [arr_delay_lag1_lag3] | |
| } | |
| input_df = pd.DataFrame(data) | |
| # One-hot encode carrier | |
| carrier_code = carrier_mapping.get(carrier) | |
| if carrier_code: | |
| input_df[carrier_code] = 1 | |
| # Ensure all carrier columns exist and fill missing ones with 0 | |
| for col in carrier_mapping.values(): | |
| if col not in input_df: | |
| input_df[col] = 0 | |
| # Reorder columns to match model's expected input | |
| input_df = input_df.reindex(columns=mlp_model.feature_names_in_, fill_value=0) | |
| # Make prediction | |
| prediction = mlp_model.predict(input_df) | |
| return round(prediction[0], 2) | |
| import gradio as gr | |
| def create_terminal_crowd(delay_minutes, n_passengers=200): | |
| # Normalize delay to determine crowd behavior | |
| # Assume delay range from -1 to 5 minutes | |
| normalized_delay = min(max((delay_minutes + 1) / 6, 0), 1) # 0 to 1 scale | |
| # Terminal layout dimensions | |
| terminal_width = 100 | |
| terminal_height = 60 | |
| # Calculate crowd parameters based on delay | |
| crowd_density = normalized_delay * 0.8 + 0.2 # 0.2 to 1.0 | |
| crowd_clustering = normalized_delay * 0.7 + 0.3 # 0.3 to 1.0 | |
| # Generate passenger positions | |
| n_actual_passengers = int(n_passengers * crowd_density) | |
| # Create different passenger groups | |
| # 1. Seated passengers (at gates) | |
| # 2. Moving passengers (walking through terminal) | |
| # 3. Clustered passengers (at info desks, security, etc.) | |
| # Define gate areas (x, y, width, height) | |
| gates = [ | |
| [10, 5, 20, 10], # Gate A | |
| [40, 5, 20, 10], # Gate B | |
| [70, 5, 20, 10], # Gate C | |
| [10, 45, 20, 10], # Gate D | |
| [40, 45, 20, 10], # Gate E | |
| [70, 45, 20, 10] # Gate F | |
| ] | |
| # Define info desk/security areas (x, y, radius) | |
| service_points = [ | |
| [50, 30, 6], # Main info desk | |
| [25, 30, 4], # Security point 1 | |
| [75, 30, 4] # Security point 2 | |
| ] | |
| # Allocate passengers to different areas based on delay | |
| seated_ratio = max(0.6 - normalized_delay * 0.4, 0.2) # More delay = fewer seated | |
| service_ratio = normalized_delay * 0.5 # More delay = more at service points | |
| walking_ratio = 1 - seated_ratio - service_ratio | |
| n_seated = int(n_actual_passengers * seated_ratio) | |
| n_service = int(n_actual_passengers * service_ratio) | |
| n_walking = n_actual_passengers - n_seated - n_service | |
| # Generate positions | |
| passenger_x = [] | |
| passenger_y = [] | |
| passenger_colors = [] | |
| # 1. Seated passengers (distributed across gates) | |
| gates_fullness = [0.3, 0.4, 0.5, 0.6, 0.7, 0.8] # Different fullness per gate | |
| for i in range(n_seated): | |
| # Select a gate, with higher delay making some gates more full than others | |
| gate_idx = min(int(i / n_seated * len(gates)), len(gates) - 1) | |
| gate = gates[gate_idx] | |
| # Add random position within gate area | |
| fullness = gates_fullness[gate_idx] * (1 + normalized_delay * 0.5) # Adjusted for delay | |
| gate_crowd_factor = min(fullness, 1.0) # Cap at 1.0 | |
| # Generate position with more clustering as gate gets fuller | |
| if gate_crowd_factor < 0.5: | |
| # Spread out | |
| x = gate[0] + np.random.uniform(0, gate[2]) | |
| y = gate[1] + np.random.uniform(0, gate[3]) | |
| else: | |
| # Cluster more | |
| x = gate[0] + gate[2]/2 + np.random.normal(0, gate[2]/4) | |
| y = gate[1] + gate[3]/2 + np.random.normal(0, gate[3]/4) | |
| # Keep within bounds | |
| x = max(min(x, gate[0] + gate[2]), gate[0]) | |
| y = max(min(y, gate[1] + gate[3]), gate[1]) | |
| passenger_x.append(x) | |
| passenger_y.append(y) | |
| passenger_colors.append('blue') # Seated passengers are blue | |
| # 2. Service point passengers (clustered at info desks/security) | |
| for i in range(n_service): | |
| # More delay means more concentration at the main info desk | |
| if normalized_delay > 0.7 and np.random.random() < 0.7: | |
| point_idx = 0 # Main info desk | |
| else: | |
| point_idx = min(int(i / n_service * len(service_points)), len(service_points) - 1) | |
| point = service_points[point_idx] | |
| # Cluster around the service point with degree based on delay | |
| cluster_tightness = 0.5 + normalized_delay * 0.5 # 0.5 to 1.0 | |
| distance = np.random.exponential(point[2] / cluster_tightness) | |
| angle = np.random.uniform(0, 2 * np.pi) | |
| # Polar to cartesian conversion for clustered distribution | |
| x = point[0] + distance * np.cos(angle) | |
| y = point[1] + distance * np.sin(angle) | |
| # Keep within terminal bounds | |
| x = max(min(x, terminal_width), 0) | |
| y = max(min(y, terminal_height), 0) | |
| passenger_x.append(x) | |
| passenger_y.append(y) | |
| passenger_colors.append('red') # Service point passengers are red (frustrated) | |
| # 3. Walking passengers (random paths in terminal) | |
| central_path_y = terminal_height / 2 | |
| path_width = 20 | |
| for i in range(n_walking): | |
| # With more delay, people tend to wander more randomly | |
| if normalized_delay < 0.4: | |
| # Mostly following the central path | |
| x = np.random.uniform(0, terminal_width) | |
| y = central_path_y + np.random.normal(0, path_width * 0.2) | |
| else: | |
| # More random movements | |
| x = np.random.uniform(0, terminal_width) | |
| y = np.random.uniform(0, terminal_height) | |
| # Keep within terminal bounds | |
| x = max(min(x, terminal_width), 0) | |
| y = max(min(y, terminal_height), 0) | |
| passenger_x.append(x) | |
| passenger_y.append(y) | |
| # Color walking passengers by delay frustration level | |
| if normalized_delay < 0.3: | |
| passenger_colors.append('green') # Happy/Normal | |
| elif normalized_delay < 0.7: | |
| passenger_colors.append('orange') # Mild frustration | |
| else: | |
| passenger_colors.append('red') # High frustration | |
| return passenger_x, passenger_y, passenger_colors, normalized_delay | |
| def predict_delay_interface(carrier, arr_flights, arr_diverted, temperature, wind_speed, month, | |
| arr_delay_lag1, arr_flights_lag2, weather_delay, security_delay, | |
| carrier_delay, arr_delay_lag1_lag2, arr_delay_lag1_lag3): | |
| # Get prediction | |
| delay_minutes = predict_delay( | |
| carrier, arr_flights, arr_diverted, temperature, wind_speed, month, | |
| arr_delay_lag1, arr_flights_lag2, weather_delay, security_delay, | |
| carrier_delay, arr_delay_lag1_lag2, arr_delay_lag1_lag3 | |
| ) | |
| # Create airport terminal crowd simulation | |
| passenger_x, passenger_y, passenger_colors, normalized_delay = create_terminal_crowd(delay_minutes) | |
| # Create terminal layout for visualization | |
| fig = make_subplots(rows=1, cols=1) | |
| # Add terminal background | |
| terminal_width = 100 | |
| terminal_height = 60 | |
| # Draw terminal structure | |
| # Main walking area | |
| fig.add_shape( | |
| type="rect", | |
| x0=0, y0=0, x1=terminal_width, y1=terminal_height, | |
| line=dict(width=2, color="gray"), | |
| fillcolor="lightgray", | |
| opacity=0.3 | |
| ) | |
| # Gates | |
| gates = [ | |
| [10, 5, 20, 10, "Gate A"], | |
| [40, 5, 20, 10, "Gate B"], | |
| [70, 5, 20, 10, "Gate C"], | |
| [10, 45, 20, 10, "Gate D"], | |
| [40, 45, 20, 10, "Gate E"], | |
| [70, 45, 20, 10, "Gate F"] | |
| ] | |
| for gate in gates: | |
| fig.add_shape( | |
| type="rect", | |
| x0=gate[0], y0=gate[1], x1=gate[0]+gate[2], y1=gate[1]+gate[3], | |
| line=dict(width=1, color="blue"), | |
| fillcolor="lightblue", | |
| opacity=0.4 | |
| ) | |
| # Add gate label | |
| fig.add_annotation( | |
| x=gate[0]+gate[2]/2, y=gate[1]+gate[3]/2, | |
| text=gate[4], | |
| showarrow=False, | |
| font=dict(size=10) | |
| ) | |
| # Info desk and security points | |
| service_points = [ | |
| [50, 30, 6, "Information"], | |
| [25, 30, 4, "Security 1"], | |
| [75, 30, 4, "Security 2"] | |
| ] | |
| for point in service_points: | |
| fig.add_shape( | |
| type="circle", | |
| x0=point[0]-point[2], y0=point[1]-point[2], | |
| x1=point[0]+point[2], y1=point[1]+point[2], | |
| line=dict(width=1, color="darkgray"), | |
| fillcolor="darkgray", | |
| opacity=0.4 | |
| ) | |
| # Add label | |
| fig.add_annotation( | |
| x=point[0], y=point[1], | |
| text=point[3], | |
| showarrow=False, | |
| font=dict(size=10, color="black") | |
| ) | |
| # Add passengers as scatter points | |
| fig.add_trace( | |
| go.Scatter( | |
| x=passenger_x, y=passenger_y, | |
| mode='markers', | |
| marker=dict( | |
| size=7, | |
| color=passenger_colors, | |
| opacity=0.7 | |
| ), | |
| name='Passengers' | |
| ) | |
| ) | |
| # Add delay information | |
| # Create color for delay text based on severity | |
| if delay_minutes < 0: | |
| delay_color = "green" | |
| delay_text = "Early by" | |
| elif delay_minutes < 1: | |
| delay_color = "green" | |
| delay_text = "On time (+" | |
| elif delay_minutes < 3: | |
| delay_color = "orange" | |
| delay_text = "Delayed by" | |
| else: | |
| delay_color = "red" | |
| delay_text = "Significantly delayed by" | |
| # Add delay annotation | |
| fig.add_annotation( | |
| xref="paper", yref="paper", | |
| x=0.5, y=0.97, | |
| text=f"<b>{delay_text} {abs(delay_minutes):.1f} minutes</b>", | |
| showarrow=False, | |
| font=dict(size=16, color=delay_color), | |
| align="center", | |
| bgcolor="rgba(255, 255, 255, 0.8)", | |
| bordercolor=delay_color, | |
| borderwidth=2, | |
| borderpad=4 | |
| ) | |
| # Add passenger count and frustration level | |
| total_passengers = len(passenger_x) | |
| frustrated_passengers = passenger_colors.count('red') + passenger_colors.count('orange') | |
| frustration_percent = round(frustrated_passengers / total_passengers * 100) | |
| fig.add_annotation( | |
| xref="paper", yref="paper", | |
| x=0.5, y=0.03, | |
| text=f"Terminal occupancy: {total_passengers} passengers | Frustration level: {frustration_percent}%", | |
| showarrow=False, | |
| font=dict(size=12), | |
| align="center", | |
| bgcolor="rgba(255, 255, 255, 0.8)", | |
| borderpad=4 | |
| ) | |
| # Customize layout | |
| fig.update_layout( | |
| title=f"Airport Terminal Crowd Simulation - {carrier}", | |
| showlegend=False, | |
| xaxis=dict( | |
| range=[-5, terminal_width+5], | |
| showgrid=False, | |
| zeroline=False, | |
| visible=False | |
| ), | |
| yaxis=dict( | |
| range=[-5, terminal_height+5], | |
| showgrid=False, | |
| zeroline=False, | |
| visible=False, | |
| scaleanchor="x", | |
| scaleratio=1 | |
| ), | |
| margin=dict(l=20, r=20, t=50, b=20), | |
| plot_bgcolor='white' | |
| ) | |
| return fig | |
| # Define input components | |
| inputs = [ | |
| gr.Dropdown(choices=list(carrier_mapping.keys()), label="Carrier"), | |
| gr.Slider(minimum=slider_ranges['arr_flights']['min'], maximum=slider_ranges['arr_flights']['max'], step=0.1, label="Arriving Flights Previous Month (Scaled)"), | |
| gr.Slider(minimum=slider_ranges['arr_diverted']['min'], maximum=slider_ranges['arr_diverted']['max'], step=0.1, label="Diverted Flights Previous Month (Scaled)"), | |
| gr.Slider(minimum=slider_ranges['temperature_2m']['min'], maximum=slider_ranges['temperature_2m']['max'], step=0.1, label="Temperature (Scaled)"), | |
| gr.Slider(minimum=slider_ranges['wind_speed_10m']['min'], maximum=slider_ranges['wind_speed_10m']['max'], step=0.1, label="Wind Speed (Scaled)"), | |
| gr.Slider(minimum=1, maximum=12, step=1, label="Month"), | |
| gr.Slider(minimum=slider_ranges['arr_delay_lag1']['min'], maximum=slider_ranges['arr_delay_lag1']['max'], step=0.1, label="Arrival Delay Previous Month (Scaled)"), | |
| gr.Slider(minimum=slider_ranges['arr_flights_lag2']['min'], maximum=slider_ranges['arr_flights_lag2']['max'], step=0.1, label="Arriving Flights 2 Months Ago (Scaled)"), | |
| gr.Slider(minimum=slider_ranges['weather_delay']['min'], maximum=slider_ranges['weather_delay']['max'], step=0.1, label="Weather Delay (Scaled)"), | |
| gr.Slider(minimum=slider_ranges['security_delay']['min'], maximum=slider_ranges['security_delay']['max'], step=0.1, label="Security Delay (Scaled)"), | |
| gr.Slider(minimum=slider_ranges['carrier_delay']['min'], maximum=slider_ranges['carrier_delay']['max'], step=0.1, label="Carrier Delay (Scaled)"), | |
| gr.Slider(minimum=slider_ranges['arr_delay_lag1_lag2']['min'], maximum=slider_ranges['arr_delay_lag1_lag2']['max'], step=0.1, label="Arrival Delay 2 Months Ago (Scaled)"), | |
| gr.Slider(minimum=slider_ranges['arr_delay_lag1_lag3']['min'], maximum=slider_ranges['arr_delay_lag1_lag3']['max'], step=0.1, label="Arrival Delay 3 Months Ago (Scaled)") | |
| ] | |
| outputs = gr.Plot(label="Airport Terminal Crowd Simulation") | |
| gr.Interface( | |
| fn=predict_delay_interface, | |
| inputs=inputs, | |
| outputs=outputs, | |
| title="Flight Delay Prediction with Terminal Crowd Simulation", | |
| description="Predict flight delays and visualize how they affect the airport terminal crowd. As delays increase, passengers shift from gates to information desks and security points, with increasing frustration levels (blue = calm seated passengers not experiencing any delay, green = calm passengers who aren't frustrated by the delay, orange = mild frustration, red = high frustration)." | |
| ).launch() |