器→工具, 开源项目, 数据, 术→技巧

DASH使用简明教程

钱魏Way · · 4 次浏览

在先前的介绍过快速数据可视化界面工具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': ['Value 1', 'Value 2', 'Value 3'],
    'Column 2': ['Value 4', 'Value 5', 'Value 6']
})

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的官方文档

Dash AG Grid

Dash AG Grid是一个基于AG Grid的Dash组件。AG Grid是一个功能强大的JavaScript数据网格,支持多种框架。它提供了丰富的功能,如排序、过滤、分组、树形视图、行拖拽、列调整等。

Dash AG Grid组件使得AG Grid能够集成到Dash应用中,从而提供更高级的数据表格功能。

以下是一个Dash AG Grid的基本示例:

import dash
import dash_ag_grid
import pandas as pd

# 创建一个简单的DataFrame
df = pd.DataFrame({
    'Column 1': ['Value 1', 'Value 2', 'Value 3'],
    'Column 2': ['Value 4', 'Value 5', 'Value 6']
})

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属性分别开启了列调整、排序和过滤功能。

此外,Dash AG Grid还支持更多AG Grid的高级功能,如:

  • 复杂的单元格渲染:你可以为cellRenderer属性指定一个JavaScript函数,用于自定义单元格的渲染方式。
  • 行和列的拖拽:你可以设置rowDragManaged、suppressMovableColumns等属性,以开启行拖拽和列调整功能。
  • 分组和树形视图:你可以设置groupUseEntireRow、treeData等属性,以开启分组和树形视图功能。

更多用法和详细信息,请参考Dash AG Grid的官方文档

Dash Leaflet

Dash Leaflet是一个为Dash提供的轻量级地图插件,它是基于JavaScript库Leaflet.js的Python封装。Leaflet是一个开源的JavaScript库,用于构建移动友好的交互式地图。类似Folium

以下是一个基本的Dash Leaflet示例:

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。

Dash Leaflet还提供了许多其他组件,可以实现更多的地图功能,如:

  • Marker:在地图上添加标记。
  • Popup:添加一个可以显示额外信息的弹窗,通常与dl.Marker一起使用。
  • Polyline、dl.Polygon、dl.Rectangle:在地图上绘制线、多边形和矩形。
  • Circle、dl.CircleMarker:在地图上绘制圆和圆形标记。
  • LayerGroup、dl.FeatureGroup:用于组合多个层。

此外,Dash Leaflet还支持GeoJSON格式的数据,并提供了dl.GeoJSON组件来显示这种数据。你可以使用dl.Choropleth组件来创建填充区域地图。

更多用法和详细信息,请参考Dash Leaflet的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&param2=value2,pathname将是/page-1,search将是?param1=value1&param2=value2,params将是{‘param1’: [‘value1’], ‘param2’: [‘value2’]}。

这样,你就可以根据查询参数来改变页面的内容。比如,你可以根据参数来显示不同的数据、应用不同的筛选条件等等。

注意,查询参数字符串的开头是一个?,在解析它之前需要先去掉。另外,urllib.parse.parse_qs函数会将每个参数的值解析成列表,如果你知道参数只有一个值,可以使用列表的[0]索引来获取这个值。

Dash部署到JupyterLab

安装必要的包:

pip install dash
pip install jupyter-dash

重建 jupyter lab环境:jupyter lab 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学习资源和参考网站:

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注