【Dash搭建可视化网站】项目13:销售数据可视化大屏制作步骤详解

news2024/11/24 17:54:58

销售数据可视化大屏制作步骤详解

  • 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()函数,里面有两个参数yearsegment,就是对应着头部信息中的两个筛选组件的标签,方便回调时快速获取数据。
在这里插入图片描述
把以上的代码转移到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_cellstyle_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)

保存文件,刷新网页输出内容如下。
在这里插入图片描述
至此,项目完结,撒花✿✿ヽ(°▽°)ノ✿。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/158039.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

24.数组名字取地址变成数组指针,数组名和指针变量的区别

数组名字取地址变成数组指针 1.一维数组名字取地址&#xff0c;变成一维数组指针&#xff0c;加1跳一个一维数组。 int a[10]; a1 跳一个整型元素&#xff0c;是a[1]的地址 a和a1相差一个元素&#xff0c;4个字节 &a 就变成了一个一位数组指针&#xff0c;是int(*p)[10]…

结构体重点知识大盘点

0、结构体基础知识 1、结构体是一些值的集合&#xff0c;这些值被称为成员&#xff0c;它们的类型是可以不同的。&#xff08;与数组相似&#xff0c;但数组元素的类型都是相同的&#xff09;。用来描述由基本数据类型组成的复杂类型。 2、结构体也有全局的和局部的。 3、st…

Hello World with VS 17.4.4 DOT NET MAUI Note

Hello World with VS 17.4.4 DOT NET MAUI Note kagula2023-1-12 Prologue If you touched XAML, well here is a concise guide for you running the first MAUI project. Content System Requirement 【1】Microsoft Windows [Version 10.0.19044.2486] Chinese Language …

Ubuntu Centos Linux End Kernel panic-not syncing:Attempted to kill init!

原问题&#xff1a; 当前系统为 Ubuntu 解决问题步骤&#xff1a; 1、重启电脑&#xff0c;在进入选择版本时&#xff0c;选择 系统高级选项&#xff0c; 我选的是【Ubuntu高级选项】 2、进入一个又很多系统版本的界面&#xff0c;每个版本有三个选项&#xff1a;常规启动版…

如何在 ASP.NET Core 2.X 项目中通过EF Core使用MySQL数据库

目录 一、安装MySql.Data.EntityFrameworkCore 二、创建EF Core上下文类以及相关数据模型类 三、配置连接字符串 四、在Starup.cs中注册数据库服务&#xff08;配置Context类的依赖注入&#xff09; 五、通过数据迁移命令生成数据库 目前EF Core已经支持了MySQL数据库了。…

从零开始带你实现一套自己的CI/CD(四)Jenkins Pipeline流水线

目录一、简介二、Groovy2.1 HelloWorld2.2 Pipeline script from SCM三、Jenkinsfile3.1 拉取代码3.2 代码质量检测3.3 构建代码3.4 制作镜像并发布镜像仓库3.5 部署到目标服务器3.6 完整的Jenkinsfile3.7 参数配置3.8 通过参数构建四、添加邮件通知4.1 配置Jenkins邮件配置4.2…

开源飞控初探(一):无人机的历史

这章先纠正大疆带给无人机外行小白的认知。定义无人机无人机的正式英文名字是Unmanned Aerial Vehicle&#xff0c;缩写为UAV。有人无人的区分&#xff0c;是看飞机能否一直需要人为操控。最简单的场景是&#xff0c;当飞机飞出视线之外时&#xff0c;人已经很难实时根据环境来…

微服务自动化管理【docker compose】

1.什么是docker-compose Docker-Compose项目是Docker官方的开源项目&#xff0c;负责实现对Docker容器集群的快速编排 通过编写docker-compose文件可对多个服务同时进行启动/停止/更新(可定义依赖&#xff0c;按顺序启动服务) docker-compose将所管理的容器分为3层结构&#…

Yii2下PHP远程调试PHP5.6/7.2与Xdebug2.5/2.7/3.0 在PHPSTORM下的差异化表现

学习起因&#xff1a;新人学YII2不知道远程调试(远程调试和控制台调试是两件事&#xff0c;同一个原理) 因为yii2框架&#xff0c;设计复杂度非常高&#xff0c;加上php代码的弱类型语言结构&#xff0c;在代码非常复杂的情况下&#xff0c;不采用调试的方式来看源码调用栈&am…

MPLS 虚拟专线 实验

目录 1.拓扑图 2.实验思路 3.主要配置 4.测试 5.实验总结 1.拓扑图 2.实验思路 IGP路由 MPLS Domain 配置MPLS VPN PE之间的MP-BGP邻居关系 CE端与PE端的路由交换 双向重发布&#xff0c;实现路由共享 3.主要配置 R6&#xff1a; *公网环境&#xff1a; [r6]ospf 1 r…

记录robosense RS-LIDAR-16使用过程2

一、安装并使用可视化工具RSView&#xff0c;官网提供了不同版本的安装包&#xff0c;根据个人环境下载解压。本人ubuntu18系统&#xff0c;修改权限&#xff1a;chmod ax run_rsview.sh;然后运行&#xff1a;./run_rsview.sh。该软件每次启动时都要运行./run_rsviewer.sh该软件…

Acwing 1214. 波动数列

题目链接&#xff1a;1214. 波动数列 - AcWing题库 标签&#xff1a;动态规划 &#xff08;字好丑...&#xff09; AC代码&#xff1a; #include<iostream> using namespace std;int f[1005][1005];const int MOD 100000007;//返回正余数 int get_mod(int a,int b) {…

不重复的随机数问题

前言 对于随机数的运用&#xff0c;在开发中经常会用到。有时需要生成不重复的定范围定总量的随机数&#xff0c;比如1~20&#xff0c;需要打乱的1~20中的10个数&#xff0c;那到底怎么做呢? 一、不重复的随机数 我们知道&#xff0c;直接用random会有重复的数字&#xff0…

电商物流云仓的原理是什么?

以云的速度和范围获得胜利  这是一个快速转型时期&#xff0c;封锁、就地避难订单和游览限制扰乱了美国经济的各个范畴&#xff0c;对供给链运营产生了严重影响。在如此动乱的时期&#xff0c;企业正越来越多地转向云优先战略&#xff0c;以使其供给链愈加矫捷和灵敏。  战…

【NI Multisim 14.0原理图文件管理——保存/备份文件及新建电路图页文件】

目录 序言 ⛄1.保存文件 ⛄2.备份文件 ⛄3.新建电路图页文件 序言 NI Multisim最突出的特点之一就是用户界面友好。它可以使电路设计者方便、快捷地使用虚拟元器件和仪器、仪表进行电路设计和仿真。 首先启动NI Multisim 14.0&#xff0c;打开如图所示的启动界面&#xff…

CTK Plugin Framework插件框架学习--事件监听

文章目录一、前言二、框架事件三、插件事件四、服务事件五、添加事件监听一、前言 CTK一共有三种事件可以监听&#xff1a; 框架事件插件事件服务事件 但是这些事件只有在变化时才能监听到&#xff0c;如果已经变化过后&#xff0c;进入一个稳定的状态&#xff0c;这时才去监…

归并排序详细说明及实现-python

算法思想&#xff1a; 设初始序列含有n个记录&#xff0c;则可看成n个有序的子序列&#xff0c;每个子序列长度为1 两两合并&#xff0c;得到&#xff08;n//2) 个长度为2&#xff08;n为奇数时&#xff0c;最后一个序列的长度为1&#xff09;的有序子序列 再两两合并&#xff…

【Flink系列】开发篇:1. Flink维表关联方案

数据流往往需要访问外部的数据源来丰富自己的信息&#xff0c;比如通过record中的ip地址查询ip数据库maxmind的GeoIP2 Databases得到ip对应的城市名称&#xff0c;城市经纬度&#xff0c;将这些作为新的字段添加到原来的record中。这就涉及到本篇的主题&#xff1a;维表关联。 …

分布式锁方案分析:看图说话(图+文)

1 缘起 曾经在看分布式锁的时候&#xff0c;还是处于了解阶段&#xff0c; 回头总结时&#xff0c;发现有很多细节没有探究到&#xff0c; 本文以-看图说话的方式分析不同的分布式锁方案&#xff0c; 分布式锁需要保证&#xff1a; &#xff08;1&#xff09;互斥性&#xff1…

【从零开始学习深度学习】46. 目标检测中锚框的概念、计算方法、样本锚框标注方式及如何选取预测边界框

本文主要介绍目标检测中常用到的锚框相关概念、计算方式、样本标注及如何选取预测边界框并输出的相关内容。 目录1. 锚框介绍1.1 生成多个锚框2. 交并比--Jaccard系数3. 标注训练集的锚框4. 输出预测边界框---非极大值抑制方法总结1. 锚框介绍 在目标检测算法中通常会在输入图…