Home > Mobile >  Click on a bar in bar plot to produce a scatterplot of the values in that bar
Click on a bar in bar plot to produce a scatterplot of the values in that bar

Time:11-05

This code produces a bar plot:

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd



import plotly.graph_objects as go
classes= ['class1', 'class2', 'class3', 'class4', 'class5', 'class6', 'class7']
lens = [199, 30, 89, 59, 109, 115, 89]
nums = [145, 457, 123, 67, 35, 31, 134]


fig = go.Figure(data=[
    go.Bar(name='Length', x=classes, y=lens),
    go.Bar(name='Number', x=classes, y=nums),
])

# Change the bar mode
fig.update_layout(barmode='group')
fig.update_layout(title_text='Length and Number',
                  title_x=0.1,
                  plot_bgcolor='rgba(0,0,0,0)',
                  paper_bgcolor='rgba(0,0,0,0)',
                  bargap=0.30,
                  bargroupgap=0.0,
                  margin=dict(l=50, r=50, t=50, b=50),
                  xaxis_title="Score Class",
                  yaxis_title="Length and Number",
                  yaxis = dict(
                  tickfont = dict(size=13)),

                  xaxis = dict(
                  tickfont = dict(size=13)),)



fig.update_xaxes(showline=True, linewidth=2, linecolor='black')
fig.update_yaxes(showline=True, linewidth=2, linecolor='black')
fig.show()

The output is:

enter image description here

I want to click on any of the red bars, and it will bring me to a scatterplot of values in that class.

I can produce a scatterplot with this:

dict2 = {}

dict2['class1'] = [(2,2),(1,1),(2,3),(3,4),(5,1)]
dict2['class2'] = [(3,1),(4,4),(5,5),(6,2),(7,1)]
dict2['class3'] = [(3,2),(4,1),(5,4),(6,4),(7,1)]
dict2['class4'] = [(3,1),(4,5),(6,3),(4,3),(5,3)]
dict2['class5'] = [(1,1),(1,1),(1,2),(3,1),(4,3)]
dict2['class6'] = [(2,2),(2,1),(2,3),(5,3),(6,4)]


class1_dict = {}
class1_dict['xs'] = [i[0] for i in dict2['class1']]
class1_dict['ys'] = [i[1] for i in dict2['class1']]
plt.scatter(class1_dict['xs'],class1_dict['ys'])
plt.show()

enter image description here

And I know how to click on a bar generally to return a dataframe that I could put into the scatterplot like this:

dict_name = {}
dict_name['classes'] = classes
dict_name['lens'] = lens
dict_name['nums'] = nums

df = pd.DataFrame.from_dict(dict_name, orient='columns')
print(df)
axs = df.hist(bins=4, picker=True)
ax = axs[0, 0]

def onpick(event):
    bar = event.artist
    left = bar.get_x()
    right = left   bar.get_width()
    col_df = df[(df.lens >= left) & (df.lens <= right)]
    
    
ax.figure.canvas.mpl_connect('pick_event', onpick)
#plt.show()

I'm trying to change that last piece of code, so instead of axs = df.hist(bins=4, picker=True), I can read in my bar plot, and upon clicking, return a dataframe that I can read into a scatterplot.

So I thought I just needed to somehow add these two lines:

axs = df.hist(bins=4, picker=True)
ax = axs[0, 0]

To my bar plot code, to make it clickable.

So I thought since axs is just a plot, which is what fig, I could just add this line to the bar plot code and it would work:

fig = go.Figure(data=[
    go.Bar(name='Length', x=classes, y=lens),
    go.Bar(name='Number', x=classes, y=nums),
]) 
ax = fig[0,0]

The error I get is:

Traceback (most recent call last):
  File "/Users/slowatkela/anaconda/lib/python3.7/site-packages/plotly/basedatatypes.py", line 188, in _check_path_in_prop_tree
    obj = obj[p]
  File "/Users/slowatkela/anaconda/lib/python3.7/site-packages/plotly/basedatatypes.py", line 732, in __getitem__
    prop = BaseFigure._str_to_dict_path(prop)
  File "/Users/slowatkela/anaconda/lib/python3.7/site-packages/plotly/basedatatypes.py", line 1839, in _str_to_dict_path
    ret = _str_to_dict_path_full(key_path_str)[0]
  File "/Users/slowatkela/anaconda/lib/python3.7/site-packages/plotly/basedatatypes.py", line 71, in _str_to_dict_path_full
    if len(key_path_str):
TypeError: object of type 'int' has no len()

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "test3.py", line 17, in <module>
    ax=axs[0,0]
  File "/Users/slowatkela/anaconda/lib/python3.7/site-packages/plotly/basedatatypes.py", line 754, in __getitem__
    err = _check_path_in_prop_tree(self, orig_prop, error_cast=PlotlyKeyError)
  File "/Users/slowatkela/anaconda/lib/python3.7/site-packages/plotly/basedatatypes.py", line 212, in _check_path_in_prop_tree
    if prop[i][0] == "_":
TypeError: 'int' object is not subscriptable

I guess it's because the first plot makes a grouped bar plot makes one figure whereas the histogram example makes two plots? Could someone show me where I'm going wrong?

CodePudding user response:

As mentioned by @JohanC in the comments, plotly and matplotlib are very different libraries. This means that their objects are not related by any kind of class hierarchy, and don't share the same properties.

For that reason, you cannot set a matplotlib axes object equal to a plotly figure object. A plotly figure object is not the same as a matplotlib figure object. You will probably need to stay within one library to achieve what you want. If the matplotlib onpick functionality is important to you, then you should probably stay within matplotlib. I believe that in matplotlib you can construct hoverevents but it's more effort than in plotly which has hoverevents as the default for almost if not all of the figures.

Also a plotly figure isn't an array so fig[0,0] doesn't make sense. Perhaps you meant to access fig.data which is a tuple meaning you can access fig.data[0], fig.data[1], ... fig.data[N]

  • Related