Issue updating yaxis categoryarray via dropdown (contains minimum example)

Hello,

I am having difficulties updating yaxis_categoryarray via the 'update' method of a Plotly dropdown. Here is a minimum problematic example to show the problem.

First, the setup:

# -*- coding: utf-8 -*-

import random
import pandas as pd
import plotly.graph_objects as go

ngroups = 3
ncategories = 20

ncategories = min(65+ncategories,91)-65
categories = list(map(chr, range(65, 65+ncategories)))

groups = list(map(chr, range(65, 65+ngroups)))

df = pd.DataFrame(categories, columns=['Cat'])
df['data'] = [random.choice(['No','Yes']) for i in range(ncategories)]
df['x'] = 1
df['groups'] = ['Group ' + random.choice(groups) for i in range(ncategories)]

Here, categories will go along the yaxis and are stored in the column Cat in the dataframe. The data is either 'Yes' or 'No' and all the data is at x=1. The categories are then distributed amongst ngroup groups.

I create multiple scatter plots, one for 'Yes', one for 'No' for each group in groups

data = []
groups = sorted(df['groups'].unique())
for group in groups:
    mask = df[df['groups'] == group]

    dfno = mask[mask['data'] == 'No']
    dfyes = mask[mask['data'] == 'Yes']

    data.append(
        go.Scatter(
            name='Yes',
            mode='markers',
            legendgroup='Yes',
            marker=dict(size=16),
            visible = True if group == groups[0] else False,
            x=dfyes['x'] if len(dfyes.index) > 0 else [None],
            y=dfyes['Cat'] if len(dfyes.index) > 0 else [None],))
    data.append(
        go.Scatter(
            name='No',
            legendgroup='No',
            mode='markers',
            marker=dict(size=16),
            visible = True if group == groups[0] else False,
            x=dfno['x'] if len(dfno.index) > 0 else [None],
            y=dfno['Cat'] if len(dfno.index) > 0 else [None],))

I then create the figure

fig = go.Figure(
    data=data,
    layout = dict(
        showlegend=True,
        yaxis=dict(
            type='category',
            categoryarray=sorted(list(set(df[df['groups']==groups[0]]['Cat'].tolist())))[::-1],
            categoryorder='array')))

Now to create the dropdown, which will both make the apropiate traces visible and update yaxis_categoryarray.

index=0
buttons=[]
for group in groups:
    visible = [False] * len(groups) * 2
    visible[index:index+1] = [True, True]
    index += 2
    group_categories = sorted(list(set(df[df['groups']==group]['Cat'].tolist())))[::-1]
    print(group)
    print(group_categories)
    buttons.append(
        dict(
            label=group,
            method='update',
            args = [{'visible': visible},
                    {'yaxis_categoryarray': group_categories}]))

fig.update_layout(
    updatemenus=[
        dict(
            buttons=buttons,
            bgcolor="white",
            x=1,
            xanchor="right",
            y=1.04,
            yanchor="top",)])

fig.show()

Unfortunately, the problem is encountered here and categoryarray is not updated. Said another way, the yaxis is correct for the first button selection but not the other ones. In fact, for the other ones, the points are sorted by trace.

If instead I use categoryorder="category descending" and drop the categoryarray argument, the resulting figure is completly gone (no axis, no traces, no dropdown, and no error).

Is there an error in the code above? A missing option perhaps? Or is this a bug?

@houtanb

If you redefine the buttons as follows:

buttons.append(
        dict(
            label=group,
            method='update',
            args = [{'visible': visible},
                    {'yaxis.categoryarray': group_categories}]))

it works like a charm.
In button args an inner attribute of a nested dictionary must be accessed following javascript rule, but your code contained the Plotly magic underscore: yaxis_categoryarray.

1 Like

@empet thank you so much! Indeed, it works perfectly. And thanks for the explanation.