有时回调会产生很大的开销,尤其是当它们:
接收和/或返回大量数据(传输时间)
被频繁调用(网络延迟、排队、握手)
是需要在浏览器和Dash之间进行多次往返的回调链的一部分
当回调的开销变得过大且无法进行其他优化时,可以修改回调以直接在浏览器中运行,而不是向Dash发出请求。
回调的语法几乎完全相同;声明回调时,通常使用Input和Output,但是还将JavaScript函数定义为@callback装饰器的第一个参数。
clientside_callback 是 Dash 的一个特性,它允许你在客户端(即浏览器)执行 JavaScript 函数,而不是在服务器端。这可以提高应用的性能,因为不需要与服务器进行通信。
在这个特定的 clientside_callback 中,函数接受两个参数:data 和 scale。data 参数是从 clientside-figure-store 组件中获取的,它包含了要在图中显示的数据。scale 参数是从 clientside-graph-scale 组件中获取的,它决定了 y 轴的类型(线性或对数)。
这个 JavaScript 函数返回一个对象,该对象包含两个属性:data 和 layout。data 属性直接对应于输入参数 data,而 layout 属性是一个对象,其 yaxis 属性设置为 {‘type’: scale}。这意味着 y 轴的类型(线性或对数)由 scale 参数决定。
然后,这个由 JavaScript 函数返回的对象被传递给 clientside-graph 组件的 figure 属性。这样,图就会根据 data 和 scale 的值进行更新。
总的来说,这段代码的作用是允许用户在客户端动态地改变图的数据和 y 轴的类型,而不需要与服务器进行通信。这可以提高应用的响应性和性能。
# 导入dash库中的必要组件,dash是一个用于构建交互式数据可视化web应用的Python框架
from dash import Dash, dcc, html, Input, Output, callback, clientside_callback
# 导入pandas库,用于数据处理和分析
import pandas as pd
# 导入json库,用于处理JSON数据
import json
# 设置外部样式表链接,用于美化web应用界面
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
# 创建一个Dash应用实例,并设置外部样式表
app = Dash(__name__, external_stylesheets=external_stylesheets)
# 从网络URL读取CSV数据,并存储到DataFrame对象df中
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv')
# 从DataFrame对象df中获取所有不重复的国家名称,并存储到available_countries变量中
available_countries = df['country'].unique()
# 设置Dash应用的布局
app.layout = html.Div([
# 创建一个图形组件,用于显示数据可视化结果
dcc.Graph(
id='clientside-graph'
),
# 创建一个数据存储组件,用于在客户端存储图形数据
dcc.Store(
id='clientside-figure-store',
data=[{
'x': df[df['country'] == 'Canada']['year'], # 设置x轴数据为加拿大年份数据
'y': df[df['country'] == 'Canada']['pop'] # 设置y轴数据为加拿大人口数据
}]
),
# 创建一个下拉选择框组件,用于选择数据指标(人口、预期寿命、人均GDP)
dcc.Dropdown(
{'pop': 'Population', 'lifeExp': 'Life Expectancy', 'gdpPercap': 'GDP per Capita'},
'pop',
id='clientside-graph-indicator'
),
# 创建一个下拉选择框组件,用于选择国家
dcc.Dropdown(available_countries, 'Canada', id='clientside-graph-country'),
# 创建一个单选框组件,用于选择图形的比例尺(线性或对数)
dcc.RadioItems(
['linear', 'log'],
'linear',
id='clientside-graph-scale'
),
# 创建一个水平分隔线组件,用于分隔布局内容
html.Hr(),
# 创建一个详情组件,用于显示图形存储内容的摘要和详细信息
html.Details([
html.Summary('Contents of figure storage'), # 设置摘要内容
dcc.Markdown( # 设置详细信息内容,使用Markdown格式显示
id='clientside-figure-json'
)
])
])
# 定义一个回调函数,用于更新数据存储组件的数据,当数据指标或国家选择发生变化时触发更新
@callback(
Output('clientside-figure-store', 'data'), # 设置输出为数据存储组件的数据属性
Input('clientside-graph-indicator', 'value'), # 设置输入为数据指标选择框的值属性
Input('clientside-graph-country', 'value') # 设置输入为国家选择框的值属性
)
def update_store_data(indicator, country): # 定义回调函数,接收数据指标和国家作为参数
dff = df[df['country'] == country] # 根据选择的国家筛选数据
return [{ # 返回包含筛选后的数据和图形模式的字典列表
'x': dff['year'], # 设置x轴数据为年份数据
'y': dff[indicator], # 设置y轴数据为选择的数据指标数据
'mode': 'markers' # 设置图形模式为标记点图
}]
# 定义一个客户端回调函数,用于在客户端更新图形组件的figure属性,当数据存储组件的数据或图形比例尺发生变化时触发更新
clientside_callback(
"""
function(data, scale) {
return {
'data': data,
'layout': {
'yaxis': {'type': scale}
}
};
}
""",
Output('clientside-graph', 'figure'), # 设置输出为图形组件的figure属性(需要使用JavaScript语法)
Input('clientside-figure-store', 'data'), # 设置输入为数据存储组件的数据属性(需要使用JavaScript语法)
Input('clientside-graph-scale', 'value') # 设置输入为图形比例尺选择框的值属性(需要使用JavaScript语法)
)
# 定义一个回调函数,用于更新详细信息内容组件的children属性,当数据存储组件的数据发生变化时触发更新(显示图形数据的JSON格式)
@callback(
Output('clientside-figure-json', 'children'), # 设置输出为详细信息内容组件的children属性
#(需要使用JavaScript语法)
Input('clientside-figure-store', 'data') # 设置输入为数据存储组件的数据属性(需要使用JavaScript语法)
)
def generated_figure_json(data):
return '```\n'+json.dumps(data, indent=2)+'\n```'
# 判断当前脚本是否作为主程序运行,如果是则启动Dash应用并开启调试模式(热重载)(Python语法)
if __name__ == '__main__':
app.run(debug=True)