Python:将计算的线添加到具有嵌套分类x轴的散点图中

问题描述 投票:0回答:1

交叉发布:https://discourse.bokeh.org/t/add-calculated-horizontal-lines-corresponding-to-categories-on-the-x-axis/5544

我想在Python中复制此图:corrected Tableau plot

这是我的尝试,使用pandasbokeh

进口:

import pandas as pd
from bokeh.io import output_notebook, show, reset_output
from bokeh.palettes import Spectral5, Turbo256
from bokeh.plotting import figure
from bokeh.transform import factor_cmap
from bokeh.models import Band, Span, FactorRange, ColumnDataSource

创建数据:

fruits = ['Apples', 'Pears']
years = ['2015', '2016']

data = {'fruit' : fruits,
        '2015'   : [2, 1],
        '2016'   : [5, 3]}

fruit_df = pd.DataFrame(data).set_index("fruit")
tidy_df = (pd.DataFrame(data)
           .melt(id_vars=["fruit"], var_name="year")
           .assign(fruit_year=lambda df: list(zip(df['fruit'], df['year'])))
           .set_index('fruit_year'))

创建bokeh图:

p = figure(x_range=FactorRange(factors=tidy_df.index.unique()),
           plot_height=400,
           plot_width=400,
           tooltips=[('Fruit', '@fruit'), # first string is user-defined; second string must refer to a column
                     ('Year', '@year'),
                     ('Value', '@value')])

cds = ColumnDataSource(tidy_df)

index_cmap = factor_cmap("fruit", 
                         Spectral5[:2], 
                         factors=sorted(tidy_df["fruit"].unique())) # this is a reference back to the dataframe

p.circle(x='fruit_year', 
         y='value', 
         size=20,
         source=cds,
         fill_color=index_cmap,
         line_color=None,
        )
# how do I add a median just to one categorical section?
median = Span(location=tidy_df.loc[tidy_df["fruit"] == "Apples", "value"].median(), # median value for Apples
              #dimension='height', 
              line_color='red',
              line_dash='dashed', 
              line_width=1.0
             )

p.add_layout(median)

# how do I add this standard deviation(ish) band to just the Apples or Pears section?
band = Band(
    base='fruit_year',
    lower=2,
    upper=4,
    source=cds,
)

p.add_layout(band)

show(p)

输出:

bokeh output

我可以解决这个问题吗?https://github.com/bokeh/bokeh/issues/8592是否还有其他适用于Python的数据可视化库可以实现此目的? Altair,Holoviews,Matplotlib,Plotly ...?

python data-visualization bokeh
1个回答
0
投票

Band是连接的区域,但是所需输出的图像具有两个断开的区域。意思是,您实际上需要两个频段。请看以下示例,以更好地了解乐队:https://docs.bokeh.org/en/latest/docs/user_guide/annotations.html#bands

[通过使用Band(base='fruit_year', lower=2, upper=4, source=cds),您要求Bokeh绘制一个频带,其中对于fruit_year的每个值,下坐标为2,上坐标为4。这正是在Bokeh图上看到的。

有点不相关,但仍然是一个错误-请注意您的X轴与所需的轴有何不同。您必须首先指定主要类别,因此将list(zip(df['fruit'], df['year']))替换为list(zip(df['year'], df['fruit']))

现在,进入“操作方法”部分。由于需要两个单独的频段,因此无法为它们提供相同的数据源。这样做的方法是拥有两个额外的数据源-每个频段一个。最终结果是这样的:

for year, sd in [('2015', 0.3), ('2016', 0.5)]:
    b_df = (tidy_df[tidy_df['year'] == year]
            .drop(columns=['year', 'fruit'])
            .assign(lower=lambda df: df['value'].min() - sd,
                    upper=lambda df: df['value'].max() + sd)
            .drop(columns='value'))
    p.add_layout(Band(base='fruit_year', lower='lower', upper='upper',
                      source=ColumnDataSource(b_df)))

但是还有两个问题。第一个是琐碎的-自动Y范围(默认为DataRange1d类的实例)将不考虑波段的高度。因此,乐队可以轻松超越界限并被剧情裁剪。这里的解决方案是使用考虑到SD值的手动量程。

第二个问题是,带的宽度限制为X范围因子,这意味着圆圈将部分位于带的外部。这个不是那么容易解决。通常的解决方案是使用transform在边缘稍微移动坐标。但是,由于这是分类轴,因此我们无法做到这一点。一种可能的解决方案是创建一个自定义Band模型,该模型添加偏移量:

class MyBand(Band):
    # language=TypeScript
    __implementation__ = """
import {Band, BandView} from "models/annotations/band"

export class MyBandView extends BandView {
    protected _map_data(): void {
        super._map_data()
        const base_sx = this.model.dimension == 'height' ? this._lower_sx : this._lower_sy
        if (base_sx.length > 1) {
            const offset = (base_sx[1] - base_sx[0]) / 2
            base_sx[0] -= offset
            base_sx[base_sx.length - 1] += offset
        }
    }
}

export class MyBand extends Band {
    __view_type__: MyBandView

    static init_MyBand(): void {
        this.prototype.default_view = MyBandView
    }
}
    """

只需在上面的代码中将Band替换为MyBand,它就可以工作。一个警告-您将需要安装Node.js,并且启动时间将持续一到两秒,因为自定义模型代码需要编译。另一个警告-自定义模型代码了解BokehJS的内部结构。意思是,虽然它可以与Bokeh 2.0.2一起使用,但我不能保证它可以与任何其他Bokeh版本一起使用。

© www.soinside.com 2019 - 2024. All rights reserved.