代码之家  ›  专栏  ›  技术社区  ›  Evan

Python:使用嵌套的分类x轴向散点图添加计算线

  •  0
  • Evan  · 技术社区  · 6 年前

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

    我想用Python复制此绘图: corrected Tableau plot

    pandas bokeh

    进口:

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

    绘图:

    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

    0 回复  |  直到 6 年前
        1
  •  1
  •   Eugene Pakhomov    6 年前

    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图上看到的。

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

    DataRange1d 默认情况下,类)将不考虑标注栏的高度。因此,乐队可以很容易地出界,并被情节裁剪。这里的解决方案是使用考虑SD值的手动测距。

    第二个问题是频带宽度被限制在X范围因子内,这意味着圆将部分位于频带之外。这个问题不太容易解决。通常,解决方法是使用 transform 只需在边缘移动一点坐标。但由于这是一个分类轴,我们不能这样做。这里一个可能的解决方案是创建一个自定义 乐队

    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
        }
    }
        """
    

    替换 乐队 MyBand 在上面的代码中,它应该可以工作。需要注意的是,您需要安装Node.js,启动时间将延长一两秒钟,因为自定义模型代码需要编译。另一个警告-自定义模型代码了解BokeHJ的内部结构。这意味着,当它与Bokeh2.0.2一起工作时,我不能保证它将与任何其他Bokeh版本一起工作。