Drop files here
or click to upload
import streamlit as st
import pandas as pd
import numpy as np
from st_aggrid import AgGrid, GridOptionsBuilder, GridUpdateMode
from st_aggrid.shared import JsCode
import plotly.express as px
from datetime import datetime, timedelta
# Set page config
st.set_page_config(layout="wide", page_title="Sales Dashboard")
# Currency formatter for AG Grid
currency_formatter = JsCode("""
function(params) {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(params.value);
}
""")
@st.cache_data
def generate_sales_data(num_rows=100):
"""Generate sample sales data"""
start_date = datetime(2024, 1, 1)
dates = [start_date + timedelta(days=x) for x in range(num_rows)]
products = ['Laptop', 'Phone', 'Tablet', 'Watch', 'Headphones']
regions = ['North', 'South', 'East', 'West']
data = {
'Date': dates,
'Product': np.random.choice(products, num_rows),
'Region': np.random.choice(regions, num_rows),
'Sales': np.random.randint(1000, 5000, num_rows),
'Units': np.random.randint(1, 50, num_rows),
'Profit_Margin': np.random.uniform(0.1, 0.4, num_rows)
}
df = pd.DataFrame(data)
df['Revenue'] = df['Sales'] * (1 + df['Profit_Margin'])
return df
cell_click_handler = JsCode("""
function(params) {
const col = params.column.colId;
const row = params.node.data;
console.log('Cell clicked:', col, row);
}
""")
def main():
st.title("📊 Interactive Sales Dashboard")
# Sidebar controls
st.sidebar.header("Dashboard Controls")
num_rows = st.sidebar.slider("Number of Records", 50, 500, 100)
df = generate_sales_data(num_rows)
st.sidebar.subheader("Grid Options")
enable_selection = st.sidebar.checkbox("Enable Row Selection", True)
enable_filtering = st.sidebar.checkbox("Enable Filtering", True)
enable_sorting = st.sidebar.checkbox("Enable Sorting", True)
st.sidebar.subheader("Visualization")
chart_type = st.sidebar.selectbox(
"Chart Type",
["Bar", "Line", "Scatter"]
)
group_by = st.sidebar.selectbox(
"Group By",
["Product", "Region", "Date"]
)
metric = st.sidebar.selectbox(
"Metric",
["Sales", "Revenue", "Units"]
)
# Configure grid options
gb = GridOptionsBuilder.from_dataframe(df)
if enable_selection:
gb.configure_selection(
selection_mode="multiple",
use_checkbox=True,
groupSelectsChildren=True,
groupSelectsFiltered=True,
)
if enable_filtering:
gb.configure_default_column(filterable=True)
if enable_sorting:
gb.configure_default_column(sorteable=True)
# Configure columns with formatting
gb.configure_column("Date",
type=["dateColumnFilter", "customDateTimeFormat"],
custom_format_string="yyyy-MM-dd")
gb.configure_column("Sales",
type=["numericColumn", "numberColumnFilter"],
valueFormatter=currency_formatter,
precision=2)
gb.configure_column("Revenue",
type=["numericColumn", "numberColumnFilter"],
valueFormatter=currency_formatter,
precision=2)
gb.configure_column("Profit_Margin",
type=["numericColumn", "numberColumnFilter"],
valueFormatter=JsCode("function(params) { return (params.value * 100).toFixed(1) + '%'; }"),
precision=3)
gb.configure_column("Units",
type=["numericColumn", "numberColumnFilter"],
valueFormatter=JsCode("function(params) { return params.value.toLocaleString(); }"))
gb.configure_grid_options(onCellClicked=cell_click_handler)
grid_options = gb.build()
st.subheader("Sales Data Grid")
response = AgGrid(
df,
grid_options,
update_mode=GridUpdateMode.SELECTION_CHANGED,
allow_unsafe_jscode=True,
theme="material"
)
selected_data = pd.DataFrame(response['selected_rows'])
if not selected_data.empty:
plot_data = selected_data
else:
plot_data = df
col1, col2 = st.columns([3, 2])
with col1:
st.subheader("Sales Visualization")
agg_data = plot_data.groupby(group_by)[metric].agg(['sum', 'mean']).reset_index()
if chart_type == "Bar":
fig = px.bar(
agg_data,
x=group_by,
y='sum',
title=f"{metric} by {group_by}",
labels={'sum': f'Total {metric}'}
)
# Format currency for y-axis if showing monetary values
if metric in ['Sales', 'Revenue']:
fig.update_layout(yaxis_tickprefix='$', yaxis_tickformat=',.0f')
elif chart_type == "Line":
if group_by == "Date":
fig = px.line(
agg_data,
x=group_by,
y='sum',
title=f"{metric} over Time",
labels={'sum': f'Total {metric}'}
)
if metric in ['Sales', 'Revenue']:
fig.update_layout(yaxis_tickprefix='$', yaxis_tickformat=',.0f')
else:
st.warning("Line chart is best suited for time-series data. Consider selecting 'Date' as Group By.")
fig = px.bar(
agg_data,
x=group_by,
y='sum',
title=f"{metric} by {group_by}",
labels={'sum': f'Total {metric}'}
)
if metric in ['Sales', 'Revenue']:
fig.update_layout(yaxis_tickprefix='$', yaxis_tickformat=',.0f')
else:
fig = px.scatter(
agg_data,
x=group_by,
y='sum',
size='mean',
title=f"{metric} by {group_by}",
labels={'sum': f'Total {metric}'}
)
if metric in ['Sales', 'Revenue']:
fig.update_layout(yaxis_tickprefix='$', yaxis_tickformat=',.0f')
st.plotly_chart(fig, use_container_width=True)
with col2:
st.subheader("Summary Statistics")
summary_stats = pd.DataFrame({
'Metric': ['Total Sales', 'Average Sales', 'Total Revenue', 'Total Units'],
'Value': [
f"${plot_data['Sales'].sum():,.2f}",
f"${plot_data['Sales'].mean():,.2f}",
f"${plot_data['Revenue'].sum():,.2f}",
f"{plot_data['Units'].sum():,}"
]
})
st.dataframe(summary_stats, hide_index=True)
if __name__ == "__main__":
main()
Hi! I can help you with any questions about Streamlit and Python. What would you like to know?