在先前的介绍过快速数据可视化界面工具Streamlit,Dash是一个与之非常类似的工具,个人在使用Streamlit加载地图呈现时遇到响应非常慢的的问题,于是使用了Dash整理使用起来也非常的简单,这里做下简要的分享。

DASH简介
Dash是一个开源的Python框架,用于创建基于Web的应用程序。它由Plotly公司开发,专为数据科学家和分析家设计,以便他们可以构建自定义的数据可视化Web应用程序,而无需知道太多的前端开发知识。

以下是Dash的一些主要特性:
- 交互性:Dash提供了丰富的交互式组件库,如图表、表格、滑块等,可以根据用户的操作动态更新。
- Pure Python:Dash应用程序完全使用Python编写,无需JavaScript、HTML或CSS的知识。虽然对前端知识的了解可能有助于微调应用程序,但并非必需。
- 可扩展性:Dash使用js进行图形渲染,支持高度定制化和交互式图表。还可以扩展现有的React.js库以创建自定义组件。
- 部署简单:Dash应用程序可以轻松部署到服务器或云平台,方便与他人共享和协作。
- 企业级应用:Plotly提供了一个名为Dash Enterprise的商业产品,它增加了许多功能,如数据缓存、分析、身份验证和更多。
DASH的使用
Dash的基本结构
Dash应用程序的基本结构主要由两个部分组成:布局(Layout)和交互(Callbacks)。
布局(Layout)
布局定义了应用程序的基本结构,可以看作是一个HTML模板。Dash使用Python类表示HTML标签。例如,html.Div对应HTML的<div>标签,html.H1对应HTML的<h1>标签等等。这些类的实例通过嵌套的Python列表来组织,形成树形的结构。
Dash提供了两种类型的组件:
- dash_html_components:这个库提供了一套纯粹基于HTML的组件集,如Div,Label,P(段落)等等,用于创建HTML内容。
- dash_core_components:这个库提供了一套基于js的丰富的交互式组件,如Dropdown,Slider,Graph等等,用于创建具有交互性的应用程序。
一个简单的布局示例如下:
app.layout = html.Div([
html.H1('Hello Dash'),
html.Div([
html.P('Dash converts Python classes into HTML'),
html.P('This conversion happens behind the scenes by Dashs JavaScript front-end')
])
])
以下是一个使用dash_core_components的布局示例:
app.layout = html.Div([ dcc.Graph(id='my-graph'), dcc.Slider(id='my-slider', min=0, max=10, step=0.5, value=5) ])
这将生成一个包含图形和滑块的<div>标签。
总的来说,理解布局和Dash组件是创建Dash应用程序的关键。通过嵌套和组合这些组件,我们可以创建出各种复杂的应用程序界面。
交互(Callbacks)
在Dash中,交互是通过“回调”机制实现的。回调函数定义了应用程序如何响应用户的操作,如点击按钮,拖动滑块等。
每个回调函数主要由三部分组成:输入(Input)、输出(Output)和状态(State)。
- 输入(Input):这是触发回调函数的事件源,如按钮的点击、滑块的拖动等。一个回调函数可以有多个输入。
- 输出(Output):这是回调函数的结果要显示的地方,通常是某个组件的属性。一个回调函数可以有多个输出。
- 状态(State):这是回调函数的额外输入,但它的变化不会触发回调函数。只有当其他输入触发回调函数时,状态才会被读取。
这些输入、输出和状态都是通过dash.dependencies.Input,dash.dependencies.Output和dash.dependencies.State类来定义的。
以下是一个简单的回调函数示例:
@app.callback(
Output('my-graph', 'figure'),
[Input('my-slider', 'value')]
)
def update_graph(slider_value):
return {'data': [{'y': [i * slider_value for i in range(10)]}]}
这个回调函数将’my-slider’的’value’属性作为输入,将’my-graph’的’figure’属性作为输出。每当滑块的值改变时,都会触发这个回调函数,生成一个新的图形数据,并更新到图形组件中。
需要注意的是,Dash强制回调函数具有唯一的输出。这是为了避免多个回调函数更新同一个组件属性带来的冲突。如果需要更新多个输出,可以使用dash.callback_context来检查是哪个输入触发了回调函数。
由于回调函数是在服务器端执行的,因此在设计回调函数时需要注意性能问题。避免在回调函数中执行时间消耗大的操作,如复杂的计算或网络请求。如果必须这样做,可以考虑将结果缓存起来,以提高响应速度。
完整示例
下面是一个简单但完整的Dash应用程序示例。这个应用程序包含一个滑块和一个图形。当您移动滑块时,图形上的数据将会改变。
# 导入所需的库
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
# 创建Dash应用实例
app = dash.Dash(__name__)
# 定义布局
app.layout = html.Div([
dcc.Slider(
id='my-slider',
min=0,
max=10,
step=0.5,
value=5,
),
dcc.Graph(id='my-graph'),
])
# 定义回调
@app.callback(
Output('my-graph', 'figure'),
[Input('my-slider', 'value')]
)
def update_graph(slider_value):
return {
'data': [{
'x': list(range(10)),
'y': [i * slider_value for i in range(10)],
'type': 'bar'
}]
}
# 运行应用
if __name__ == '__main__':
app.run_server(debug=True)
你可以将这段代码保存为Python文件,然后运行它。你将看到一个网页应用程序,其中包含一个滑块和一个图形。当你移动滑块时,图形会动态更新。
这个示例非常简单,但它展示了Dash应用程序的基本结构和工作原理:定义布局,添加交互性,然后运行应用程序。你可以根据自己的需求,添加更多的组件和回调,创建更复杂的应用程序。
Dash复杂布局
Dash的布局方案主要有以下几种:
- 单列布局:这是最基本的布局形式,所有的内容都按照顺序排列在一列中。
- 多列布局:你可以使用dash_html_components的Div组件和CSS来创建多列布局。
- 选项卡布局:你可以使用dash_core_components的Tabs和Tab组件来创建选项卡布局。
- 侧边栏布局:你可以使用dash_bootstrap_components的Sidebar组件来创建侧边栏布局。
- 网格布局:你可以使用dash_bootstrap_components的Container, Row和Col组件来创建基于网格系统的响应式布局。
注意,为了创建复杂的布局,你可能需要学习一些CSS知识。你可以使用内联样式,也可以创建一个外部的CSS文件,并通过app = dash.Dash(__name__, external_stylesheets=[…])来引用它。
你也可以使用预构建的CSS框架,如Bootstrap,来简化布局和样式的编写。
Dash可以很方便地与Bootstrap一起使用,以便利用Bootstrap的响应式网格系统和预定义样式。这可以通过一个第三方库dash-bootstrap-components来实现。
首先,你需要安装dash-bootstrap-components库,运行如下命令:
pip install dash-bootstrap-components
然后,你需要在你的Dash应用中引入Bootstrap的CSS。你可以使用Bootstrap的CDN,也可以下载Bootstrap的CSS文件并放在你的应用目录中。以下是一个使用CDN的例子:
import dash import dash_bootstrap_components as dbc app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
现在,你可以在你的应用中使用dash-bootstrap-components提供的组件了。这些组件大部分与Bootstrap的类名相同,比如dbc.Row、dbc.Col、dbc.Button等等。
以下是一个使用Bootstrap网格系统的例子:
import dash
import dash_bootstrap_components as dbc
import dash_html_components as html
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = dbc.Container([
dbc.Row([
dbc.Col(html.Div("This is column 1"), width=4),
dbc.Col(html.Div("This is column 2"), width=8),
])
])
if __name__ == '__main__':
app.run_server(debug=True)
在这个例子中,我们创建了一个包含两列的行,第一列占据了总宽度的4份,第二列占据了总宽度的8份。Bootstrap的网格系统总宽度为12,你可以根据需要分配每列的宽度。
更多关于dash-bootstrap-components的信息,你可以查阅其官方文档。
Dash的数据表格组件
Dash的数据表格组件(dash_table.DataTable)是一个灵活且强大的工具,能够呈现、编辑和操作数据。以下是如何创建和使用DataTable的基本步骤:
首先,你需要在代码中导入dash_table模块,然后创建一个DataTable实例,并将其添加到你的布局中。例如:
import dash
import dash_table
import pandas as pd
# 创建一个简单的DataFrame
df = pd.DataFrame({
'Column 1': ['Value1', 'Value2', 'Value3'],
'Column 2': ['Value4', 'Value5', 'Value6']
})
app = dash.Dash(__name__)
app.layout = html.Div([
dash_table.DataTable(
id='table',
columns=[{"name": i, "id": i} for i in df.columns],
data=df.to_dict('records'),
)
])
if __name__ == '__main__':
app.run_server(debug=True)
在这个例子中,DataTable的columns属性接受一个字典列表,其中每个字典表示一列,name键是列的标题,id键是列的标识符。data属性接受一个字典列表,其中每个字典代表一行数据。
此外,DataTable还提供了许多其他属性,可以用来自定义表格的外观和行为,如:
- filter_action:设置为’native’,可以启用内建的过滤功能。
- sort_action:设置为’native’,可以启用内建的排序功能。
- page_action:设置为’native’,可以启用内建的分页功能。
- style_data_conditional:这是一个字典列表,可以包含一些条件和样式,用来根据单元格的值改变其样式。
- editable:设置为True,可以让用户直接在表格中编辑数据。
这只是DataTable的一部分功能,更多高级功能和详细用法可以参考Dash的官方文档。
DashAGGrid
DashAGGrid是一个基于AGGrid的Dash组件。AGGrid是一个功能强大的JavaScript数据网格,支持多种框架。它提供了丰富的功能,如排序、过滤、分组、树形视图、行拖拽、列调整等。
DashAGGrid组件使得AGGrid能够集成到Dash应用中,从而提供更高级的数据表格功能。
以下是一个DashAGGrid的基本示例:
import dash
import dash_ag_grid
import pandas as pd
# 创建一个简单的DataFrame
df = pd.DataFrame({
'Column 1': ['Value1', 'Value2', 'Value3'],
'Column 2': ['Value4', 'Value5', 'Value6']
})
app = dash.Dash(__name__)
app.layout = dash_ag_grid.AgGrid(
data=df.to_dict('records'),
columnDefs=[{'headerName': i, 'field': i} for i in df.columns],
enableColResize=True,
enableSorting=True,
enableFilter=True,
)
if __name__ == '__main__':
app.run_server(debug=True)
在这个例子中,AgGrid组件的 data 属性接收一个字典列表,每个字典代表一行数据;columnDefs 属性接收一个字典列表,每个字典代表一列,headerName 是列的标题,field 是列的标识符。enableColResize、enableSorting 和 enableFilter 属性分别开启了列调整、排序和过滤功能。
此外,DashAGGrid 还支持更多 AGGrid 的高级功能,如:
- 复杂的单元格渲染:你可以为 cellRenderer 属性指定一个 JavaScript 函数,用于自定义单元格的渲染方式。
- 行和列的拖拽:你可以设置 rowDragManaged、suppressMovableColumns 等属性,以开启行拖拽和列调整功能。
- 分组和树形视图:你可以设置 groupUseEntireRow、treeData 等属性,以开启分组和树形视图功能。
更多用法和详细信息,请参考 DashAGGrid 的官方文档。
DashLeaflet
DashLeaflet是一个为 Dash 提供的轻量级地图插件,它是基于 JavaScript 库 Leaflet.js 的 Python 封装。Leaflet 是一个开源的 JavaScript 库,用于构建移动友好的交互式地图。类似Folium。
以下是一个基本的 DashLeaflet 示例:
import dash
import dash_html_components as html
import dash_leaflet as dl
app = dash.Dash(__name__)
app.layout = html.Div([
dl.Map([
dl.TileLayer()
], style={'width': '1000px', 'height': '500px'})
], style={'display': 'flex', 'justify-content': 'center', 'align-items': 'center', 'height': '100vh'})
if __name__ == '__main__':
app.run_server(debug=True)
在这个例子中,dl.Map 组件创建了一个地图,dl.TileLayer 组件添加了一个基础的切片图层,图层的来源默认是 OpenStreetMap。
DashLeaflet 还提供了许多其他组件,可以实现更多的地图功能,如:
- Marker:在地图上添加标记。
- Popup:添加一个可以显示额外信息的弹窗,通常与 dl.Marker 一起使用。
- Polyline、dl.Polygon、dl.Rectangle:在地图上绘制线、多边形和矩形。
- Circle、dl.CircleMarker:在地图上绘制圆和圆形标记。
- LayerGroup、dl.FeatureGroup:用于组合多个层。
此外,DashLeaflet 还支持 GeoJSON 格式的数据,并提供了 dl.GeoJSON 组件来显示这种数据。你可以使用 dl.Choropleth 组件来创建填充区域地图。
更多用法和详细信息,请参考 DashLeaflet 的GitHub 页面。
多页面应用
Dash 能够让你创建多页面应用。在 Dash 中,你可以使用 dash_core_components.Location 组件和 dash_core_components.Link 组件来创建多页面应用。
以下是一个简单的多页面应用示例:
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
app = dash.Dash(__name__, suppress_callback_exceptions=True)
app.layout = html.Div([
dcc.Location(id='url', refresh=False),
html.Div(id='page-content')
])
index_page = html.Div([
dcc.Link('Go to Page 1', href='/page-1'),
html.Br(),
dcc.Link('Go to Page 2', href='/page-2'),
])
page_1_layout = html.Div([
html.H1('Page 1'),
dcc.Link('Go back to home', href='/'),
])
page_2_layout = html.Div([
html.H1('Page 2'),
dcc.Link('Go back to home', href='/'),
])
@app.callback(Output('page-content', 'children'),
Input('url', 'pathname'))
def display_page(pathname):
if pathname == '/page-1':
return page_1_layout
elif pathname == '/page-2':
return page_2_layout
else:
return index_page
if __name__ == '__main__':
app.run_server(debug=True)
在这个例子中,dcc.Location 组件用于获取当前的 URL,dcc.Link 组件用于创建链接。通过监听 dcc.Location 组件的 pathname 属性,我们可以判断用户当前访问的是哪个页面,并返回相应的内容。注意,在 URL 中,根路径(/)对应的是首页。
此外,在构建多页面应用时,你可能需要将每个页面的布局和回调函数放在不同的模块中,以保持代码的清晰和可维护。为了使这些模块中的回调函数能够正常工作,你需要在创建 dash.Dash 实例时,将 suppress_callback_exceptions 参数设置为 True,以允许 Dash 在启动时不检查回调函数的输入和输出是否在初始布局中。
在 Dash 中,可以利用 dash_core_components.Location 组件来获取 URL 中的参数,这个组件的 search 属性会返回 URL 中的查询参数字符串。
以下是一个使用 URL 参数的示例:
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from urllib.parse import parse_qs
app = dash.Dash(__name__, suppress_callback_exceptions=True)
app.layout = html.Div([
dcc.Location(id='url', refresh=False),
html.Div(id='page-content')
])
@app.callback(Output('page-content', 'children'),
Input('url', 'pathname'),
Input('url', 'search'))
def display_page(pathname, search):
params = parse_qs(search[1:]) # 解析查询参数
page = html.Div([
html.H3('The pathname is "{}"'.format(pathname)),
html.H3('The search is "{}"'.format(search)),
html.H3('The parameters are "{}"'.format(params)),
])
return page
if __name__ == '__main__':
app.run_server(debug=True)
在这个例子中,如果你访问 http://localhost:8050/page-1?param1=value1¶m2=value2,pathname 将是 /page-1,search 将是 ?param1=value1¶m2=value2,params 将是 {‘param1’:[‘value1’], ‘param2’:[‘value2’]}。
这样,你就可以根据查询参数来改变页面的内容。比如,你可以根据参数来显示不同的数据、应用不同的筛选条件等等。
注意,查询参数字符串的开头是一个 ?,在解析它之前需要先去掉。另外,urllib.parse.parse_qs 函数会将每个参数的值解析成列表,如果你知道参数只有一个值,可以使用列表的 [0] 索引来获取这个值。
Dash 部署到 JupyterLab
安装必要的包:
pip install dash pip install jupyter-dash
重建 jupyterlab 环境:jupyterlab build
使用示例:
from dash import Dash, dcc, html
import plotly.express as px
import pandas as pd
from jupyter_dash import JupyterDash
# app = Dash(__name__)
app = JupyterDash(__name__)
df = pd.read_csv('gdp-life-exp-2007.csv')
fig = px.scatter(df, x="gdp per capita", y="life expectancy",
size="population", color="continent", hover_name="country",
log_x=True, size_max=60)
app.layout = html.Div([
dcc.Graph(
id='life-exp-vs-gdp',
figure=fig
)
])
if __name__ == '__main__':
# app.run_server(mode='jupyterlab')
app.run_server(mode='inline')
# app.run_server(mode='external')
以下是一些实用的 Dash 学习资源和参考网站:
- Dash 官方文档: 这是最权威、最全面的 Dash 资源,包括了介绍、教程、示例和 API 文档。
- GitHub – plotly/dash: 基于 React、Plotly.js 和 Flask 的分析 Web 应用框架: 你可以在这里查看源代码,了解 Dash 的最新更新,以及一些示例代码。
- Medium 上的 Dash 教程: 这里有许多由社区成员编写的教程和案例分析。
- Awesome Dash: 这是一个收集了很多 Dash 资源的列表,包括了库、工具、教程和示例。



