Hey,
I know that there have been numerous other posts about this, but I’m trying to solve this creating my own css style sheet.
The problem I am having is that I cannot make a table and a pie chart appear side by side in my dashboard. I’m very new to using dash and have just been practicing with a toy dataset from kaggle, so I am not doing it very well. I have tried to use a css grid system, specifying the width of column classes in my css style sheet. However, even if I make the width of the two items I am trying to align side by side significantly less than half the page, they still appear in a single column.
Here is my Python code:
import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output
import plotly.express as px
import pandas as pd
import time
colours = {'background': '#161a28', 'text': '#f3f5f4'}
def build_banner():
"""Builds the banner for the top of the dashboard"""
return html.Div(
id="banner",
className="banner",
children=[
html.Div(
id="banner-text",
children=[
html.H5(
children="VEHICLES SALES ANALYSIS",
id='banner-header'
),
html.H6(
children="Sales detail broken down by product line",
id='banner-sub-header'
),
],
),
],
)
def generate_table(dataframe, max_rows=10):
"""Generates a dash HTML table from a pandas dataframe"""
return html.Table(children=[
html.Thead(
html.Tr([html.Th(col) for col in dataframe.columns])
),
html.Tbody([
html.Tr([
html.Td(dataframe.iloc[i][col]) for col in dataframe.columns
]) for i in range(min(len(dataframe), max_rows))
])],
id='left-bottom-table'
)
def build_date_slider():
"""Builds the slider for selecting a range of dates to
which are used to update the graphs and tables"""
return dcc.RangeSlider(
id='month-slider',
min=unix_time_ms(monthly_sales.index.min()),
max=unix_time_ms(monthly_sales.index.max()),
value=[unix_time_ms(monthly_sales.index.min()),
unix_time_ms(monthly_sales.index.max())],
marks=get_marks(start=monthly_sales.index.min(),
end=monthly_sales.index.max()),
step=None
)
def unix_time_ms(dt):
''' Convert datetime to unix timestamp '''
return int(time.mktime(dt.timetuple()))
def unix_to_datetime(unix):
''' Convert unix timestamp to datetime. '''
return pd.to_datetime(unix, unit='s')
def get_marks(start, end):
''' Returns the marks for labeling range slider'''
marks = {}
daterange = pd.date_range(start, end, freq='M')
for date in daterange:
# Append value to dict
marks[unix_time_ms(date)] = {'label': str(date.strftime('%Y-%m')),
'style': {'color': '#f3f5f4'}}
return marks
app = dash.Dash(__name__)
df = pd.read_csv('sales_data_sample.csv', encoding="ISO-8859-1")
df['DATE'] = df.apply(lambda row: str(row['YEAR_ID']) + '-' +
str(row['MONTH_ID']) + '-01', axis=1)
df['DATE'] = df['DATE'].apply(pd.to_datetime)
monthly_sales = pd.pivot_table(data=df,
index='DATE',
columns='PRODUCTLINE',
values='SALES',
aggfunc='sum')
df_for_table = df.groupby('PRODUCTLINE').sum()[['SALES', 'QUANTITYORDERED']]
df_for_table['Average price'] = (df_for_table['SALES']
/ df_for_table['QUANTITYORDERED'])
df_for_table = df_for_table.round(2).sort_values('SALES', ascending=False)
df_for_table.columns = ['Sales (USD)', 'Volume (Units)', 'ASP (USD)']
df_for_table.index.name = 'Product line'
app.layout = html.Div(
id='main-container',
className='main-container',
children=[
build_banner(),
html.Div(
id='app-container',
children=[
html.Div(
className='row',
children=[
html.Div(
className='twelve-columns',
children=[
dcc.Graph(className='graphs', id='sales-graph'),
build_date_slider()
]
)
]
),
html.Div(
className='row',
children=[
html.Div(
className='six-columns',
children=[
generate_table(df_for_table.reset_index()),
]
),
html.Div(
className='six-columns',
children=[
dcc.Graph(className='graphs',
id='right-bottom-pie')
]
)
]
)
]
)
]
)
@app.callback(
Output('sales-graph', 'figure'),
Output('left-bottom-table', 'children'),
Output('right-bottom-pie', 'figure'),
Input('month-slider', 'value'))
def update_graph(selected_period):
# Filter the df for the graph
filtered_graph = monthly_sales.loc[unix_to_datetime(selected_period[0]):
unix_to_datetime(selected_period[1])]
# Filter the df for the Table
df_for_table = df[(df['DATE'] > unix_to_datetime(selected_period[0])) &
(df['DATE'] < unix_to_datetime(selected_period[1]))]
df_for_table = df_for_table.groupby('PRODUCTLINE').sum()
df_for_table = df_for_table[['SALES', 'QUANTITYORDERED']]
df_for_table['Average price'] = (df_for_table['SALES']
/ df_for_table['QUANTITYORDERED'])
df_for_table = df_for_table.round(2).sort_values('SALES', ascending=False)
df_for_table.columns = ['Sales (USD)', 'Volume (Units)', 'ASP (USD)']
df_for_table.index.name = 'Product line'
df_for_table = df_for_table.reset_index()
# Make bar graph
fig = px.bar(data_frame=filtered_graph.reset_index(),
x='DATE',
y=['Classic Cars', 'Motorcycles', 'Planes',
'Ships', 'Trains', 'Trucks and Buses', 'Vintage Cars'],
labels={'value': 'Sales (USD)',
'variable': 'Product category',
'DATE': ''})
fig.update_layout(barmode='stack',
plot_bgcolor=colours['background'],
paper_bgcolor=colours['background'],
font=dict(color=colours['text']))
# Make Table
tab = generate_table(df_for_table)
# Make pie chart
pie = px.pie(data_frame=df_for_table,
values='Sales (USD)',
names='Product line')
pie.update_layout(plot_bgcolor=colours['background'],
paper_bgcolor=colours['background'],
font=dict(color=colours['text']))
return fig, tab, pie
if __name__ == '__main__':
app.run_server(debug=True)
And here is the relevent snippet from my css formatting:
.main-container {
max-width: 100%;
align-items: center;
padding-left: 0px;
padding-right: 0px;
padding-top: 0px;
padding-right: 0px;
background-color: inherit;
}
#app-container {
width: auto;
}
.twelve-columns {
width: 96%;
}
.six-columns {
width: 45%;
}
#left-bottom-table {
color: #f3f5f4;
background-color: #161a28;
width: 100%;
height: 350px;
margin-top: 5px;
margin-bottom: 5px;
}
#right-bottom-pie {
margin-top: 5px;
margin-bottom: 5px;
heght: 350px;
}
This is what I get back in the dash board:
I really appreciate any help with this, thank you very much.