import altair as alt import pandas as pd import panel as pn # Load the Panel extension pn.extension('vega') # Load the dataset data = pd.read_csv("/Users/kenzabaddou/Downloads/archive/marketing_campaign.csv", sep=";") # Data cleaning data = data.rename(columns=lambda x: x.strip()) # Remove leading and trailing spaces from column names data = data.dropna(subset=['Income']) mean_income = data['Income'].mean() data['Income'] = data['Income'].fillna(mean_income) data['total_spent'] = data[['MntWines', 'MntFruits', 'MntMeatProducts', 'MntFishProducts', 'MntSweetProducts', 'MntGoldProds']].sum(axis=1) # Define widgets education_dropdown = alt.binding_select(options=sorted(data['Education'].unique()), name='Education Level:') education_select = alt.selection_single(fields=['Education'], bind=education_dropdown, name='Select') # New widget: marital status dropdown marital_status_dropdown = alt.binding_select(options=sorted(data['Marital_Status'].unique()), name='Marital Status:') marital_status_select = alt.selection_single(fields=['Marital_Status'], bind=marital_status_dropdown, name='Select') # New widget: range slider for number of web visits num_web_visits_slider = alt.binding_range(min=0, max=data['NumWebVisitsMonth'].max(), step=1, name='Web Visits per Month:') num_web_visits_select = alt.selection_single(fields=['NumWebVisitsMonth'], bind=num_web_visits_slider, name='Select') # Add new widgets for product selection products = ['MntWines', 'MntFruits', 'MntMeatProducts', 'MntFishProducts', 'MntSweetProducts', 'MntGoldProds'] product_x_dropdown = alt.binding_select(options=products, name='Product X:') product_x_select = alt.selection_single(fields=['x_product'], bind=product_x_dropdown, init={'x_product': 'MntWines'}) product_y_dropdown = alt.binding_select(options=products, name='Product Y:') product_y_select = alt.selection_single(fields=['y_product'], bind=product_y_dropdown, init={'y_product': 'MntMeatProducts'}) # Define views # View 1: Bar chart of customer distribution by education level education_chart = alt.Chart(data).mark_bar().encode( x=alt.X('Education:N', title='Education Level'), y=alt.Y('count():Q', title='Number of Customers'), color='Education:N' ).properties(title='Customer Distribution by Education Level') # Define widgets education_dropdown = alt.binding_select(options=sorted(data['Education'].unique()), name='Education Level:') education_select = alt.selection_single(fields=['Education'], bind=education_dropdown, name='Select') income_slider = alt.binding_range(min=0, max=data['Income'].max(), step=1000, name='Annual Income:') income_select = alt.selection_single(fields=['Income'], bind=income_slider, name='Select') # View 2: Scatter plot of customer annual income and total amount spent on products (with interaction) scatter_chart = alt.Chart(data).mark_circle().encode( x=alt.X('Income:Q', title='Annual Income'), y=alt.Y('total_spent:Q', title='Total Amount Spent on Products'), color='Education:N', tooltip=[ alt.Tooltip('Education'), alt.Tooltip('Marital_Status'), alt.Tooltip('Income', format='$,.0f'), alt.Tooltip('total_spent', format='$,.0f') ] ).properties(title='Customer Annual Income vs Amount Spent on Products').add_selection( education_select, income_select, marital_status_select, num_web_visits_select ).transform_filter( education_select ).transform_filter( income_select ).transform_filter( marital_status_select ).transform_filter( num_web_visits_select ) # Define linked selection brush = alt.selection(type='interval') # View 5: Bar chart - Average Total Spending per Education Level (linked to scatter chart) avg_spending_by_education = alt.Chart(data).mark_bar().encode( x=alt.X('Education:N', title='Education Level'), y=alt.Y('mean(total_spent):Q', title='Average Total Spending'), tooltip=['Education', 'mean(total_spent):Q'] ).properties(title='Average Total Spending per Education Level').add_selection( brush ).transform_filter( education_select ) # View 6: Yearly Total Amount Spent on Products (linked to enrollment chart) yearly_total_spent_chart = alt.Chart(data).mark_line().encode( x=alt.X('year(Dt_Customer):O', title='Year'), y=alt.Y('sum(total_spent):Q', title='Total Amount Spent'), tooltip=['year(Dt_Customer):O', 'sum(total_spent):Q'] ).transform_filter( education_select ) # View 3: Timeline chart of new customer enrollments (with linked highlighting) enrollment_chart = alt.Chart(data).mark_line().encode( x=alt.X('year(Dt_Customer):T', title='Year of Enrollment'), y=alt.Y('count():Q', title='Number of New Enrollments'), color='Marital_Status:N').properties(title='New Customer Enrollments Over Time').add_selection( brush, education_select, income_select, marital_status_select, num_web_visits_select).transform_filter( education_select).transform_filter(income_select).transform_filter(marital_status_select).transform_filter( num_web_visits_select) # New View: Average spending per marital status (with linked highlighting) avg_spending_by_marital_status = alt.Chart(data).mark_bar().encode( x=alt.X('Marital_Status:N', title='Marital Status'), y=alt.Y('mean(total_spent):Q', title='Average Total Spending'), color='Marital_Status:N', tooltip=['Marital_Status', 'mean(total_spent):Q']).properties(title='Average Total Spending per Marital Status').add_selection(brush) import pandas as pd import altair as alt # Convert the 'Dt_Customer' column to datetime format data['Dt_Customer'] = pd.to_datetime(data['Dt_Customer']) # Create 'total_spent' column by summing up spending in different product categories data['total_spent'] = data[['MntWines', 'MntFruits', 'MntMeatProducts', 'MntFishProducts', 'MntSweetProducts', 'MntGoldProds']].sum(axis=1) # Calculate the average spending for each combination of Teenhome, Kidhome, and Marital_Status avg_spending = data.groupby(['Teenhome', 'Kidhome', 'Marital_Status'])['total_spent'].mean().reset_index() # Create a hover selection hover = alt.selection_single(on='mouseover', nearest=True, empty='none') # Create a selection for the dropdown marital_status_dropdown = alt.binding_select(options=sorted(data['Marital_Status'].unique()), name='Marital Status:') marital_status_select = alt.selection_single(fields=['Marital_Status'], bind=marital_status_dropdown, init={'Marital_Status': 'Married'}) # Create a grouped bar chart for kids chart_kids = alt.Chart(avg_spending).mark_bar().encode( x=alt.X('Kidhome:O', title='Number of Kids at Home'), y=alt.Y('total_spent:Q', title='Average Spending'), color=alt.condition(hover, 'Kidhome:O', alt.value('lightgray'), legend=alt.Legend(title='Number of Kids at Home')), tooltip=['Teenhome', 'Kidhome', 'total_spent', 'Marital_Status'] ).properties(title='Average Spending by Number of Kids at Home (Filtered by Marital Status)').add_selection( hover ) # Create a grouped bar chart for teens chart_teens = alt.Chart(avg_spending).mark_bar().encode( x=alt.X('Teenhome:O', title='Number of Teens at Home'), y=alt.Y('total_spent:Q', title='Average Spending'), color=alt.condition(hover, 'Teenhome:O', alt.value('lightgray'), legend=alt.Legend(title='Number of Teens at Home')), tooltip=['Teenhome', 'Kidhome', 'total_spent', 'Marital_Status'] ).properties(title='Average Spending by Number of Teens at Home (Filtered by Marital Status)').add_selection( hover ) # Combine the charts concat_chart = alt.hconcat(chart_kids, chart_teens).add_selection( marital_status_select ).transform_filter( marital_status_select ) """scatter_plot = alt.Chart(data).mark_circle().encode( x=alt.X('NumWebVisitsMonth:Q', title='Number of Web Visits per Month'), y=alt.Y('NumWebPurchases:Q', title='Number of Web Purchases'), size=alt.Size('count():Q', scale=alt.Scale(range=[50, 500]), legend=alt.Legend(title='Number of Customers')), color=alt.Color('count():Q', scale=alt.Scale(scheme='viridis'), legend=None), tooltip=['NumWebVisitsMonth', 'NumWebPurchases', 'count()'] ).properties(title='Scatter Plot of Web Visits per Month vs. Web Purchases') scatter_plot.interactive()""" # Define dropdown selection for marital status marital_status_dropdown = alt.binding_select(options=data['Marital_Status'].unique().tolist(), name='Marital Status: ') marital_status_selection = alt.selection_single(fields=['Marital_Status'], bind=marital_status_dropdown, name='Marital_Status', init={'Marital_Status': data['Marital_Status'].iloc[0]}) # Add a scatter plot and filter by marital status scatter_plot_filtered = alt.Chart(data).mark_circle().encode( x=alt.X('NumWebVisitsMonth:Q', title='Number of Web Visits per Month'), y=alt.Y('NumWebPurchases:Q', title='Number of Web Purchases'), size=alt.Size('count():Q', scale=alt.Scale(range=[50, 500]), legend=alt.Legend(title='Number of Customers')), color=alt.Color('count():Q', scale=alt.Scale(scheme='viridis'), legend=None), tooltip=['NumWebVisitsMonth', 'NumWebPurchases', 'count()'] ).properties(title='Scatter Plot of Web Visits per Month vs. Web Purchases Filtered by Marital Status').transform_filter( marital_status_selection ) # Add interactivity and the marital status selection to the scatter plot interactive_scatter_plot_filtered = scatter_plot_filtered.interactive().add_selection(marital_status_selection) interactive_scatter_plot_filtered # Group by 'Dt_Customer' and calculate average spending for new and returning customers new_customers = data[data['NumWebPurchases'] == 0].groupby('Dt_Customer')['total_spent'].mean().reset_index() returning_customers = data[data['NumWebPurchases'] > 0].groupby('Dt_Customer')['total_spent'].mean().reset_index() # new_customers and returning_customers DataFrames # Create line charts for new and returning customers without filtering new_line = alt.Chart(new_customers).mark_line().encode( x=alt.X('Dt_Customer:T', title='Date'), y=alt.Y('total_spent:Q', title='Average Spending'), color=alt.value('blue'), tooltip=['Dt_Customer', 'total_spent'] ).properties(title='New Customers') returning_line = alt.Chart(returning_customers).mark_line().encode( x=alt.X('Dt_Customer:T', title='Date'), y=alt.Y('total_spent:Q', title='Average Spending'), color=alt.value('green'), tooltip=['Dt_Customer', 'total_spent'] ).properties(title='Returning Customers') # Display the charts side by side combined_chart = alt.hconcat(new_line, returning_line) combined_chart # Merge Marital_Status to new_customers and returning_customers DataFrames new_customers = new_customers.merge(data[['Dt_Customer', 'Marital_Status']], on='Dt_Customer', how='left') returning_customers = returning_customers.merge(data[['Dt_Customer', 'Marital_Status']], on='Dt_Customer', how='left') # Filter data by marital status using transform_filter within the charts new_line_filtered = alt.Chart(new_customers).mark_line().encode( x=alt.X('Dt_Customer:T', title='Date'), y=alt.Y('total_spent:Q', title='Average Spending'), color=alt.value('blue'), tooltip=['Dt_Customer', 'total_spent'] ).properties(title='New Customers').transform_filter( marital_status_selection ) returning_line_filtered = alt.Chart(returning_customers).mark_line().encode( x=alt.X('Dt_Customer:T', title='Date'), y=alt.Y('total_spent:Q', title='Average Spending'), color=alt.value('green'), tooltip=['Dt_Customer', 'total_spent'] ).properties(title='Returning Customers').transform_filter( marital_status_selection ) # Add interactivity for panning and zooming interactive_chart_filtered = alt.layer(new_line_filtered, returning_line_filtered).resolve_scale(y='shared').interactive().add_selection(marital_status_selection) # Combine the interactive chart, the legend, and the marital status selection # Create legend legend = alt.Chart(pd.DataFrame({'legend': ['New Customers', 'Returning Customers'], 'color': ['blue', 'green']})).mark_point().encode( x=alt.value(20), y=alt.Y('legend', title=None), color=alt.Color('color', scale=None), tooltip=['legend'] ) # Combine the interactive chart, the legend, and the marital status selection final_chart_filtered = alt.hconcat(interactive_chart_filtered, legend).properties(title='Average Spending of New vs Returning Customers by Marital Status') final_chart_filtered import panel as pn pn.extension('vega') # Define dashboard layout dashboard = pn.Column( pn.Row( pn.Column( education_chart, avg_spending_by_education, avg_spending_by_marital_status, width=350 ), pn.Column( scatter_chart, enrollment_chart, width=700 ), ), pn.Row( concat_chart, width=1000 ), pn.Row( final_chart_filtered, width=1000 ), ) # Display dashboard dashboard.servable() app = pn.serve(dashboard, return_views=True) dashboard = pn.Column( pn.Row( pn.Column(education_chart, avg_spending_by_education, interactive_scatter_plot_filtered, width=350), scatter_chart, width=700 ), pn.Row( avg_spending_by_marital_status, concat_chart, width=1000 ), pn.Row( enrollment_chart, final_chart_filtered, width=1000 ) )