Reputation: 191
I'm trying to make a scatter plot with 2 dropdown menus that select a data column (from a pandas data frame) to be plotted for x and y-axis. I also want the plot to have a correlation stats annotation that change with the dropdown selection, because the annotation is calculated on both the x and y data as parameters. The first part I've managed to do with the code example below, but I am struggling with the annotation.
import pandas as pd
import numpy as np
import plotly.graph_objects as go
# Prep random data
data = pd.DataFrame(dict(
A=np.random.randint(11, size=10),
B=np.random.randint(11, size=10),
C=np.random.randint(11, size=10),
D=np.random.randint(11, size=10)
))
# Create figure and add one scatter trace
fig = go.Figure()
fig.add_trace(go.Scatter(
x=data['A'],
y=data['A'],
visible=True,
mode='markers',
)
)
# Create x and y buttons
x_buttons = []
y_buttons = []
for column in data.columns:
x_buttons.append(dict(method='restyle',
label=column,
args=[{'x': [data[column]]}]
)
)
y_buttons.append(dict(method='restyle',
label=column,
args=[{'y': [data[column]]}]
)
)
# Pass buttons to the updatemenus argument
fig.update_layout(updatemenus=[dict(buttons=x_buttons, direction='up', x=0.5, y=-0.1),
dict(buttons=y_buttons, direction='right', x=-0.01, y=0.5)])
My idea was to first define a function that will take the x and y attributes from the figure data structure (hoping that the dropdown selection change this attributes) and returns the text annotation. Then, based on the plotly reference example, add the annotation to args
and change the method of the buttons to 'update'. However, that doesn't seem to be the case and the annotation is static. Anyone has an idea of how I could achieve this? Here is the function and the final code:
from scipy import stats
def corr_annotation(x, y):
pearsonr = stats.pearsonr(x, y)
return 'r = {:.2f} (p = {:.3f})'.format(pearsonr[0], pearsonr[1])
import pandas as pd
import numpy as np
import plotly.graph_objects as go
# Prep random data
data = pd.DataFrame(dict(
A=np.random.randint(11, size=10),
B=np.random.randint(11, size=10),
C=np.random.randint(11, size=10),
D=np.random.randint(11, size=10)
))
# Create figure and add one scatter trace
fig = go.Figure()
fig.add_trace(go.Scatter(
x=data['A'],
y=data['A'],
visible=True,
mode='markers',
)
)
fig.add_annotation(dict(text=corr_annotation(fig['data'][0]['x'], fig['data'][0]['y']),
showarrow=False,
yref='paper', xref='paper',
x=0.99, y=0.95))
# Create x and y buttons
x_buttons = []
y_buttons = []
for column in data.columns:
x_buttons.append(dict(method='update',
label=column,
args=[{'x': [data[column]]},
{'annotations': [dict(text=corr_annotation(fig['data'][0]['x'], fig['data'][0]['y']),
showarrow=False,
yref='paper', xref='paper',
x=0.99, y=0.95)]}]
)
)
y_buttons.append(dict(method='update',
label=column,
args=[{'y': [data[column]]},
{'annotations': [dict(text=corr_annotation(fig['data'][0]['x'], fig['data'][0]['y']),
showarrow=False,
yref='paper', xref='paper',
x=0.99, y=0.95)]}]
)
)
# Pass buttons to the updatemenus argument
fig.update_layout(updatemenus=[dict(buttons=x_buttons, direction='up', x=0.5, y=-0.1),
dict(buttons=y_buttons, direction='right', x=-0.01, y=0.5)])
Upvotes: 1
Views: 5390
Reputation: 191
The solution for me was to change to a single dropdown button that select pairs of variables (i.e. changes both x and y). One caveat to this is when dealing with large datasets, as the number of combinations can get pretty big, but for my case (~20 columns) it was fine.
from scipy import stats
def corr_annotation(x, y):
pearsonr = stats.pearsonr(x, y)
return 'r = {:.2f} (p = {:.3f})'.format(pearsonr[0], pearsonr[1])
# Prep random data
import pandas as pd
import numpy as np
np.random.seed(12)
data = pd.DataFrame(dict(
A=np.random.randint(11, size=10),
B=np.random.randint(11, size=10),
C=np.random.randint(11, size=10),
D=np.random.randint(11, size=10)
))
# Create base figure
import plotly.express as px
fig = px.scatter(data, x='A', y='B')
fig.add_annotation(dict(text=corr_annotation(data['A'], data['B']),
showarrow=False,
yref='paper', xref='paper',
x=0.99, y=0.95))
# Create buttons
import itertools
buttons = []
for x, y in itertools.combinations(data.columns, 2):
buttons.append(dict(method='update',
label='{} x {}'.format(x, y),
args=[{'x': [data[x]],
'y': [data[y]]},
{'xaxis': {'title': x},
'yaxis': {'title': y},
'annotations': [dict(text=corr_annotation(data[x], data[y]),
showarrow=False,
yref='paper', xref='paper',
x=0.99, y=0.95)]}]
)
)
# Update and show figure
fig.update_layout(updatemenus=[dict(buttons=buttons, direction='down', x=0.1, y=1.15)])
fig.show()
Upvotes: 4
Reputation: 35155
Since we need to create annotations for each of them, we will create annotations for the x-axis and for the y-axis for the x,y combinations in ABCD order and DCBA order. We have the same R-values, but we have not verified them, so please deal with them yourself.
from scipy import stats
def corr_annotation(x, y):
pearsonr = stats.pearsonr(x, y)
return 'r = {:.2f} (p = {:.3f})'.format(pearsonr[0], pearsonr[1])
import pandas as pd
import numpy as np
import plotly.graph_objects as go
# Prep random data
data = pd.DataFrame(dict(
A=np.random.randint(11, size=10),
B=np.random.randint(11, size=10),
C=np.random.randint(11, size=10),
D=np.random.randint(11, size=10)
))
# Create figure and add one scatter trace
fig = go.Figure()
fig.add_trace(go.Scatter(
x=data['A'],
y=data['A'],
visible=True,
mode='markers',
)
)
fig.add_annotation(dict(text=corr_annotation(fig['data'][0]['x'], fig['data'][0]['y']),
showarrow=False,
yref='paper', xref='paper',
x=0.99, y=0.95))
# Create x and y buttons
x_buttons = []
y_buttons = []
for ncol,rcol in zip(data.columns, data.columns[::-1]):
x_buttons.append(dict(method='update',
label=ncol,
args=[{'x': [data[ncol]]},
{'annotations': [dict(text=corr_annotation(data[ncol], data[rcol]),
showarrow=False,
yref='paper', xref='paper',
x=0.99, y=0.95)]}]
)
)
y_buttons.append(dict(method='update',
label=ncol,
args=[{'y': [data[ncol]]},
{'annotations': [dict(text=corr_annotation(data[rcol], data[ncol]),
showarrow=False,
yref='paper', xref='paper',
x=0.99, y=0.95)]}]
)
)
# Pass buttons to the updatemenus argument
fig.update_layout(updatemenus=[dict(buttons=x_buttons, direction='up', x=0.5, y=-0.1),
dict(buttons=y_buttons, direction='right', x=-0.01, y=0.5)])
fig.show()
Upvotes: 2