python - 创建下拉按钮以根据分类列进行过滤
问题描述
我有一个这样的数据框:
import pandas as pd
df = pd.DataFrame()
df['category'] = ['G1', 'G1', 'G1', 'G1','G1', 'G1','G1', 'G2', 'G2', 'G2', 'G2', 'G2', 'G2', 'G2']
df['date'] = ['2012-04-01', '2012-04-05', '2012-04-09', '2012-04-11', '2012-04-16', '2012-04-23', '2012-04-30',
'2012-04-01', '2012-04-05', '2012-04-09', '2012-04-11', '2012-04-16', '2012-04-23', '2012-04-30']
df['col1'] = [54, 34, 65, 67, 23, 34, 54, 23, 67, 24, 64, 24, 45, 89]
df['col2'] = round(df['col1'] * 0.85)
我想创建一个有一个 x ( date
) 和 2 ys ( col1
and col2
) 的情节图。就像这个,类别下拉按钮让您选择类别并通过过滤所选类别的数据来col1
更新图形。col2
但是我无法使下拉菜单起作用并更新行。
这是我试过的代码:
# import plotly
from plotly.offline import init_notebook_mode, iplot, plot
import plotly.graph_objs as go
init_notebook_mode(connected=True)
x = 'date'
y1 = 'col1'
y2 = 'col2'
trace1 = {
'x': df[x],
'y': df[y1],
'type': 'scatter',
'mode': 'lines',
'name':'col 1',
'marker': {'color': 'blue'}
}
trace2={
'x': df[x],
'y': df[y2],
'type': 'scatter',
'mode': 'lines',
'name':'col 2',
'marker': {'color': 'yellow'}
}
data = [trace1, trace2]
# Create layout for the plot
layout=dict(
title='my plot',
xaxis=dict(
title='Date',
type='date',
tickformat='%Y-%m-%d',
ticklen=5,
titlefont=dict(
family='Old Standard TT, serif',
size=20,
color='black'
)
),
yaxis=dict(
title='values',
ticklen=5,
titlefont=dict(
family='Old Standard TT, serif',
size=20,
color='black'
)
)
)
# create the empty dropdown menu
updatemenus = list([dict(buttons=list()),
dict(direction='down',
showactive=True)])
total_codes = len(df.category.unique()) + 1
for s, categ in enumerate(df.category.unique()):
visible_traces = [False] * total_codes
visible_traces[s + 1] = True
updatemenus[0]['buttons'].append(dict(args=[{'visible': visible_traces}],
label='category',
method='update'))
updatemenus[0]['buttons'].append(dict(args=[{'visible': [True] + [False] * (total_codes - 1)}],
label='category',
method='update'))
layout['updatemenus'] = updatemenus
fig = dict(data = data, layout = layout)
iplot(fig)
我想使用category
列中的唯一组创建类别下拉按钮,然后选择category
(G1
或G2
)将过滤该数据并为该选定类别绘制x
和。ys
我已经查看了 plotly 网站上的下拉页面,但无法使下拉菜单正常工作。
解决方案
Plotly 3 实现ipython widgets
了本机支持,因此我不确定他们是否在维护旧的小部件。我建议使用ipython widgets
它们,因为它们更加标准和灵活,而且我发现它们更容易使用,即使需要一些时间来适应它们。这是一个工作示例:
from plotly import graph_objs as go
import ipywidgets as w
from IPython.display import display
import pandas as pd
df = pd.DataFrame()
df['category'] = ['G1', 'G1', 'G1', 'G1','G1', 'G1','G1', 'G2', 'G2', 'G2', 'G2', 'G2', 'G2', 'G2']
df['date'] = ['2012-04-01', '2012-04-05', '2012-04-09', '2012-04-11', '2012-04-16', '2012-04-23', '2012-04-30',
'2012-04-01', '2012-04-05', '2012-04-09', '2012-04-11', '2012-04-16', '2012-04-23', '2012-04-30']
df['col1'] = [54, 34, 65, 67, 23, 34, 54, 23, 67, 24, 64, 24, 45, 89]
df['col2'] = round(df['col1'] * 0.85)
x = 'date'
y1 = 'col1'
y2 = 'col2'
trace1 = {
'x': df[x],
'y': df[y1],
'type': 'scatter',
'mode': 'lines',
'name':'col 1',
'marker': {'color': 'blue'}
}
trace2={
'x': df[x],
'y': df[y2],
'type': 'scatter',
'mode': 'lines',
'name':'col 2',
'marker': {'color': 'yellow'}
}
data = [trace1, trace2]
# Create layout for the plot
layout=dict(
title='my plot',
xaxis=dict(
title='Date',
type='date',
tickformat='%Y-%m-%d',
ticklen=5,
titlefont=dict(
family='Old Standard TT, serif',
size=20,
color='black'
)
),
yaxis=dict(
title='values',
ticklen=5,
titlefont=dict(
family='Old Standard TT, serif',
size=20,
color='black'
)
)
)
# Here's the new part
fig = go.FigureWidget(data=data, layout=layout)
def update_fig(change):
aux_df = df[df.category.isin(change['new'])]
with fig.batch_update():
for trace, column in zip(fig.data, [y1, y2]):
trace.x = aux_df[x]
trace.y = aux_df[column]
drop = w.Dropdown(options=[
('All', ['G1', 'G2']),
('G1', ['G1']),
('G2', ['G2']),
])
drop.observe(update_fig, names='value')
display(w.VBox([drop, fig]))
请注意,现在您甚至不需要导入offline
,因为图形本身就是一个 ipython 小部件。Plotly 3 还实现了一种命令式的方式来编写我认为非常有用的代码,你可以在这篇文章中阅读更多关于这个和其他 plotly 3 特性(遗憾的是文档中没有真正涵盖)的信息。
编辑
对于不止一个下拉菜单,这样的东西应该可以工作
def update_fig1(change):
aux_df = df[df.category == change['new']]
aux_df = aux_df[aux_df.category1 == drop2.value]
with fig.batch_update():
for trace, column in zip(fig.data, [y1, y2]):
trace.x = aux_df[x]
trace.y = aux_df[column]
def update_fig2(change):
aux_df = df[df.category1 == change['new']]
aux_df = aux_df[aux_df.category == drop1.value]
with fig.batch_update():
for trace, column in zip(fig.data, [y1, y2]):
trace.x = aux_df[x]
trace.y = aux_df[column]
drop1 = w.Dropdown(options=df.category.unique())
drop2 = w.Dropdown(options=df.category1.unique())
drop1.observe(update_fig1, names='value')
drop2.observe(update_fig2, names='value')
display(w.VBox([w.HBox([drop1, drop2]), fig]))