""" Visualization functions for the sales dashboard. """ import plotly.express as px import plotly.graph_objects as go from plotly.subplots import make_subplots import pandas as pd def create_profit_trend_chart(monthly_trends): """ Create profit trend line chart. """ if monthly_trends.empty: return go.Figure() fig = go.Figure() fig.add_trace(go.Scatter( x=monthly_trends['Date'], y=monthly_trends['Profit'], mode='lines+markers', name='Profit', line=dict(color='#2E86AB', width=3), marker=dict(size=8) )) fig.update_layout( title='Monthly Profit Trend', xaxis_title='Month', yaxis_title='Profit ($)', hovermode='x unified', template='plotly_white' ) return fig def create_quantity_trend_chart(monthly_trends): """ Create quantity sold trend line chart. """ if monthly_trends.empty: return go.Figure() fig = go.Figure() fig.add_trace(go.Scatter( x=monthly_trends['Date'], y=monthly_trends['Quantity Sold'], mode='lines+markers', name='Units Sold', line=dict(color='#A23B72', width=3), marker=dict(size=8), fill='tozeroy', fillcolor='rgba(162, 59, 114, 0.1)' )) fig.update_layout( title='Monthly Units Sold Trend', xaxis_title='Month', yaxis_title='Units Sold', hovermode='x unified', template='plotly_white' ) return fig def create_top_models_bar_chart(top_models, metric='Profit'): """ Create bar chart for top models. """ if top_models.empty: return go.Figure() metric_label = 'Profit ($)' if metric == 'Profit' else 'Units Sold' colors = ['#2E86AB', '#A23B72', '#F18F01', '#C73E1D', '#048A81'] * (len(top_models) // 5 + 1) fig = go.Figure(data=[ go.Bar( x=top_models['Model'], y=top_models[metric], marker_color=colors[:len(top_models)], text=top_models[metric].apply(lambda x: f'${x:,.0f}' if metric == 'Profit' else f'{x:,.0f}'), textposition='outside' ) ]) fig.update_layout( title=f'Top Models by {metric}', xaxis_title='Model', yaxis_title=metric_label, template='plotly_white' ) return fig def create_top_dealers_chart(top_dealers, metric='Profit'): """ Create bar chart for top dealers. """ if top_dealers.empty: return go.Figure() metric_label = 'Profit ($)' if metric == 'Profit' else 'Units Sold' fig = go.Figure(data=[ go.Bar( x=top_dealers['Dealer ID'].astype(str), y=top_dealers[metric], marker_color='#2E86AB', text=top_dealers[metric].apply(lambda x: f'${x:,.0f}' if metric == 'Profit' else f'{x:,.0f}'), textposition='outside' ) ]) fig.update_layout( title=f'Top Dealers by {metric}', xaxis_title='Dealer ID', yaxis_title=metric_label, template='plotly_white' ) return fig def create_seasonal_heatmap(sales_df): """ Create heatmap of sales by month and year. """ if sales_df.empty: return go.Figure() pivot_data = sales_df.pivot_table( values='Profit', index='Year', columns='Month_Name', aggfunc='sum', fill_value=0 ) # Reorder months month_order = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] pivot_data = pivot_data[[m for m in month_order if m in pivot_data.columns]] fig = px.imshow( pivot_data, text_auto='.0s', aspect='auto', color_continuous_scale='Viridis', title='Seasonal Sales Heatmap (Profit)' ) fig.update_layout( xaxis_title='Month', yaxis_title='Year', template='plotly_white' ) return fig def create_model_share_pie_chart(top_models, n=6): """ Create pie chart for model market share. """ if top_models.empty: return go.Figure() top_n = top_models.head(n).copy() other_sum = top_models.iloc[n:]['Profit'].sum() if len(top_models) > n else 0 if other_sum > 0: other_row = pd.DataFrame({'Model': ['Other'], 'Profit': [other_sum]}) top_n = pd.concat([top_n, other_row], ignore_index=True) fig = px.pie( top_n, values='Profit', names='Model', title='Profit Distribution by Model', color_discrete_sequence=px.colors.qualitative.Set3 ) fig.update_traces(textposition='inside', textinfo='percent+label') fig.update_layout(template='plotly_white') return fig def create_model_comparison_chart(model_performance): """ Create comparison chart for model performance metrics. """ if model_performance.empty: return go.Figure() fig = make_subplots( rows=1, cols=2, subplot_titles=('Profit by Model', 'Units Sold by Model'), specs=[[{'type': 'bar'}, {'type': 'bar'}]] ) # Profit bar chart fig.add_trace( go.Bar( x=model_performance['Model'], y=model_performance['Profit'], name='Profit', marker_color='#2E86AB', text=model_performance['Profit'].apply(lambda x: f'${x:,.0f}'), textposition='outside' ), row=1, col=1 ) # Units bar chart fig.add_trace( go.Bar( x=model_performance['Model'], y=model_performance['Quantity Sold'], name='Units Sold', marker_color='#F18F01', text=model_performance['Quantity Sold'].apply(lambda x: f'{x:,.0f}'), textposition='outside' ), row=1, col=2 ) fig.update_layout( title='Model Performance Comparison', height=500, template='plotly_white', showlegend=False ) fig.update_xaxes(tickangle=45) return fig def create_top_models_over_time_chart(ranking_over_time): """ Create line chart showing top models' performance over time. """ if ranking_over_time.empty: return go.Figure() fig = go.Figure() colors = ['#2E86AB', '#A23B72', '#F18F01', '#C73E1D', '#048A81'] for i, model in enumerate(ranking_over_time['Model'].unique()): model_data = ranking_over_time[ranking_over_time['Model'] == model] fig.add_trace(go.Scatter( x=model_data['Date'], y=model_data['Profit'], mode='lines+markers', name=model, line=dict(color=colors[i % len(colors)], width=2), marker=dict(size=6) )) fig.update_layout( title='Top Models Profit Over Time', xaxis_title='Date', yaxis_title='Profit ($)', hovermode='x unified', template='plotly_white' ) return fig def create_quarterly_breakdown_chart(quarterly_data): """ Create bar chart for quarterly breakdown. """ if quarterly_data.empty: return go.Figure() fig = make_subplots( rows=2, cols=1, subplot_titles=('Quarterly Profit', 'Quarterly Units Sold'), vertical_spacing=0.15 ) # Profit chart fig.add_trace( go.Bar( x=quarterly_data['Year_Quarter'], y=quarterly_data['Profit'], name='Profit', marker_color='#2E86AB', text=quarterly_data['Profit'].apply(lambda x: f'${x:,.0f}'), textposition='outside' ), row=1, col=1 ) # Units chart fig.add_trace( go.Bar( x=quarterly_data['Year_Quarter'], y=quarterly_data['Quantity Sold'], name='Units Sold', marker_color='#A23B72', text=quarterly_data['Quantity Sold'].apply(lambda x: f'{x:,.0f}'), textposition='outside' ), row=2, col=1 ) fig.update_layout( title='Quarterly Sales Breakdown', height=600, template='plotly_white', showlegend=False ) fig.update_xaxes(tickangle=45) return fig def create_dealer_map(dealer_performance): """ Create map visualization for dealer locations. """ if dealer_performance.empty or 'Latitude' not in dealer_performance.columns: return go.Figure() # Filter out rows with missing coordinates map_data = dealer_performance.dropna(subset=['Latitude', 'Longitude']) if map_data.empty: return go.Figure() fig = px.scatter_geo( map_data, lat='Latitude', lon='Longitude', size='Profit', hover_name='Dealer Name', hover_data={ 'City': True, 'State': True, 'Profit': ':$,.0f', 'Quantity Sold': ':,.0f' }, title='Dealer Locations and Performance', projection='albers usa', size_max=50, color='Profit', color_continuous_scale='Viridis' ) fig.update_layout( title_x=0.5, geo=dict( scope='usa', showland=True, landcolor='rgb(243, 243, 243)', countrycolor='rgb(204, 204, 204)' ), template='plotly_white' ) return fig def create_recalls_impact_chart(recalls_impact): """ Create chart showing recall impact on models. """ if recalls_impact.empty: return go.Figure() fig = make_subplots( rows=1, cols=2, subplot_titles=('Recall Ratio by Model', 'Recall Units vs Total Sales'), specs=[[{'type': 'bar'}, {'type': 'bar'}]] ) # Recall ratio chart fig.add_trace( go.Bar( x=recalls_impact['Model'].head(10), y=recalls_impact['Recall_Ratio'].head(10), name='Recall Ratio', marker_color='#C73E1D', text=recalls_impact['Recall_Ratio'].head(10).apply(lambda x: f'{x:.1%}'), textposition='outside' ), row=1, col=1 ) # Recall vs Sales chart (using a subset) comparison_data = recalls_impact.head(10).copy() fig.add_trace( go.Bar( x=comparison_data['Model'], y=comparison_data['Recall_Units'], name='Recall Units', marker_color='#F18F01' ), row=1, col=2 ) fig.add_trace( go.Bar( x=comparison_data['Model'], y=comparison_data['Quantity Sold'], name='Total Sales', marker_color='#2E86AB' ), row=1, col=2 ) fig.update_layout( title='Recall Impact Analysis', height=500, template='plotly_white', barmode='group', legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='right', x=1) ) fig.update_xaxes(tickangle=45) return fig def create_yearly_growth_chart(yearly_summary): """ Create chart showing year-over-year growth. """ if yearly_summary.empty: return go.Figure() fig = make_subplots( rows=1, cols=2, subplot_titles=('Year-over-Year Profit Growth', 'Year-over-Year Units Growth'), specs=[[{'type': 'bar'}, {'type': 'bar'}]] ) # Profit growth fig.add_trace( go.Bar( x=yearly_summary['Year'].astype(str), y=yearly_summary['Profit_Growth'].fillna(0), name='Profit Growth %', marker_color='#2E86AB', text=yearly_summary['Profit_Growth'].fillna(0).apply(lambda x: f'{x:.1f}%'), textposition='outside' ), row=1, col=1 ) # Units growth fig.add_trace( go.Bar( x=yearly_summary['Year'].astype(str), y=yearly_summary['Units_Growth'].fillna(0), name='Units Growth %', marker_color='#A23B72', text=yearly_summary['Units_Growth'].fillna(0).apply(lambda x: f'{x:.1f}%'), textposition='outside' ), row=1, col=2 ) fig.update_layout( title='Year-over-Year Growth', height=400, template='plotly_white', showlegend=False ) return fig