Figure Friday 2025 - week 37

join the Figure Friday session on September 19, at noon Eastern Time, to showcase your creation and receive feedback from the community.

What type of products were sold on Amazon?

Answer this question and a few others by using Plotly on the Amazon Sales dataset.

Things to consider:

  • what can you improve in the app or sample figure below (subplots)?
  • would you like to tell a different data story using a different Plotly figure?
  • how can you explore the data with Plotly Studio?

Sample figure:
:folded_hands: Thank you to @Ester for the sample code and image.

Code for sample figure:
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots

# 1. Load your sales data from CSV
df = pd.read_csv("https://raw.githubusercontent.com/plotly/Figure-Friday/refs/heads/main/2025/week-37/amazon_sales_data.csv")

# 2. Convert the 'Date' column to datetime format
df["Date"] = pd.to_datetime(df["Date"], format="%d-%m-%y")

# 3. Filter for only COMPLETED orders (ignore Cancelled, Pending, etc. for analysis)
completed = df[df["Status"] == "Completed"]

# 4. Create a 'Month' column for grouping
completed["Month"] = completed["Date"].dt.to_period("M").astype(str)

# 5. Aggregate total sales by month
monthly_sales = completed.groupby("Month")["Total Sales"].sum()

# 6. Aggregate number of completed orders by month
monthly_orders = completed.groupby("Month")["Order ID"].count()

# 7. Determine the best-selling category (by total sales)
top_category = completed.groupby("Category")["Total Sales"].sum().idxmax()

# 8. For the top category, aggregate sales per month
cat_monthly = (
    completed[completed["Category"] == top_category]
    .groupby("Month")["Total Sales"].sum()
)

# 9. Setup three bar chart subplots side-by-side
fig = make_subplots(
    rows=1, cols=3,
    subplot_titles=[
        "Total Sales by Month",
        "Completed Orders by Month",
        f"{top_category} Sales by Month"
    ],
    shared_xaxes=False
)

# 10. Add total monthly sales bar chart (left)
fig.add_trace(
    go.Bar(
        x=monthly_sales.index,
        y=monthly_sales.values,
        marker_color="#377eb8",
        name="Total Sales",
        text=[f"${v:,.0f}" for v in monthly_sales.values],  # Show value on bars
        textposition="inside"
    ),
    row=1, col=1
)

# 11. Add completed orders count bar chart (middle)
fig.add_trace(
    go.Bar(
        x=monthly_orders.index,
        y=monthly_orders.values,
        marker_color="#ff7f00",
        name="Order Count",
        text=monthly_orders.values,  # Show count on bars
        textposition="inside"
    ),
    row=1, col=2
)

# 12. Add top category's monthly sales bar chart (right)
fig.add_trace(
    go.Bar(
        x=cat_monthly.index,
        y=cat_monthly.values,
        marker_color="#59a14f",
        name=f"{top_category} Sales",
        text=[f"${v:,.0f}" for v in cat_monthly.values],  # Show value on bars
        textposition="inside"
    ),
    row=1, col=3
)

# 13. Layout: Height, width, title with blank line below, and visual style
fig.update_layout(
    height=420,
    width=1350,
    title="Sales Dashboard: Monthly Bar Charts",  
    template="plotly_white",
    font=dict(size=13),
    margin=dict(t=110, l=20, r=20, b=30),  # Margins for breathing room
    showlegend=False
)

# 14. Set axis titles for all three subplots
fig.update_xaxes(title_text="Month", row=1, col=1)
fig.update_yaxes(title_text="Total Sales (USD)", row=1, col=1)
fig.update_xaxes(title_text="Month", row=1, col=2)
fig.update_yaxes(title_text="Completed Orders", row=1, col=2)
fig.update_xaxes(title_text="Month", row=1, col=3)
fig.update_yaxes(title_text=f"{top_category} Sales (USD)", row=1, col=3)

# 15. Show the figure in your browser or notebook
fig.show()

For community members that would like to build the data app with Plotly Studio, but don’t have the application yet, simply go to Plotly.com/studio.

Below is a screenshot of a figure built by Plotly Studio on top of this dataset:

Participation Instructions:

  • Create - use the weekly data set to build your own Plotly visualization or Dash app. Or, enhance the sample figure provided in this post, using Plotly or Dash.
  • Submit - post your creation to LinkedIn or Twitter with the hashtags #FigureFriday and #plotly by midnight Thursday, your time zone. Please also submit your visualization as a new post in this thread.
  • Celebrate - join the Figure Friday sessions to showcase your creation and receive feedback from the community.

:point_right: If you prefer to collaborate with others on Discord, join the Plotly Discord channel.

Data Source:

Thank you to Zahid Feroze on Kaggle for the data.

Hello, this week’s dataset is an interesting sample according to the dates. I propose two charts: one that reflects the volume of orders by category and another that shows the total sales of each product using a bubble chart to detect the best-performing products.

Open Application

6 Likes

Very interesting app, @U-Danny . Is this your first app on Plotly Cloud?

1 Like

Hi @adamschroeder, Yes, and the deployment is very fast. Also, updating it week by week is very easy.

2 Likes

wow, really cool chart. Very clever way of dealing with axes of different magnitudes while keeping the labels positioned together.

2 Likes

Hello Everyone, for this FF Week 37
I’ve built an small interactive customer analytics dashboard “Customer Journey Analyzer”

Core Functionality:
A customer selector dropdown and three analytical views:

  • Overview: Key performance metrics with market benchmark comparisons
  • Detailed Analysis: Visual breakdown of order patterns and revenue analysis
  • Evolution & Strategy: Temporal timeline with automated strategic insights

Key Features:

  • Market comparison system showing how each customer performs against your averages
  • Revenue segmentation (realized, lost, and potential revenue streams)
  • Interactive timeline visualization with product-level hover details
  • Automated insight generation that flags opportunities and concerns
  • Responsive design optimized for business stakeholder presentations
The Code

import pandas as pd
import plotly.graph_objects as go
from dash import Dash, html, dcc, callback, Output, Input
import dash_bootstrap_components as dbc
from datetime import datetime

“”"
Customer Journey Analyzer Dashboard
This dashboard provides a multi-faceted view of customer behavior,
including a comparison to global metrics, a detailed order funnel,
and a temporal analysis of their purchasing evolution.
“”"

1. Calculation and Analysis Functions

def calculate_global_metrics(df):
“”"
Calculates average metrics for all customers based ONLY on completed orders.
This provides a market benchmark for comparison.
“”"
completed_df = df[df[‘Status’] == ‘Completed’]

total_customers = df['Customer Name'].nunique()
completed_customers = completed_df['Customer Name'].nunique()
customer_order_counts = df.groupby('Customer Name').size()
repeat_customers = (customer_order_counts > 1).sum()

pending_customers = df[df['Status'] == 'Pending']['Customer Name'].nunique()
cancelled_customers = df[df['Status'] == 'Cancelled']['Customer Name'].nunique()

global_completion_rate = (completed_customers / total_customers) * 100 if total_customers > 0 else 0

total_revenue = completed_df['Total Sales'].sum()
avg_order_value = completed_df['Total Sales'].mean() if len(completed_df) > 0 else 0

customer_stats = completed_df.groupby('Customer Name').agg(
    total_spent=('Total Sales', 'sum'),
    avg_order_value=('Total Sales', 'mean'),
    order_count=('Customer Name', 'count'),
    categories_bought=('Category', 'nunique'),
    products_bought=('Product', 'nunique')
).fillna(0)

return {
    'total_customers_global': total_customers,
    'completed_customers_global': completed_customers,
    'repeat_customers_global': repeat_customers,
    'pending_customers_global': pending_customers,
    'cancelled_customers_global': cancelled_customers,
    'total_revenue_global': total_revenue,
    'avg_order_value_global': avg_order_value,
    'avg_total_spent': customer_stats['total_spent'].mean() if not customer_stats.empty else 0,
    'avg_order_count': customer_stats['order_count'].mean() if not customer_stats.empty else 0,
    'avg_categories': customer_stats['categories_bought'].mean() if not customer_stats.empty else 0,
    'global_completion_rate': global_completion_rate
}

def analyze_customer(df, customer_name):
“”"
Analyzes a specific customer’s journey, providing metrics for a funnel-focused view.
“”"
customer_data = df[df[‘Customer Name’] == customer_name].copy()
customer_data = customer_data.sort_values(‘Date’)

if customer_data.empty:
    return None

total_orders = len(customer_data)
completed = len(customer_data[customer_data['Status'] == 'Completed'])
pending = len(customer_data[customer_data['Status'] == 'Pending'])
cancelled = len(customer_data[customer_data['Status'] == 'Cancelled'])

completion_rate = (completed / total_orders) * 100 if total_orders > 0 else 0
pending_rate = (pending / total_orders) * 100 if total_orders > 0 else 0
cancel_rate = (cancelled / total_orders) * 100 if total_orders > 0 else 0

completed_data = customer_data[customer_data['Status'] == 'Completed']
total_spent = completed_data['Total Sales'].sum()
avg_order_value = completed_data['Total Sales'].mean() if not completed_data.empty else 0

cancelled_data = customer_data[customer_data['Status'] == 'Cancelled']
pending_data = customer_data[customer_data['Status'] == 'Pending']
lost_revenue = cancelled_data['Total Sales'].sum()
potential_revenue = pending_data['Total Sales'].sum()

categories_bought = completed_data['Category'].nunique() if not completed_data.empty else 0
products_bought = completed_data['Product'].nunique() if not completed_data.empty else 0

if not completed_data.empty:
    fav_category = completed_data.groupby('Category')['Total Sales'].sum().idxmax()
    fav_category_spent = completed_data.groupby('Category')['Total Sales'].sum().max()
    fav_category_pct = (fav_category_spent / total_spent) * 100 if total_spent > 0 else 0
else:
    fav_category = "N/A"
    fav_category_pct = 0

customer_data['Order_Number'] = range(1, len(customer_data) + 1)

return {
    'data': customer_data,
    'metrics': {
        'total_orders': total_orders,
        'completed': completed,
        'pending': pending,
        'cancelled': cancelled,
        'completion_rate': completion_rate,
        'pending_rate': pending_rate,
        'cancel_rate': cancel_rate,
        'total_spent': total_spent,
        'avg_order_value': avg_order_value,
        'lost_revenue': lost_revenue,
        'potential_revenue': potential_revenue,
        'categories_bought': categories_bought,
        'products_bought': products_bought,
        'fav_category': fav_category,
        'fav_category_pct': fav_category_pct
    }
}

2. Functions to create UI components

def create_kpi_card(title, value, format_type=‘number’, icon=‘:bar_chart:’):
“”“Creates a KPI card with a title, value, and icon.”“”
if format_type == ‘currency’:
value_str = f"${value:,.0f}"
elif format_type == ‘percentage’:
value_str = f"{value:.1f}%"
else:
value_str = f"{value:.0f}"

return dbc.Col(dbc.Card([
    dbc.CardBody([
        html.H6(f"{icon} {title}", className="card-subtitle mb-2 text-center text-muted"),
        html.H4(value_str, className=f"text-center mb-0 text-dark")
    ])
], className="shadow-sm border-0 h-100 bg-white"), width=12, md=3)

def create_comparison_card(title, customer_value, market_value, format_type=‘number’, icon=‘:bar_chart:’):
“”"
Creates a comparison card showing a customer’s metric vs. the market average.
Includes a status indicator based on the difference.
“”"
if format_type == ‘currency’:
customer_str = f"{customer_value:,.0f}" market_str = f"{market_value:,.0f}"
elif format_type == ‘percentage’:
customer_str = f"{customer_value:.1f}%"
market_str = f"{market_value:.1f}%"
else:
customer_str = f"{customer_value:.1f}"
market_str = f"{market_value:.1f}"

if market_value > 0:
    diff_pct = ((customer_value - market_value) / market_value) * 100
    if diff_pct > 10:
        status_color = "success"
        status_icon = "📈"
        status_text = f"+{diff_pct:.0f}%"
    elif diff_pct < -10:
        status_color = "danger"
        status_icon = "📉"
        status_text = f"{diff_pct:.0f}%"
    else:
        status_color = "secondary"
        status_icon = "➡️"
        status_text = f"{diff_pct:+.0f}%"
else:
    status_color = "info"
    status_icon = "ℹ️"
    status_text = "N/A"

return dbc.Col([
    dbc.Card([
        dbc.CardBody([
            html.H6(f"{icon} {title}", className="card-subtitle mb-2 text-muted"),
            html.H4(customer_str, className=f"text-{status_color} mb-1"),
            html.P(f"Market: {market_str}", className="text-muted mb-1", style={'fontSize': '0.9em'}),
            html.Span([
                status_icon, f" {status_text}"
            ], className=f"badge bg-{status_color}")
        ])
    ], className="shadow-sm border-0 h-100 bg-white")
], width=12, lg=3)

— Functions to render the content of each tab —

def _render_tab_1(metrics, global_metrics):
“”“Renders the content of the ‘Overview’ tab.”“”
# Section 1: Customer KPI Cards
kpi_cards = [
create_kpi_card(“Completion Rate”, metrics[‘completion_rate’], ‘percentage’, ‘:white_check_mark:’),
create_kpi_card(“Current Revenue”, metrics[‘total_spent’], ‘currency’, ‘:money_bag:’),
create_kpi_card(“Lost Revenue”, metrics[‘lost_revenue’], ‘currency’, ‘:cross_mark:’),
create_kpi_card(“Potential Revenue”, metrics[‘potential_revenue’], ‘currency’, ‘:hourglass_not_done:’)
]

# Section 2: Market Comparison Cards
comparison_cards = [
    create_comparison_card("Conversion", metrics['completion_rate'], global_metrics['global_completion_rate'], 'percentage', '✅'),
    create_comparison_card("Revenue/Customer", metrics['total_spent'], global_metrics['avg_total_spent'], 'currency', '💰'),
    create_comparison_card("Completed Orders", metrics['completed'], global_metrics['avg_order_count'], 'number', '📦'),
    create_comparison_card("Categories", metrics['categories_bought'], global_metrics['avg_categories'], 'number', '🏷️')
]

return html.Div([
    dbc.Row(dbc.Col(html.H4("Customer Journey Metrics", className="card-title text-center mb-2 text-info"))),
    dbc.Row(kpi_cards, className="g-4 mb-4"),
    dbc.Row(dbc.Col(html.H4("Customer vs. Market", className="card-title text-center mb-2 text-info"))),
    dbc.Row(comparison_cards, className="g-4")
])

def _render_tab_2(customer_data, metrics, selected_customer):
“”“Renders the content of the ‘Detailed Analysis’ tab.”“”
customer_counts = customer_data[‘Status’].value_counts().reset_index()
customer_counts.columns = [‘Status’, ‘count’]
status_order = [‘Completed’, ‘Pending’, ‘Cancelled’]
customer_counts[‘Status’] = pd.Categorical(customer_counts[‘Status’], categories=status_order, ordered=True)
customer_counts = customer_counts.sort_values(‘Status’)

fig_donut_chart = go.Figure(go.Pie(
    labels=customer_counts['Status'],
    values=customer_counts['count'],
    hole=.65,
    marker_colors=['#27ae60', '#f39c12', '#e74c3c'],
    textinfo='label+percent',
    insidetextorientation='radial',
    hoverinfo='label+value'
))
fig_donut_chart.update_layout(
    title_text=f"Order Distribution: {selected_customer}",
    legend=dict(
        orientation="h",
        yanchor="top",
        y=1.02,
        xanchor="center",
        x=0.5
    ),
    plot_bgcolor='rgba(0,0,0,0)',
    paper_bgcolor='rgba(0,0,0,0)',
    showlegend=False
)


total_potential = metrics['total_spent'] + metrics['lost_revenue'] + metrics['potential_revenue']
realized_pct = (metrics['total_spent'] / total_potential * 100) if total_potential > 0 else 0
lost_pct = (metrics['lost_revenue'] / total_potential * 100) if total_potential > 0 else 0
potential_pct = (metrics['potential_revenue'] / total_potential * 100) if total_potential > 0 else 0

revenue_analysis_content = [
    html.Div([html.H6("Current Revenue", className="mb-2 text-muted"), html.H5(f"${metrics['total_spent']:,.0f}", className="text-success"), html.Small(f"{realized_pct:.1f}% of potential", className="text-muted")], className="mb-3"),
    html.Div([html.H6("Lost Revenue", className="mb-2 text-muted"), html.H5(f"${metrics['lost_revenue']:,.0f}", className="text-danger"), html.Small(f"{lost_pct:.1f}% of potential", className="text-muted")], className="mb-3"),
    html.Div([html.H6("Potential Revenue", className="mb-2 text-muted"), html.H5(f"${metrics['potential_revenue']:,.0f}", className="text-warning"), html.Small(f"{potential_pct:.1f}% of potential", className="text-muted")], className="mb-3"),
    html.Hr(),
    html.Div([html.H6("Total Revenue Potential", className="mb-2 text-muted"), html.H4(f"${total_potential:,.0f}", className="text-primary")])
]

return dbc.Row([
    dbc.Col([
        html.H4("Order Evolution", className="card-title text-center mb-3 text-info"),
        dcc.Graph(figure=fig_donut_chart, style={'height': '500px'})
    ], width=12, lg=9),
    dbc.Col([
        html.H4("Revenue Analysis", className="card-title text-center mb-3 text-info"),
        dbc.Card(dbc.CardBody(revenue_analysis_content), className="shadow-sm border-0 h-100 bg-white")
    ], width=12, lg=3)
], className="g-4")

def _render_tab_3(customer_data, metrics, global_metrics, selected_customer):
“”“Renders the content of the ‘Evolution and Strategy’ tab.”“”
fig_timeline = go.Figure()
status_config = {
‘Completed’: {‘color’: ‘#2ecc71’, ‘symbol’: ‘star’, ‘name’: ‘Completed :white_check_mark:’},
‘Pending’: {‘color’: ‘#f39c12’, ‘symbol’: ‘diamond’, ‘name’: ‘Pending :hourglass_not_done:’},
‘Cancelled’: {‘color’: ‘#e74c3c’, ‘symbol’: ‘x’, ‘name’: ‘Cancelled :cross_mark:’}
}
for status in customer_data[‘Status’].unique():
status_data = customer_data[customer_data[‘Status’] == status].sort_values(‘Date’)
config = status_config.get(status, {‘color’: ‘#95a5a6’, ‘symbol’: ‘circle’, ‘name’: status})
fig_timeline.add_trace(go.Scatter(
x=status_data[‘Date’], y=status_data[‘Total Sales’], mode=‘markers+lines’, name=f"{config[‘name’]} ({len(status_data)})“,
marker=dict(size=15, color=config[‘color’], symbol=config[‘symbol’], line=dict(width=2, color=‘white’)),
line=dict(color=config[‘color’], width=2, dash=‘dot’),
hovertemplate=”%{text}
Date: %{x|%d-%m-%Y}
Value: %{y:,.0f}<br>Status: " + status + "<extra></extra>", text=[f"{row['Product']}" for _, row in status_data.iterrows()] )) avg_order_value = global_metrics['avg_order_value_global'] if avg_order_value > 0: fig_timeline.add_hline(y=avg_order_value, line_dash="dash", line_color="#3498db", annotation_text=f"💹 Market Avg: {avg_order_value:.0f}“, annotation_position=“top right”)
fig_timeline.update_layout(
title=f"Temporal Evolution: {selected_customer}”, xaxis_title=“Order Date”, yaxis_title=“Order Value ($)”,
hovermode=‘closest’, plot_bgcolor=‘rgba(0,0,0,0)’, paper_bgcolor=‘rgba(0,0,0,0)’,
font=dict(color=‘#2c3e50’),
xaxis=dict(tickformat=‘%d-%m-%Y’, tickangle=45, showgrid=True),
yaxis=dict(showgrid=True),
legend=dict(orientation=“h”, yanchor=“bottom”, y=1.02, xanchor=“left”, x=0)
)

insights = []
if metrics['completion_rate'] > global_metrics['global_completion_rate'] * 1.2:
    insights.append(dbc.Alert([html.H5("✅ Excellent Conversion!", className="alert-heading"), html.P(f"Their completion rate of {metrics['completion_rate']:.1f}% is significantly higher than the market average of {global_metrics['global_completion_rate']:.1f}%. This customer is highly reliable and valuable to your business.")], color="success", className="shadow-sm border-0"))
elif metrics['completion_rate'] < global_metrics['global_completion_rate'] * 0.7:
    insights.append(dbc.Alert([html.H5("⚠️ Improvement Opportunity", className="alert-heading"), html.P(f"With a completion rate of {metrics['completion_rate']:.1f}% vs. the market average of {global_metrics['global_completion_rate']:.1f}%, it's worth reviewing their journey. Look for ways to improve their experience!"), html.P("This is a chance to lend a helping hand! 🤝")], color="warning", className="shadow-sm border-0"))
if metrics['lost_revenue'] > metrics['total_spent'] * 0.3:
    insights.append(dbc.Alert([html.H5("💔 High Lost Revenue!", className="alert-heading"), html.P(f"There's ${metrics['lost_revenue']:,.0f} in lost revenue from cancellations. Investigate the reasons for abandonment to stop losing sales."), html.P("What went wrong here? 🤔")], color="danger", className="shadow-sm border-0"))
if metrics['potential_revenue'] > metrics['total_spent'] * 0.2:
    insights.append(dbc.Alert([html.H5("🚀 High Pending Potential!", className="alert-heading"), html.P(f"There's a massive ${metrics['potential_revenue']:,.0f} in pending orders. A great opportunity for follow-up and reminders to close those sales!"), html.P("The gold is within reach! ✨")], color="info", className="shadow-sm border-0"))
if metrics['fav_category_pct'] > 60:
    insights.append(dbc.Alert([html.H5(f"🎯 The {metrics['fav_category']} Specialist!", className="alert-heading"), html.P(f"{metrics['fav_category_pct']:.1f}% of their purchases are in {metrics['fav_category']}. This makes them an ideal candidate for targeted upselling."), html.P("Keep giving them what they love! 😉")], color="primary", className="shadow-sm border-0"))

return dbc.Row([
    dbc.Col([
        html.H4("Temporal Evolution", className="card-title text-center mb-3 text-info"),
        dcc.Graph(figure=fig_timeline, style={'height': '600px'})
    ], width=12, lg=9),
    dbc.Col([
        html.H4("Strategic Insights", className="card-title text-center mb-3 text-info"),
        dbc.Card(
            dbc.CardBody(
                html.Div(insights, style={'height': '530px', 'overflowY': 'scroll'})
            ), className="shadow-sm border-0 h-100 bg-white"
        )
    ], width=12, lg=3)
], className="g-4")

3. Data and global metrics

This line assumes you have a CSV file named “amazon_sales_data.csv”

df = pd.read_csv(“amazon_sales_data.csv”)
df[‘Date’] = pd.to_datetime(df[‘Date’], format=‘%d-%m-%y’, errors=‘coerce’)
df = df.sort_values([‘Customer Name’, ‘Date’])
global_metrics = calculate_global_metrics(df)

4. Initialize Dash App with a modern theme

app = Dash(name, external_stylesheets=[dbc.themes.ZEPHYR])
app.title = ‘Customer Journey Analyzer’

5. Layout structure

app.layout = dbc.Container([
# Titles
dbc.Row([
dbc.Col([
html.H1(“Customer Journey Analyzer”, className=“text-center mt-4 mb-2 text-primary”),
html.H5(“Customer Lifecycle Analysis with Amazon products sold from January-Mid April 2025”, className=“text-center mb-4 text-info”),
])
]),

# Customer dropdown
dbc.Row(dbc.Col(
    dbc.Card(dbc.CardBody(
        dbc.Row([
            dbc.Col(html.Label("Select Customer:", className="form-label fw-bold mb-2 text-dark"), width=3),
            dbc.Col(dcc.Dropdown(
                id='customer-dropdown',
                options=[{'label': str(customer), 'value': str(customer)}
                         for customer in sorted(df['Customer Name'].unique())],
                value=str(df['Customer Name'].iloc[0]),
                className="mb-0 bg-white text-dark"
            ), width=6)
        ])
    ), className="shadow-sm border-0 bg-white"), width=7, className="mx-auto mb-2"),
),

# Radio Items for tabs
dbc.Row(dbc.Col(
    dcc.RadioItems(
        id='radio-items',
        options=[
            {'label': html.Span('Overview', className="fw-bold fs-6"), 'value': 'tab-1'},
            {'label': html.Span('Detailed Analysis', className="fw-bold fs-6"), 'value': 'tab-2'},
            {'label': html.Span('Evolution & Strategy', className="fw-bold fs-6"), 'value': 'tab-3'}
        ],
        value='tab-1',
        className="d-flex justify-content-center flex-wrap gap-2 text-dark",
        inputClassName="me-1"
    ), className="mt-2 mb-5 text-center")
),

# Dynamic content container with loading feedback
dbc.Row([
    dbc.Col(
        dcc.Loading(
            id="loading-spinner",
            type="default",
            children=[html.Div(id="content-container", className="p-4")]
        )
    )
]),

# Footer
dbc.Row([
    dbc.Col([
        html.Hr(className="my-4"),
        html.P("Developed using Plotly | Dash | Data from Kaggle Thanks Zahid Feroze", className="text-center text-muted")
    ])
], className="mt-5 mb-3")

], fluid=True, style={‘background’: ‘linear-gradient(45deg, #d4edda 0%, #fffde4 100%)’}, className=“mt-4”)

6. Callbacks

@callback(
Output(“content-container”, “children”),
Input(“radio-items”, “value”),
Input(‘customer-dropdown’, ‘value’)
)
def render_content(selected_option, selected_customer):
customer_analysis = analyze_customer(df, selected_customer)
if not customer_analysis:
return html.Div(“No data available for this customer.”, className=“text-center text-muted”)

customer_data = customer_analysis['data']
metrics = customer_analysis['metrics']

if selected_option == "tab-1":
    return _render_tab_1(metrics, global_metrics)

elif selected_option == "tab-2":
    return _render_tab_2(customer_data, metrics, selected_customer)

elif selected_option == "tab-3":
    return _render_tab_3(customer_data, metrics, global_metrics, selected_customer)

return html.Div("Please select one of the tabs to view the content.", className="text-center text-muted")

server = app.server

the web app
https://amazon-sales.plotly.app

link working



5 Likes

Looks great as always. Your app link gives a 403, maybe you can give us permission to see the app.

You assumed that the two blue rows in the image are the same customer?

2 Likes

Hi Marianne,

I do not know if this message is for me but anyway, I´m solving the sharing issue, for some reason it does not allow me to set public. I just ask @nathandrezner for help :winking_face_with_tongue:

1 Like

Yes, the message was for you :slight_smile:

2 Likes

The image you share is your app?

1 Like

Ready you can access the app, @nathandrezner thanks a lot

1 Like

Very Interesting Marianne,

In the same image Olivia Wilson, Michael Brown purchase from different cities. May be they are frequent travelers. Good Catch

1 Like

It’s just the dataframe in Spider. But I had the same basic idea as you worked out, customers, assigned unique customer id’s (anonymous), scrolled a bit to see the history and noticed the different locations (and people ordering 5 washing machines, 3 smartphones etc. ). Our customers are part of the Star Trek crew and very greedy. :grinning_face:

2 Likes

:rofl: :rofl: :rofl: very funny, Well in the Time Line I did you can track hover on each point, to figure out what´s going on kkkk

Hi Marianne, I did a geographic view to compare by customer location what´s going on and customer names, here is the image, we can see in summary what´s going on

2 Likes

This weeks good intention was a) create something for FF and b) join the session on friday after 3 months, I think.

A is not working out well this week, I share it anyway because actually it made me think about lots of things. Not necessary this assignment. :grinning_face: I don’t think it provides many insights in a glance.

App: PyCafe - Dash - Don't make me think :-)

3 Likes

@marieanne I’m looking forward to seeing you back at FF sessions. We’ve missed you.
I think the heatmap you created is good. I might just keep the sales amount or the percentage amount. Both numbers present make it a bit harder to follow.

3 Likes

@Avacsiglo21 thank you for adding the footnote to your app, recognizing the data source.

I think it’s a great idea to add those percentages, indicating the difference between the market average and the customer.

Also, next to the temporal evolution graph, it’s smart how you added suggestions for the sales team :flexed_biceps:

2 Likes

How did you assign the customer id? Did you map name to id or the combination of name+location to id? In other words “Chris Martin” has one customer id and travels a lot or “We have two Chris Martins, one living in Boston and one living in New York, meaning two different customer id’s“?

2 Likes