单击条形图中的条形图以生成该条形图中值的散点图

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

此代码生成条形图:

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

输出为:

我想点击任何一个红色条,它会把我带到那个 class.

中值的散点图

我可以用这个生成散点图:

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

而且我知道如何点击条形图 return 我可以像这样放入散点图中的数据框:

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

我正在尝试更改最后一段代码,因此我可以读取条形图而不是 axs = df.hist(bins=4, picker=True),单击后,return 一个我可以读取的数据框散点图。

所以我想我只需要以某种方式添加这两行:

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

到我的条形图代码,使其可点击。

所以我想因为 axs 只是一个情节,这就是 fig,我可以将这一行添加到条形图代码中,它会起作用:

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

我得到的错误是:

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

我想这是因为第一个图使分组条形图成为一个数字,而直方图示例却成为两个图?谁能告诉我哪里出错了?

正如@JohanC 在评论中提到的,plotlymatplotlib 是非常不同的库。这意味着它们的对象与任何类型的 class 层次结构都不相关,并且不共享相同的属性。

因此,您不能将 matplotlib axes 对象设置为等于 plotly 图形对象。绘图图形对象与 matplotlib 图形对象不同。您可能需要留在一个库中才能实现您想要的。如果 matplotlib onpick 功能对你很重要,那么你应该留在 matplotlib 中。我相信在 matplotlib 中你可以构造 hoverevents 但它比在 plotly 中更努力,后者将 hoverevents 作为几乎所有数字的默认值。

此外,绘图不是数组,因此 fig[0,0] 没有意义。也许你打算访问 fig.data 这是一个元组意味着你可以访问 fig.data[0], fig.data[1], ... fig.data[N]

你没有指定你想以任何方式组合 plotly 和 matplotlib,所以如果你想知道如何用 Plotly 做你要求的事情,这里有一个设置那。如果这是您可以使用的东西,我很乐意解释细节。

Plotly Dash 应用程序

完整代码:

import plotly.graph_objects as go # or plotly.express as px
fig = go.Figure() 

from jupyter_dash import JupyterDash
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import json


import plotly.express as px

styles = {
    'pre': {
        'border': 'thin lightgrey solid',
        'overflowX': 'scroll'
    }
}


df = px.data.tips()
fig1 = px.bar(df, x="sex", y="total_bill", color='time')
fig2= px.scatter(df, x="total_bill", y="tip")
# f = fig.full_figure_for_development(warn=False)

app = JupyterDash(__name__)

app.layout = html.Div([
    dcc.Graph(id = 'fig1', figure=fig1),
    dcc.Graph(id = 'fig2', figure=fig2),
    html.Div(className='row', children=[
        html.Div([
            dcc.Markdown(("""
              Clickinfo:
            """)),
            html.Pre(id='txt_output', style=styles['pre']),
        ], className='three columns'),
    ])
])

# inspect clickdata
@app.callback(
    Output('txt_output', 'children'),
    [Input('fig1', 'clickData')])
def display_click_data(clickData):
    if clickData is not None:
        output = json.dumps({'clickinfo':clickData}
                                , indent = 2)
        return output

# Use clickrInfo from fig1 to subset data in fig2
@app.callback(
    Output('fig2', 'figure'),
    [Input('fig1', 'clickData')])
def display_click_data(clickData):
    
    if clickData is not None:
        subset = clickData['points'][0]['x']
        fig = px.scatter(df[df['sex'] == subset], x="total_bill", y="tip")
        return fig
    return fig2


app.run_server(mode='external', port = 8071, dev_tools_ui=True,
          dev_tools_hot_reload =True, threaded=True)