销售数据可视化大屏制作步骤详解
- 1 项目效果图
- 2 项目架构
- 3 文件介绍和功能完善
- 3.1 assets文件夹介绍
- 3.2 app.py和index.py文件完善
- 3.3 header.py文件完善
- 3.4 api.py文件和api.ipynb文件完善
- 3.4.1 需求数据获取
- 3.4.2 header.py文件中数据变更
- 3.5 middle.py文件完善
- 3.5.1 中间第一部分图形绘制
- 3.5.2 中间第二部分图形绘制
- 3.5.3 中间第三部分图形绘制
- 3.5.4 中间第四部分信息卡片制作
- 3.6 bottom.py文件完善
- 3.6.1 底部第一个部分表格制作
- 3.6.2 底部第二部分图形绘制
- 3.6.3 底部第三部分图形绘制
- 4 样式修改
- 4.1 整体样式修改
- 4.2 header.py文件样式修改
- 4.3 middle.py文件样式修改
- 4.4 bottom.py文件样式修改
手动反爬虫: 原博地址 https://blog.csdn.net/lys_828/article/details/128646202
知识梳理不易,请尊重劳动成果,文章仅发布在CSDN网站上,在其他网站看到该博文均属于未经作者授权的恶意爬取信息
1 项目效果图
整个项目的页面包含三部分,由上中下三栏构成。项目已上传至 个人Github仓库 。
2 项目架构
项目中各文件名称与对应功能见下表 。整个项目是由上中下三部分组成,头部信息较为简单,重点在于中部和下部的内容设置。
3 文件介绍和功能完善
3.1 assets文件夹介绍
assets文件夹中就是加载的样式和图片,样式这里可以使用之前已经下载好的bootstrap.min.css样式(也可以按照 项目6.2bootstrap组件 中的介绍下载其它的样式模板),本次项目中不需要加载本地图片。
3.2 app.py和index.py文件完善
主框架就是app.py项目初始化文件和index.py主程序运行文件,其中app.py文件中的信息较为简单,就是创建一个dash的应用,代码如下。
index.py文件中引入初始化后的应用,然后进行布局及运行初始化设置。布局的主体是上中下三个部分,分别对应header.py、middle.py和bottom.py文件中的三个函数,具体代码如下。
from dash import html
import dash_bootstrap_components as dbc
from app import app
from header import HeaderInfo
from middle import MiddleInfo
from bottom import BottomInfo
app.layout = html.Div(
[
html.Div([HeaderInfo()]),
html.Div([MiddleInfo()]),
html.Div([BottomInfo()])
]
)
if __name__ == '__main__':
app.run_server(debug=True)
布局的三个文件中的内容初始化如下,头部信息和底部信息都是一行三列,中间是一行四列布局。(api.py和api.ipynb文件暂时没有内容)
3.3 header.py文件完善
该文件包含了三个部分,最左侧是项目标题,对应一行文字;中间是一个时间选择Slider组件(Rangeslider组件是选取时间跨度,Slider组件是获取具体年份),右侧是一个RadioItems组件(Checklist组件是多选,Radioiitems组件是单选)。该文件中的全部代码如下。
from dash import html,dcc
import dash_bootstrap_components as dbc
def HeaderInfo():
return dbc.Row(
[
dbc.Col([html.H3('Sales Dashboard')]),
dbc.Col([
html.H6('销售年份'),
dcc.Slider(
min=2015,max=2018,step=1,
value=2018,
id='slider'
)]),
dbc.Col([
html.H6('Segment'),
dcc.RadioItems(
options=['Consumer','Comporate','Home Office'],
value='Comporate',
id='radio'
)
]),
]
)
保存文件后,运行index.py文件,点击网址http://127.0.0.1:8050/,刷新页面,输出结果如下。
3.4 api.py文件和api.ipynb文件完善
这两个文件会处理后续所有图表中需要使用到的数据,所以文件会根据需要进行代码加入。首先获取中间部分的数据,第一个图是不同子类别,不同地区下的年份销售数据;第二个图形是不同类别的年份销售数据占比;第三个图是不同月份的年份销售趋势图;第四个是年份销售额相关数据。
3.4.1 需求数据获取
打开api.ipynb文件,读入数据文件。
然后借助时间解析函数,快速获取年、月、日信息。
接着获取年份和部门的唯一值,这里是用作头部信息中的数据,替换掉当时指定的固定数值。
最后就是封装一个get_data()
函数,里面有两个参数year
和segment
,就是对应着头部信息中的两个筛选组件的标签,方便回调时快速获取数据。
把以上的代码转移到api.py文件中,此时api.py中的全部代码如下。
import pandas as pd
import warnings #由于版本的关系可能出现警告,忽略掉即可
warnings.filterwarnings('ignore')
#读入数据文件
data = pd.read_csv('data/train.csv')
def get_year_month_day(df,time_col):
'''Extract the year, month, and day of the time field data'''
df[time_col] = pd.to_datetime(df[time_col])
df['year'] = df[time_col].dt.year
df['month'] = df[time_col].dt.month
df['day'] = df[time_col].dt.day
return df
#借助时间解析函数,获取年、月、日信息
data = get_year_month_day(data,'Order Date')
#获取数据的年份唯一值,核实没有缺失值None
year_ls = data['year'].unique()
# 获取类别部门唯一值,核实没有缺失值
segment_ls = data['Segment'].unique()
#封装获取数据的过程,方便回调
def get_data(year,segment):
return data[(data['year']==year) & (data['Segment']==segment)]
3.4.2 header.py文件中数据变更
然后把数据变量导入到文件中使用,首先把头部信息中的Slider组件和RadioItems组件标签更换一下,如下。
3.5 middle.py文件完善
3.5.1 中间第一部分图形绘制
接着处理中间部分第一个的图像。首先解决数据问题,该图中上方还有一个筛选的组件,然后图中显示的信息就是按照这个组件的标签显示。即使用get_data()
函数获取数据后,再按照上方的指定标签进行Sales
字段的分组求和,最后进行排序后输出前五条数据。
此时该文件中的全部代码如下。
from dash import html,dcc,Input,Output
import dash_bootstrap_components as dbc
from app import app
from api import get_data
import plotly.express as px
def MiddleInfo():
return dbc.Row(
[
dbc.Col([Sub_Category_or_Region()]),
dbc.Col([]),
dbc.Col([]),
dbc.Col([])
]
)
def Sub_Category_or_Region():
return html.Div(
[
dcc.RadioItems(
['Sub-Category','Region'],
'Sub-Category',
id = 'radio-sub-category-or-region'
),
dcc.Graph(id='h-bar-sub-category-or-region')
]
)
@app.callback(
Output('h-bar-sub-category-or-region','figure'),
[Input('slider-year','value'),Input('radio-segment','value'),Input('radio-sub-category-or-region','value')]
)
def update(year,segment,sub_category_or_region):
df = get_data(year,segment)
sales_data_series = df.groupby(sub_category_or_region)['Sales'].sum().sort_values(ascending=False)[:5][::-1]
fig = px.bar(x=sales_data_series.values,y=sales_data_series.index,orientation='h')
fig.update_layout(
margin=dict(t=50,r=0,l=0,b=0),
title={'text': f'Sales by {sub_category_or_region} {year}',
'y': 0.99,
'x': 0.5,
'xanchor': 'center',
'yanchor': 'top'}
)
return fig
保存文件后,刷新网页,输出结果如下。测试图中的信息可以随着筛选组件的选择发生变化(包括头部的筛选组件)
3.5.2 中间第二部分图形绘制
该图形是按照类别Category字段统计年份销售额的占比情况。定义一个CategoryPie()
函数用于创建容器只需要指定一个id即可,然后放在布局的第二个Col()
中,此部分的代码如下。
def CategoryPie():
return dcc.Graph(id='pie-category')
@app.callback(
Output('pie-category','figure'),
[Input('slider-year','value'),Input('radio-segment','value')]
)
def update(year,segment):
df = get_data(year,segment)
type_ = 'Category'
sales_data_series = df.groupby(type_)['Sales'].sum()
fig=px.pie(values=sales_data_series.values,names=sales_data_series.index)
fig.update_layout(
margin=dict(t=50,r=0,l=0,b=0),
title={'text': f'Sales by {type_} {year}',
'y': 0.99,
'x': 0.5,
'xanchor': 'center',
'yanchor': 'top'}
)
return fig
保存文件后,刷新网址,输出内容如下。
通过观察,发现图形布局样式的更新存在大量的代码重复,因此,可以考虑封装一个函数,把样式布局当做一个共用的,减少重复代码。
##以下是共用的部分
def update_layout(fig,type_,year):
fig.update_layout(
margin=dict(t=50,r=0,l=0,b=0),
title={'text': f'Sales by {type_} {year}',
'y': 0.99,
'x': 0.5,
'xanchor': 'center',
'yanchor': 'top'}
)
return fig
此时该文件中的代码简化如下。这样做的好处在于该项目中5个图都用到了这个样式更新,因此可以直接封装一个简单的函数,后续根据需要修改完善参数,进行更详细的布局样式的更改,这样会简化很多的重复代码,看上去十分清爽。
from dash import html,dcc,Input,Output
import dash_bootstrap_components as dbc
from app import app
from api import get_data
import plotly.express as px
def MiddleInfo():
return dbc.Row(
[
dbc.Col([Sub_Category_or_Region()]),
dbc.Col([CategoryPie()]),
dbc.Col([]),
dbc.Col([])
]
)
##以下是共用的部分
def update_layout(fig,type_,year):
fig.update_layout(
margin=dict(t=50,r=0,l=0,b=0),
title={'text': f'Sales by {type_} {year}',
'y': 0.99,
'x': 0.5,
'xanchor': 'center',
'yanchor': 'top'}
)
return fig
##以下是第一个图形中的内容设置
def Sub_Category_or_Region():
return html.Div(
[
dcc.RadioItems(
['Sub-Category','Region'],
'Sub-Category',
id = 'radio-sub-category-or-region'
),
dcc.Graph(id='h-bar-sub-category-or-region')
]
)
@app.callback(
Output('h-bar-sub-category-or-region','figure'),
[Input('slider-year','value'),Input('radio-segment','value'),Input('radio-sub-category-or-region','value')]
)
def update(year,segment,sub_category_or_region):
df = get_data(year,segment)
sales_data_series = df.groupby(sub_category_or_region)['Sales'].sum().sort_values(ascending=False)[:5][::-1]
fig = px.bar(x=sales_data_series.values,y=sales_data_series.index,orientation='h')
return update_layout(fig,sub_category_or_region,year)
##以下是第二个图形内容设置
def CategoryPie():
return dcc.Graph(id='pie-category')
@app.callback(
Output('pie-category','figure'),
[Input('slider-year','value'),Input('radio-segment','value')]
)
def update(year,segment):
df = get_data(year,segment)
type_ = 'Category'
sales_data_series = df.groupby(type_)['Sales'].sum()
fig=px.pie(values=sales_data_series.values,names=sales_data_series.index)
return update_layout(fig,type_,year)
3.5.3 中间第三部分图形绘制
该图形是按月份进行年份销售数据的统计。首先需要筛选年份的个月销售额,即按照月份进行分组合并求和即可。定义一个MonthLine()
函数用于创建容器只需要指定一个id即可,然后放在布局的第三个Col()
中,此部分的代码如下。
##以下是第三个图形的内容设置
def MonthLine():
return dcc.Graph(id='line-month')
@app.callback(
Output('line-month','figure'),
[Input('slider-year','value'),Input('radio-segment','value')]
)
def update(year,segment):
df = get_data(year,segment)
type_='month'
sales_data_series = df.groupby(type_)['Sales'].sum()
print(sales_data_series)
fig = px.line(x=sales_data_series.index,y=sales_data_series.values)
return update_layout(fig,type_,year)
保存文件后,刷新网址,输出内容如下。
3.5.4 中间第四部分信息卡片制作
该部分是按照年份统计年份的销售额和相对于去年的增降幅,只和头部的年份筛选有关。定义一个YOY()
函数用于创建容器只需要指定一个id即可,然后放在布局的第四个Col()
中,此部分的代码如下。
##以下是第四部分的内容设置
def YOY():
return html.Div(id='YOY')
@app.callback(
Output('YOY','children'),
[Input('slider-year','value')]
)
def update(year):
current_year_sales=data[data['year']==year]['Sales'].sum()
if year != min(year_ls):
previous_year_sales=data[data['year']==year-1]['Sales'].sum()
yoy_growth=(current_year_sales-previous_year_sales)*100/previous_year_sales
else:
previous_year_sales='No data'
yoy_growth='No data'
return [
html.H6('Current Year'),
html.P(f'${current_year_sales:,.0f}'),
html.H6('Previous Year'),
html.P(f'${previous_year_sales:,.0f}'),
html.H6('YOY Grouth'),
html.P(f'{yoy_growth:.2f}%'),
]
保存文件后,刷新网址,输出内容如下。
此时middle.py文件中的内容初步设置完毕,文件中的全部代码如下。
from dash import html,dcc,Input,Output
import dash_bootstrap_components as dbc
from app import app
from api import get_data,year_ls,data
import plotly.express as px
def MiddleInfo():
return dbc.Row(
[
dbc.Col([Sub_Category_or_Region()]),
dbc.Col([CategoryPie()]),
dbc.Col([MonthLine()]),
dbc.Col([YOY()])
]
)
##以下是共用的部分
def update_layout(fig,type_,year):
fig.update_layout(
margin=dict(t=50,r=0,l=0,b=0),
title={'text': f'Sales by {type_} {year}',
'y': 0.99,
'x': 0.5,
'xanchor': 'center',
'yanchor': 'top'}
)
return fig
##以下是第一个图形中的内容设置
def Sub_Category_or_Region():
return html.Div(
[
dcc.RadioItems(
['Sub-Category','Region'],
'Sub-Category',
id = 'radio-sub-category-or-region'
),
dcc.Graph(id='h-bar-sub-category-or-region')
]
)
@app.callback(
Output('h-bar-sub-category-or-region','figure'),
[Input('slider-year','value'),Input('radio-segment','value'),Input('radio-sub-category-or-region','value')]
)
def update(year,segment,sub_category_or_region):
df = get_data(year,segment)
sales_data_series = df.groupby(sub_category_or_region)['Sales'].sum().sort_values(ascending=False)[:5][::-1]
fig = px.bar(x=sales_data_series.values,y=sales_data_series.index,orientation='h')
return update_layout(fig,sub_category_or_region,year)
##以下是第二个图形内容设置
def CategoryPie():
return dcc.Graph(id='pie-category')
@app.callback(
Output('pie-category','figure'),
[Input('slider-year','value'),Input('radio-segment','value')]
)
def update(year,segment):
df = get_data(year,segment)
type_ = 'Category'
sales_data_series = df.groupby(type_)['Sales'].sum()
fig=px.pie(values=sales_data_series.values,names=sales_data_series.index)
return update_layout(fig,type_,year)
##以下是第三个图形的内容设置
def MonthLine():
return dcc.Graph(id='line-month')
@app.callback(
Output('line-month','figure'),
[Input('slider-year','value'),Input('radio-segment','value')]
)
def update(year,segment):
df = get_data(year,segment)
type_='month'
sales_data_series = df.groupby(type_)['Sales'].sum()
print(sales_data_series)
fig = px.line(x=sales_data_series.index,y=sales_data_series.values)
return update_layout(fig,type_,year)
##以下是第四部分的内容设置
def YOY():
return html.Div(id='YOY')
@app.callback(
Output('YOY','children'),
[Input('slider-year','value')]
)
def update(year):
current_year_sales=data[data['year']==year]['Sales'].sum()
if year != min(year_ls):
previous_year_sales=data[data['year']==year-1]['Sales'].sum()
yoy_growth=(current_year_sales-previous_year_sales)*100/previous_year_sales
else:
previous_year_sales='No data'
yoy_growth='No data'
return [
html.H6('Current Year'),
html.P(f'${current_year_sales:,.0f}'),
html.H6('Previous Year'),
html.P(f'${previous_year_sales:,.0f}'),
html.H6('YOY Grouth'),
html.P(f'{yoy_growth:.2f}%'),
]
3.6 bottom.py文件完善
3.6.1 底部第一个部分表格制作
该部分是按照头部筛选条件获取的数据,最终以表格的形式展现出来。定义一个SaleTable()
函数用于创建容器只需要指定一个id即可,然后放在布局的第一个Col()
中,此部分的代码如下。
#以下是第一部分的内容设置
def SaleTable():
return dash_table.DataTable(id='table-sale',page_size=15)
@app.callback(
Output('table-sale','data'),
[Input('slider-year','value'),Input('radio-segment','value')]
)
def update(year,segment):
df = get_data(year,segment)
filter_df = df[['Order Date', 'Customer ID', 'Customer Name','Segment', 'City', 'State', 'Region','Category', 'Sub-Category', 'Product Name','Sales', 'year', 'month']]
return filter_df.to_dict('records')
保存文件后,刷新网址,输出内容如下。
3.6.2 底部第二部分图形绘制
该部分是按照州State还是城市City进行年销售数据的统计,此部分和中间第一部分图形的绘制类似,代码可以直接复制粘贴过来后小改一下。定义一个State_or_City()
函数用于创建容器,里面第一需要是构建一个RadioItems组件,再创建一个放置图形的容器设置id,然后放在布局的第二个Col()
中,此部分的代码如下。
from middle import update_layout
#以下是第二部分的内容设置
def State_or_City():
return html.Div(
[
dcc.RadioItems(['State','City'],'State',id='radio-state-or-city'),
dcc.Graph(id='bar-state-or-city')
]
)
@app.callback(
Output('bar-state-or-city','figure'),
[Input('slider-year','value'),Input('radio-segment','value'),Input('radio-state-or-city','value')]
)
def update(year,segment,state_or_city):
df = get_data(year,segment)
sales_data_series = df.groupby(state_or_city)['Sales'].sum().sort_values(ascending=False)[:5][::-1]
fig = px.bar(x=sales_data_series.values,y=sales_data_series.index,orientation='h')
return update_layout(fig,state_or_city,year)
保存文件后,刷新网址,输出内容如下。(直接把中间第一部分的代码复制过来,只修改几个名称就可复用)
此时下方的表格直接横跨了整个页面,后续再进行样式调整,目前只是功能实现。
3.6.3 底部第三部分图形绘制
该部分是按照月份进行州和城市年份销售数据的统计,最终结果以散点图的形式呈现。定义一个SaleTable()
函数用于创建容器只需要指定一个id即可,然后放在布局的第三个Col()
中,此部分的代码如下。此部分的State and City不是指两者字段相加,而是点击图形中的三点会同时显示当前月份下的State和City对应销售数据,需要使用到hover相关的参数。
#以下是第三部分的内容设置
def State_and_City():
return dcc.Graph(id='scatter-state-and-city')
@app.callback(
Output('scatter-state-and-city','figure'),
[Input('slider-year','value'),Input('radio-segment','value')]
)
def update(year,segment):
df = get_data(year,segment)
obtain_fields = ['year', 'month', 'Segment', 'State', 'City']
sales_data_df = df.groupby(obtain_fields)['Sales'].sum().reset_index()
fig = px.scatter(sales_data_df,x=sales_data_df['month'],y=sales_data_df['Sales'],hover_data=obtain_fields)
return update_layout(fig,'State and City',year)
保存文件后,刷新网址,输出内容如下。使用hover_data
需要前面先传入sales_data_df
(也就是与其对应的df)。
至此整个项目的全部功能都基本上完成,后续就是针对样式进行修改。
4 样式修改
4.1 整体样式修改
在assets文件夹下新建一个setting.css文件,设置整体风格。主要是背景、边缘和字体颜色的设置。
此时保存文件,刷新网页输出内容如下。由于表格中的字体设置成为白色后,和绘制图形的背景一致,所以导致看不见文字。
4.2 header.py文件样式修改
文字已经居中了,就只需要调整一下头部信息与中间信息和浏览器顶部的间距即可,修改样式内容如下。
此时保存文件,刷新网页输出内容如下。间距已经调整适合。
4.3 middle.py文件样式修改
首先需要设置card_container属性,把之前的代码直接复制过来,添加到对应组件中。属性中的背景颜色的设置课根据项目主题颜色进行修改,上一个项目是暗色调,这个项目是蓝色调。
此时保存文件,刷新网页输出内容如下。
然后针对于各个图形进行单独的样式设置,全部的代码如下。
from dash import html,dcc,Input,Output
import dash_bootstrap_components as dbc
from app import app
from api import get_data,year_ls,data
import plotly.express as px
import plotly.graph_objects as go
def MiddleInfo():
return dbc.Row(
[
dbc.Col([Sub_Category_or_Region()],className='card_container',width=3),
dbc.Col([CategoryPie()],className='card_container',width=3),
dbc.Col([MonthLine()],className='card_container',width=4),
dbc.Col([YOY()],className='card_container')
]
)
##以下是共用的部分
def update_layout(fig,type_,year):
fig.update_layout(
paper_bgcolor='#1f2c56',
plot_bgcolor='#1f2c56',
titlefont={'color': 'white',
'size': 15},
font=dict(family='sans-serif',
color='white',
size=15),
legend={'orientation': 'h',
'bgcolor': '#010915',
'xanchor': 'center', 'x': 0.5, 'y': -0.2},
margin=dict(t=50,r=20,l=0,b=0),
title={'text': f'Sales by {type_} {year}',
'y': 0.99,
'x': 0.5,
'xanchor': 'center',
'yanchor': 'top'},
xaxis=dict(title='<b></b>',
color = 'orange',
showline=True,
showgrid=False,
showticklabels=True,
linecolor='orange',
linewidth=1,
zeroline=False,
ticks='outside',
tickfont=dict(
family='Aerial',
color='orange',
size=12
)),
yaxis=dict(title='<b></b>',
color='orange',
autorange = 'reversed',
showline=True,
showgrid=False,
showticklabels=True,
linecolor='orange',
linewidth=1,
zeroline=False,
gridcolor='gray',
ticks='outside',
tickfont=dict(
family='Aerial',
color='orange',
size=12
)
)
)
return fig
##以下是第一个图形中的内容设置
def Sub_Category_or_Region():
return html.Div(
[
dcc.RadioItems(
['Sub-Category','Region'],
'Sub-Category',
id = 'radio-sub-category-or-region'
),
dcc.Graph(id='h-bar-sub-category-or-region')
]
)
@app.callback(
Output('h-bar-sub-category-or-region','figure'),
[Input('slider-year','value'),Input('radio-segment','value'),Input('radio-sub-category-or-region','value')]
)
def update(year,segment,sub_category_or_region):
df = get_data(year,segment)
sales_data_series = df.groupby(sub_category_or_region)['Sales'].sum().sort_values(ascending=False)[:5][::-1]
fig = px.bar(x=sales_data_series.values,y=sales_data_series.index,orientation='h',text=sales_data_series)
fig.update_layout(height=400)
fig.update_traces(texttemplate='$' + '%{text:,.2s}', textposition='inside')
return update_layout(fig,sub_category_or_region,year)
##以下是第二个图形内容设置
def CategoryPie():
return dcc.Graph(id='pie-category')
@app.callback(
Output('pie-category','figure'),
[Input('slider-year','value'),Input('radio-segment','value')]
)
def update(year,segment):
df = get_data(year,segment)
type_ = 'Category'
sales_data_series = df.groupby(type_)['Sales'].sum()
fig=px.pie(values=sales_data_series.values,names=sales_data_series.index,hole=.7)
# fig.update_layout(height=400)
return update_layout(fig,type_,year)
##以下是第三个图形的内容设置
def MonthLine():
return dcc.Graph(id='line-month')
@app.callback(
Output('line-month','figure'),
[Input('slider-year','value'),Input('radio-segment','value')]
)
def update(year,segment):
df = get_data(year,segment)
type_='month'
sales_data_series = df.groupby(type_)['Sales'].sum()
fig = go.Figure(go.Scatter(
x=sales_data_series.index,
y=sales_data_series.values,
text = sales_data_series.values,
texttemplate= '$' + '%{text:,.2s}',
textposition='bottom left',
mode='markers+lines+text',
line=dict(width=3, color='orange'),
marker=dict(color='#19AAE1', size=10, symbol='circle',
line=dict(color='#19AAE1', width=2))
))
return update_layout(fig,type_,year)
##以下是第四部分的内容设置
def YOY():
return html.Div(id='YOY')
@app.callback(
Output('YOY','children'),
[Input('slider-year','value')]
)
def update(year):
current_year_sales=data[data['year']==year]['Sales'].sum()
if year != min(year_ls):
previous_year_sales=data[data['year']==year-1]['Sales'].sum()
yoy_growth=(current_year_sales-previous_year_sales)*100/previous_year_sales
else:
previous_year_sales='No data'
yoy_growth='No data'
return [
html.H6('Current Year'),
html.P(f'${current_year_sales:,.0f}',className='year_sale'),
html.H6('Previous Year'),
html.P(f'${previous_year_sales:,.0f}',className='year_sale'),
html.H6('YOY Grouth'),
html.P(f'{yoy_growth:.2f}%',className='year_sale'),
]
保存文件,刷新网页输出内容如下。
4.4 bottom.py文件样式修改
该部分主要难点在于调整表格的大小问题,还有表格中的标题筛选。表格正常填充在卡片容器中需要指定参数virtualization=True
。然后再配合style_cell
和style_header
参数进行单元格和标题样式的设置。借助fixed_rows={'headers': True}
设置冻结首行,然后设置排序,并且允许多字段排序。
#以下是第一部分的内容设置
def SaleTable():
return dash_table.DataTable(id='table-sale',
page_size=15,
virtualization=True,
style_cell={'textAlign': 'left',
'min-width': '100px',
'backgroundColor': '#1f2c56',
'color': '#FEFEFE',
'border-bottom': '0.01rem solid #19AAE1'},
style_header={'backgroundColor': '#1f2c56',
'fontWeight': 'bold',
'font': 'Lato, sans-serif',
'color': 'orange',
'border': '#1f2c56'},
fixed_rows={'headers': True},
sort_action='native',
sort_mode='multi'
)
@app.callback(
Output('table-sale','data'),
[Input('slider-year','value'),Input('radio-segment','value')]
)
def update(year,segment):
df = get_data(year,segment)
filter_df = df[['Order Date', 'Customer ID', 'Customer Name','Segment', 'City', 'State', 'Region','Category', 'Sub-Category', 'Product Name','Sales', 'year', 'month']]
return filter_df.to_dict('records')
保存文件,刷新网页输出内容如下。
顺带也可以把表格的下部和右部的下拉组件设置一下样式。setting.css文件中修改样式如下。
/* width */
::-webkit-scrollbar {
width: 10px !important;
display: block !important;
}
/* Track */
::-webkit-scrollbar-track {
background: #222b4a !important;
border-radius: 10px !important;
display: block !important;
}
/* Handle */
::-webkit-scrollbar-thumb {
background:#1a2034 !important;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: #1a2034 !important;
}
.previous-next-container *{
background: #1f2c56 !important;
color: white !important;
}
.current-page{
background: #4c5575 !important;
color: white !important;
}
保存文件,刷新网页输出内容如下。
最后两个图形的修改。其中底部第二部分的内容其实已经随着中间第一部分内容样式修改而发生变化了,只需要添加一个文字标注就可以了,然后最后一个图发现这折线图的y轴大小顺序翻过来了,需要调整一下(把之前middle.py文件中设置的autorange = 'reversed'
设置删除即可),最后设置一下散点的大小和颜色,两部分的代码修改如下。和前面的折线图设置类似,发现直接使用px下的绘制函数,没有办法详细地设置参数,因此又改变方式使用原生的go方式绘制图形。
#以下是第二部分的内容设置
def State_or_City():
return html.Div(
[
dcc.RadioItems(['State','City'],'State',id='radio-state-or-city'),
dcc.Graph(id='bar-state-or-city')
]
)
@app.callback(
Output('bar-state-or-city','figure'),
[Input('slider-year','value'),Input('radio-segment','value'),Input('radio-state-or-city','value')]
)
def update(year,segment,state_or_city):
df = get_data(year,segment)
sales_data_series = df.groupby(state_or_city)['Sales'].sum().sort_values(ascending=False)[:5][::-1]
fig = px.bar(x=sales_data_series.values,y=sales_data_series.index,orientation='h',text=sales_data_series.values)
fig.update_traces(texttemplate='$' + '%{text:,.2s}', textposition='inside')
fig.update_layout(height=520)
return update_layout(fig,state_or_city,year)
#以下是第三部分的内容设置
def State_and_City():
return dcc.Graph(id='scatter-state-and-city')
@app.callback(
Output('scatter-state-and-city','figure'),
[Input('slider-year','value'),Input('radio-segment','value')]
)
def update(year,segment):
df = get_data(year,segment)
obtain_fields = ['year', 'month', 'Segment', 'State', 'City']
sales_data_df = df.groupby(obtain_fields)['Sales'].sum().reset_index()
fig = go.Figure(
go.Scatter(
x=sales_data_df['month'],
y=sales_data_df['Sales'],
mode='markers',
line=dict(width=3, color='orange'),
marker=dict(color= sales_data_df['Sales'],
colorscale = 'HSV',
showscale = False,
size=sales_data_df['Sales'] / 250,
symbol='circle',
line=dict(color='MediumPurple', width=2)),
hoverinfo='text',
hovertext=
'<b>Year</b>: ' + sales_data_df['year'].astype(str) + '<br>' +
'<b>Month</b>: ' + sales_data_df['month'].astype(str) + '<br>' +
'<b>Segment</b>: ' + sales_data_df['Segment'].astype(str) + '<br>' +
'<b>State</b>: ' + sales_data_df['State'].astype(str) + '<br>' +
'<b>City</b>: ' + sales_data_df['City'].astype(str) + '<br>' +
'<b>Sales</b>: $' + [f'{x:,.0f}' for x in sales_data_df['Sales']] + '<br>'
)
)
fig.update_layout(height=550)
return update_layout(fig,'State and City',year)
保存文件,刷新网页输出内容如下。
至此,项目完结,撒花✿✿ヽ(°▽°)ノ✿。