Create dropdown button to filter based on a categorical column

 ̄綄美尐妖づ 提交于 2020-12-01 23:38:44

问题


I have a dataframe like this:

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)

I want to create a plotly figure that has one x (date) and 2 ys (col1 and col2). like this one, where the category dropdown button let's you select the category and updates the figure by filtering the col1 and col2 data for the selected category.

But I cannot make the dropdown to work and update the lines.

This is the code I tried:

# 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) 

I want make the category dropdown button with unique groups from category column, and selecting the category (either G1 or G2) will filter that data and plot the x and ys for this selected category.

I already looked at dropdown page on plotly website but couldn't make the dropdown to work.

https://plot.ly/python/dropdowns/


回答1:


Plotly 3 implemented ipython widgets native support, with this I'm not sure they are maintaining their old widgets. I suggest using ipython widgets because they are more standard and flexible, also I find them a bit easier to use even when it takes some time to get used to them. Here's a working example:

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]))

note that now you don't even need to import offline as the figure itself is an ipython widget. Plotly 3 also implemented an imperative way to write the code that I find to be really useful, you can read more about this and other plotly 3 features (that are sadly not really covered on the docs) in this post.

EDIT

for more than one dropdown something like this should work

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]))


来源:https://stackoverflow.com/questions/56671386/create-dropdown-button-to-filter-based-on-a-categorical-column

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!