我正在尝试为棒球制作交互式热图。 输入/选择来自下拉菜单,输出是该玩家的热图。
然而,由于某种原因,图表只更新一次!
让我解释一下:
如果我对下拉菜单使用初始值 '',那么我什么都没有开始,这是正常的。
然后,无论我从下拉列表中选择谁,都会显示他们的热图……但之后它就不再更新,无论我选择了谁。
如果我将初始值设置为任何其他玩家,则只会显示初始玩家的图表,不会绘制新图表。
换句话说,生成的网页只会显示生成的第一个图形。无论我从下拉菜单中选择什么,它都保持相同的图形,尽管后端中的数据正在更新并且显示输入已正确更新。
我已经运行调试工具并看到正在使用选择的新值。 所以下面的
heatmap_data
和 new_df
都得到了更新,但是新的图表没有显示!编辑3:
链接到我的 github 仓库:
https://github.com/wiorz/capstone
这是我正在关注的教程,我只是在做第 1 部分:
https://github.com/ArjanCodes/2022-dash
我的
heatmap_update.py
:
# TODO: fix graph not updating after selection!
# importing dependencies
import pandas as pd
from dash import Dash, dcc, html
from dash.dependencies import Input, Output
import plotly.express as px
from . import ids, data
# input
players = data.PLAYERS # this is to keep the framework dynamic for updates, recall this is a ndarray of str!
player_default = data.BASEBALL['Pitcher'][0] # pick the first pitcher as default
df_default = data.BASEBALL[(data.BASEBALL['Pitcher'] == player_default)] # this is VERY important, as it gives a dataframe for use later
# Make data for merging later
pitch_types = data.BASEBALL['TaggedPitchType']
pitch_calls = data.BASEBALL['PitchCall']
def render(app: Dash) -> html.Div:
# interactive update to dropdown
@app.callback(
Output(ids.HEATMAP_UPDATE, "children"),
Input(ids.PLAYER_DROPDOWN, 'value')
)
# NOTE: the input is from the dropdown selection. It is currently a str, but needs to work with multiple players, which would be list[str]
def update_heatmap(pitcher_dropdown) -> html.Div:
heatmap_data = data.BASEBALL[(data.BASEBALL['Pitcher'] == pitcher_dropdown)][['TaggedPitchType','PitchCall','SpinRate']]
new_df = heatmap_data.groupby(["TaggedPitchType","PitchCall"])["SpinRate"].median().reset_index() # using median instead of count()
new_df = new_df.pivot(index='TaggedPitchType', columns='PitchCall')['SpinRate'].fillna(0)
fig = px.imshow(new_df)
fig.update_layout(
title=f'Heatmap of Pitcher {pitcher_dropdown.upper()}',
)
return html.Div(
dcc.Graph(figure=fig, id=ids.HEATMAP_UPDATE)
)
return html.Div(id=ids.HEATMAP_UPDATE)
我的
player_dropdown.py
档案:
from dash import Dash, html, dcc
from dash.dependencies import Input, Output
from . import ids, data
def render(app: Dash) -> html.Div:
# declare variables first
all_players = data.PLAYERS
return html.Div(
children = [
html.H6("Pitchers"),
# dropdown function
dcc.Dropdown(
id = ids.PLAYER_DROPDOWN,
options = [{"label" : p, "value": p} for p in all_players], # list comprehension for selection options
value = all_players[0], # initial value of the dropdown
),
]
)
我试着在 stackoverflow 和其他论坛上查找这里,还没有看到任何类似的问题。
我尝试使用 go.Figure() 手动创建热图以查看它是否是一个 plotly express 问题,这不是因为仍然没有更新。
我查阅了文档,但都没有帮助。最接近的是来自 go.Figure() 的 update_traces() 但它对我需要的东西没有用。
编辑: 这是
data.BASEBALL
中数据的截断样本,它是使用 pandas 从 csv 文件读取的数据帧:
日期 | 投手 | PitchCall | TaggedPitch | 旋转率 | 其他 |
---|---|---|---|---|---|
2022/10/04 | 汤姆 | 快球 | 罢工 | 2000 | NA |
2022/10/04 | 乔 | 沉降片 | 球 | 1300 | NA |
这是字典格式的上表:
{'Date': {2022-10-04, 2022-10-04}, 'Pitcher': {'Tom', 'Joe'}, 'PitchCall': {'Fastball', 'Sinker'}, 'TaggedPitch' : {'Strike', 'Ball'}, 'SpinRate': {2000, 1300}, 'Others': {'', ''}}
data.PLAYERS
是 str 从调用data.BASEBALL["Pitcher'].unique()
的 ndarry
这是
data.BASEBALL.head(10).to_dict()
的未截断输出:
{'未命名: 0': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}, 'PitchNo': {0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9, 9: 10}, '日期': {0: '2022-11-02', 1: '2022-11-02', 2: '2022-11-02', 3: '2022-11-02', 4: '2022-11-02' , 5: '2022-11-02', 6: '2022-11-02', 7: '2022-11-02', 8: '2022-11-02', 9: '2022-11-02' }, '时间': {0: '16:46:21.69', 1: '16:46:52.39', 2: '16:47:05.40', 3: '16:47:20.28', 4: ' 16:47:35.85', 5: '16:47:55.84', 6: '16:48:28.24', 7: '16:48:55.48', 8: '16:49:09.66', 9: ' 16:49:25.16'}, 'PAofInning': {0: 1, 1: 2, 2: 2, 3: 2, 4: 2, 5: 2, 6: 2, 7: 3, 8: 3, 9 : 3}, 'PitchofPA': {0: 1, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 1, 8: 2, 9: 3}, '投手':{0:'海丝特,特拉维斯',1:'海丝特,特拉维斯',2:'海丝特,特拉维斯',3:'海丝特,特拉维斯',4:'海丝特,特拉维斯',5:'海丝特, Travis', 6: 'Hester, Travis', 7: 'Hester, Travis', 8: 'Hester, Travis', 9: 'Hester, Travis'}, 'PitcherId': {0: 8898156.0, 1: 8898156.0, 2 : 8898156.0, 3: 8898156.0, 4: 8898156.0, 5: 8898156.0, 6: 889 8156.0, 7: 8898156.0, 8: 8898156.0, 9: 8898156.0}, 'PitcherThrows': {0: '右', 1: '右', 2: '右', 3: '右', 4: '右', 5: '正确', 6: '正确', 7: '正确', 8: '正确', 9: '正确'}, 'PitcherTeam': {0: 'BAY_BEA', 1: 'BAY_BEA', 2: 'BAY_BEA', 3: 'BAY_BEA', 4: 'BAY_BEA', 5: 'BAY_BEA', 6: 'BAY_BEA', 7: 'BAY_BEA', 8: 'BAY_BEA', 9: 'BAY_BEA'}}
编辑2: 我的其他文件。 我正在使用 main 来调用其他组件。布局处理捆绑。 ID 文件只是用于引用的 id 的集合。
我的
main.py
:
from dash import Dash, html
from dash_bootstrap_components.themes import BOOTSTRAP
from src.components.layout import create_layout
import src.components.data as data
def main() -> None:
app = Dash(external_stylesheets=[BOOTSTRAP]) #input can be a list
app.title = "Pitcher dashboard"
app.layout = create_layout(app)
app.run()
if __name__ == "__main__":
main()
我的
layout.py
:
from dash import Dash, html
from . import player_dropdown, heatmap_update # the "." means relative import
# call to the render file with the IDs, see *.py files in components
def create_layout(app: Dash) -> html.Div:
return html.Div(
className = "app-div",
children=[
html.H1(app.title),
html.Hr(),
# using render() here
html.Div(
className = "dropdown-container",
children = [
player_dropdown.render(app) # same name as the .py file
]
),
# add heatmap here
heatmap_update.render(app)
],
)
我的
ids.py
:
PLAYER_DROPDOWN = "player-dropdown"
HEATMAP_UPDATE = "heatmap-update"