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

Posted

技术标签:

【中文标题】单击条形图中的条形以生成该条形中值的散点图【英文标题】:Click on a bar in bar plot to produce a scatterplot of the values in that bar 【发布时间】:2021-12-18 06:12:40 【问题描述】:

此代码生成条形图:

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

输出是:

我想点击任何一个红色条,它会将我带到该类中值的散点图。

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

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

而且我知道如何点击一个条形图来返回一个数据框,我可以像这样将它放入散点图中:

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),然后单击返回一个可以读取到散点图中的数据框。

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

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

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

【问题讨论】:

请注意,plotly 和 matplotlib 是非常不同的库。它们不能结合在一起。您必须选择其中之一。 matplotlib 中的figaxes 对象与plotly 中创建的fig 对象完全不同。它们不共享任何相同的属性,也不与任何类型的类层次结构相关,因此您不能尝试将 matplotlib axes 对象设置为 plotly fig 对象 【参考方案1】:

你没有指定你想以任何方式结合 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)

【讨论】:

【参考方案2】:

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

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

另外,一个有情节的数字不是一个数组,所以fig[0,0] 没有意义。也许您打算访问fig.data,这是一个元组,意味着您可以访问fig.data[0], fig.data[1], ... fig.data[N]

【讨论】:

以上是关于单击条形图中的条形以生成该条形中值的散点图的主要内容,如果未能解决你的问题,请参考以下文章

数据库中jfreechart中的散点图

一个图形核心图中的折线图和条形图

Matplotlib绘制散点图与条形图

R语言使用GGally包的ggpairs函数可视化分组多变量的两两关系图对角线上连续变量密度图离散变量条形图两两关系图中包含散点图直方图箱图以及总体相关性和分组相关性分析

R语言使用GGally包的ggpairs函数可视化分组多变量的两两关系图对角线上连续变量密度图离散变量条形图两两关系图中包含散点图直方图箱图以及总体相关性和分组相关性分析

Matplotlib散点图条形图直方图-02