1

What I'm Trying To Do...

I am trying to keep the formatting of a Plotly Express Scatterplot for data from the happiness report (2018). I simply want to create a button that can change the x-axis value between certain columns in a pandas dataframe (e.g. "GDP per Capita", "Social Support", etc.)

Here's an example of the scatter plot I am trying to create a button for to switch the X-value of the graph and have it update accordingly.

df = pd.read_csv("https://media.githubusercontent.com/media/ajgallard/happiness_report/main/data/2018_eng.csv")
fig = px.scatter(df, 
                 x="GDP per capita", # The Value I am creating a button for 
                 y="Score", 
                 size="Population", 
                 color="Continent",
                 hover_name="Country/Region", 
                 size_max=60, 
                 color_discrete_sequence=px.colors.qualitative.G10)
fig.show()

I get the following as a result: Plotly Express Scatter Plot:
Plotly Express Scatter Plot

Attempted Solutions...

I attempted to implement a solution to a similar question from: Build a plotly scatterplot with two drop down buttons one for x and one for y axis

import pandas as pd
import plotly.express as px
import plotly.graph_objects as go

df = pd.read_csv("https://media.githubusercontent.com/media/ajgallard/happiness_report/main/data/2018_eng.csv")

cols = df.columns[2:4].values.tolist() # "GDP per Capita" & "Social Support"

fig = go.Figure()
for col in cols:
    figpx = px.scatter(df,
                       x=col,
                       y="Score",
                       size="Population",
                       color="Continent",
                       hover_name="Country/Region",
                       size_max=60,
                       color_discrete_sequence=px.colors.qualitative.G10).update_traces(visible=False)
    
    fig.add_traces(figpx.data)

fig.update_layout(
    updatemenus=[
        {
            "buttons": 
            [
                {
                    "label": k,
                    "method": "update",
                    "args": 
                    [
                        {"visible": [k for k in cols]},
                    ],
                }
                for k in cols
            ]
        }
    ]
).update_traces(visible=True, selector=0)

fig.show()

Using the above mentioned code I get the following as a result: Plotly Express with Button Attempt:
Plotly Express with Button Attempt

What seems to be happening is that the data is overlayed one on top of the other and the button itself does not update anything data related.

Open to Any Potential Workarounds...

I'm fairly new to implementing Plotly Graphs in my data visualizations and I'm open to any other potential workarounds to get the kind of interactive visualization I was hoping to achieve.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
ajgallard
  • 13
  • 1
  • 4

2 Answers2

2
  • there is a core concept with this approach. Need to be able to identify the traces that belong to a column. In this case where color is a categorical there are multiple traces per column
  • added a synthetic column to dataframe dynamically and included in hoverdata Plot. This then means it is in each trace and accessible as customdata[0][0]
  • updatemenus visible then builds truth list based on value in each trace
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go

df = pd.read_csv("https://media.githubusercontent.com/media/ajgallard/happiness_report/main/data/2018_eng.csv")

cols = df.columns[2:4].values.tolist() # "GDP per Capita" & "Social Support"

fig = go.Figure()
for col in cols:
    figpx = px.scatter(df.assign(Plot=col),
                       x=col,
                       y="Score",
                       size="Population",
                       color="Continent",
                       hover_name="Country/Region",
                       hover_data=["Plot"],
                       size_max=60,
                       color_discrete_sequence=px.colors.qualitative.G10).update_traces(visible=False)
    
    fig.add_traces(figpx.data)

fig.update_layout(
    updatemenus=[
        {
            "buttons": 
            [
                {
                    "label": k,
                    "method": "update",
                    "args": 
                    [
                        {"visible": [t.customdata[0][0]==k for t in fig.data]},
                    ],
                }
                for k in cols
            ]
        }
    ]
).update_traces(visible=True, selector=lambda t: t.customdata[0][0]==cols[0] )

fig
  • note Plot in hover, this is customdata[0][0] enter image description here
Rob Raymond
  • 29,118
  • 3
  • 14
  • 30
0

Following the referenced answer, we need to add an element to be updated: the value of the x-axis and the title. As an additional response, the legend has been changed from duplicate to single. I am quoting @A. Donda answer response.

import pandas as pd
import plotly.express as px
import plotly.graph_objects as go

df = pd.read_csv("https://media.githubusercontent.com/media/ajgallard/happiness_report/main/data/2018_eng.csv")

cols = df.columns[2:4].values.tolist() # "GDP per Capita" & "Social Support"

fig = go.Figure()
for col in cols:
    figpx = px.scatter(df,
                       x=col,
                       y="Score",
                       size="Population",
                       color="Continent",
                       hover_name="Country/Region",
                       size_max=60,
                       color_discrete_sequence=px.colors.qualitative.G10).update_traces(visible=False)
    
    fig.add_traces(figpx.data)

fig.update_layout(
    updatemenus=[
        {
            "buttons": 
            [
                {
                    "label": f'{k}',
                    "method": "update",
                    "args": 
                    [
                        {'x': [df[k]]},
                        {'xaxis':{'title':k}},
                        {"visible": k},
                    ],
                }
                for k in cols
            ]
        }        
    ]
).update_traces(visible=True, selector=0)

names = set()
fig.for_each_trace(
    lambda trace:
        trace.update(showlegend=False)
        if (trace.name in names) else names.add(trace.name))

figpx.data[0]['hovertemplate'] = '<b>%{hovertext}</b><br><br>Continent=Europe<br>GDP per capita=%{x}<br>Score=%{y}<br>Population=%{marker.size}<extra></extra>'

fig.show()

enter image description here

r-beginners
  • 31,170
  • 3
  • 14
  • 32
  • Unfortunately, the code provided still maps both traces one on top of the other. If you hover over the pairs of large green bubbles you will see that one references "GDP" and the other "Social Support" . The legend seems to toggle data-points for both trace datasets simultaneously. Additionally, the button somehow removes the "GDP" trace dataset entirely (even when switching back) shifting the data in a bizarre way. – ajgallard Sep 28 '21 at 23:05
  • The function of this graph is to switch the value of the x-axis, but the object drawn as a scatter is the population expressed by size and the continent names classified by color. If you check the hover template for the data created in px, you will see that 'GDP per rcapita' is not embedded. This confirms it, so you can rewrite 'Social support'. As for the legend, it is duplicated because we have set the same data twice. So I've added support for combining them into one. – r-beginners Sep 29 '21 at 04:24
  • try this:`figpx.data[0]['hovertemplate']`-> `'%{hovertext}

    Continent=Europe
    Social support=%{x}
    Score=%{y}
    Population=%{marker.size}'`
    – r-beginners Sep 29 '21 at 04:25