from dash import Dash, dcc, html, Input, Output, callback, dash_table
import pandas as pd
import plotly.graph_objs as go
import dash_ag_grid as dag
# Load the CSV file
df = pd.read_csv('GroceryDB_foods.csv')
# Strip spaces from column names
df.columns = df.columns.str.strip()
# Rename for easier access
df.rename(columns={'harmonized single category': 'category'}, inplace=True)
# Drop rows where category is missing
df = df.dropna(subset=['category'])
# Calculate per 100g values for each food item
category_summary = df.groupby('category').agg({
'Protein': 'mean',
'Carbohydrate': 'mean',
'Total Fat': 'mean',
'price': 'mean'
}).reset_index()
# Merge category averages with original data
df = df.merge(category_summary, on='category', suffixes=('', '_avg'))
# Create Dash app
app = Dash(__name__)
# Get unique categories for dropdown
all_categories = category_summary['category'].unique()
# Layout
app.layout = html.Div([
html.H1("๐ Nutritional Content & Price by Category ๐ฅ", style={'textAlign': 'center', 'color': '#333'}),
html.Div([
html.Label("Select Categories:", style={'fontSize': '16px', 'fontWeight': 'bold'}),
dcc.Dropdown(
id='category-dropdown',
options=[{'label': cat, 'value': cat} for cat in all_categories],
value=all_categories[:5].tolist(), # Default to first 5 categories
multi=True
)
], style={'width': '70%', 'margin': '20px auto'}),
dcc.Graph(id='nutrition-price-graph', style={'height': '600px'}),
html.H2("Detailed Product Data", style={'textAlign': 'center', 'marginTop': '20px'}),
dag.AgGrid(
id='data-table',
columnDefs=[
{'headerName': 'Product Name', 'field': 'name'},
{'headerName': 'Category', 'field': 'category'},
{'headerName': 'Protein (g)', 'field': 'Protein'},
{'headerName': 'Carbohydrates (g)', 'field': 'Carbohydrate'},
{'headerName': 'Total Fat (g)', 'field': 'Total Fat'},
{'headerName': 'Price (USD)', 'field': 'price'},
#{'headerName': 'Avg Protein (g)', 'field': 'Protein_avg'},
#{'headerName': 'Avg Carbohydrates (g)', 'field': 'Carbohydrate_avg'},
#{'headerName': 'Avg Total Fat (g)', 'field': 'Total Fat_avg'},
#{'headerName': 'Avg Price (USD)', 'field': 'price_avg'}
],
rowData=[],
defaultColDef={'sortable': True, 'filter': True, 'resizable': True},
style={'height': '400px', 'width': '90%', 'margin': 'auto'}
)
])
@callback(
Output('nutrition-price-graph', 'figure'),
Output('data-table', 'rowData'),
Input('category-dropdown', 'value')
)
def update_graph_and_table(selected_categories):
if not selected_categories:
selected_categories = all_categories[:5].tolist()
filtered_data = category_summary[category_summary['category'].isin(selected_categories)]
filtered_products = df[df['category'].isin(selected_categories)][['category', 'name', 'Protein', 'Carbohydrate', 'Total Fat', 'price', 'Protein_avg', 'Carbohydrate_avg', 'Total Fat_avg', 'price_avg']].to_dict('records')
fig = go.Figure()
fig.add_trace(go.Bar(
y=filtered_data['category'],
x=filtered_data['Protein'],
name='Protein',
marker=dict(color='#410445'),
orientation='h'
))
fig.add_trace(go.Bar(
y=filtered_data['category'],
x=filtered_data['Carbohydrate'],
name='Carbohydrates',
marker=dict(color='#A5158C'),
orientation='h'
))
fig.add_trace(go.Bar(
y=filtered_data['category'],
x=filtered_data['Total Fat'],
name='Total Fat',
marker=dict(color='#FF2DF1'),
orientation='h'
))
fig.add_trace(go.Scatter(
y=filtered_data['category'],
x=filtered_data['price'],
mode='markers',
name='Average Price',
xaxis='x2',
marker=dict(color='gold', size=35, symbol='circle')
))
fig.update_layout(
title='Nutritional Content & Price by Category',
xaxis={'title': 'Nutritional Value (g/100g)'},
xaxis2={'title': 'Average Price (USD)', 'overlaying': 'x', 'side': 'top'},
barmode='stack',
showlegend=True,
paper_bgcolor='white',
plot_bgcolor='white'
)
return fig, filtered_products
if __name__ == '__main__':
app.run(debug=True)