【问题标题】:bokeh - multiline line plot with flexible mappingbokeh - 具有灵活映射的多线图
【发布时间】:2019-04-29 09:58:08
【问题描述】:

我有一个如下格式的 Pandas DataFrame

name -  date   - score
 A   - 1/1/10  - 100
 A   - 1/2/10  - 200
 A   - 1/3/10  - 300
 B   - 1/1/10  - 150
 B   - 1/2/10  - 400
 B   - 1/3/10  - 600

我想创建一个散景图,在 x 轴上有日期,在 y 轴上有分数,每个名称都有单独的线 + 颜色。我正在使用 Jupyter 笔记本工作。

这里有一些测试数据,虽然我想得到一些适用于名称中任意数量/值的东西,而不仅仅是 A 和 B。

import pandas as pd
import datetime
test_data = {'name':['A','A','A','B','B','B'],
        'date':[datetime.date(2010,1,1),
               datetime.date(2010,2,1),
              datetime.date(2010,3,1),
              datetime.date(2010,1,1),
              datetime.date(2010,2,1),
              datetime.date(2010,3,1),],
        'score':[100,200,300,150,400,600]}

plot_df = pd.DataFrame(test_data)

使用 Seaborn,我会这样做。

import seaborn as sns
ax = sns.lineplot(data=plot_df, x='date',y='score',hue='name')

我想知道使用 Bokeh 做同样事情的最有效方法是什么?

我可以像这样策划一个玩家。

import bokeh.plotting as bp
bp.output_notebook()

filtered_df = plot_df[plot_df.player == 'A'].sort_values(by=['date'])
plot_ds = bp.ColumnDataSource(filtered_df)
plot = bp.figure()
plot.line('date','score',source=plot_ds)
bp.show(plot)

我想知道如何让它适用于任意数量的不同名称。同样,我需要它能够适应不同名称数量的变化。

我认为我应该以某种方式使用颜色映射器,但对我究竟如何合并它感到困惑?我还看到还有另一个答案here 对变量进行硬编码 --> 颜色映射,并试图想出最简单的概括这一点的方法。

编辑 - 多线图还需要每个名称的图例,类似于 Seaborn 示例。

下一步是让它工作,以便您可以使用滑块 + 单选按钮动态更改名称和日期范围,但我想先让这个更简单的绘图工作。这就是为什么我不只是坚持使用 Seaborn。

【问题讨论】:

    标签: python plot bokeh timeserieschart


    【解决方案1】:

    可能是这样的(对于 Bokeh 1.1.0):

    import pandas as pd
    import datetime
    import bokeh.plotting as bp
    from bokeh.palettes import Category10
    
    test_data = {'name': ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C'],
                 'date': [datetime.date(2010, 1, 1),
                          datetime.date(2010, 2, 1),
                          datetime.date(2010, 3, 1),
                          datetime.date(2010, 1, 1),
                          datetime.date(2010, 2, 1),
                          datetime.date(2010, 3, 1),
                          datetime.date(2010, 1, 1),
                          datetime.date(2010, 2, 1),
                          datetime.date(2010, 3, 1), ],
                 'score': [100, 200, 300, 150, 400, 600, 150, 250, 400]}
    
    plot_df = pd.DataFrame(test_data)
    gby = plot_df.groupby('name')
    names = list(gby.groups.keys())
    palette = Category10[len(names)]
    
    plot_df['color'] = [palette[names.index(x)] for i, sdf in gby for x in sdf['name']]
    
    plot = bp.figure(x_axis_type = 'datetime')
    gby.apply(lambda d: plot.line('date', 'score', line_color = d['color'].unique()[0], line_width = 3, legend = d['name'].unique()[0], source = d))
    
    bp.show(plot)
    

    或使用multi_line:

    import pandas as pd
    import datetime
    import bokeh.plotting as bp
    from bokeh.palettes import Category10
    from bokeh.models import ColumnDataSource
    
    test_data = {'name': ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C'],
                 'date': [datetime.date(2010, 1, 1),
                          datetime.date(2010, 2, 1),
                          datetime.date(2010, 3, 1),
                          datetime.date(2010, 1, 1),
                          datetime.date(2010, 2, 1),
                          datetime.date(2010, 3, 1),
                          datetime.date(2010, 1, 1),
                          datetime.date(2010, 2, 1),
                          datetime.date(2010, 3, 1), ],
                 'score': [100, 200, 300, 150, 400, 600, 150, 250, 400]}
    
    plot_df = pd.DataFrame(test_data)
    gby = plot_df.groupby('name')
    
    plot = bp.figure(x_axis_type = 'datetime')
    
    x = [list(sdf['date']) for i, sdf in gby]
    y = [list(sdf['score']) for i, sdf in gby]
    source = ColumnDataSource(dict( x = x, 
                                    y = y, 
                                    legend = plot_df['name'].unique(), 
                                    color = Category10[len(plot_df['name'].unique())]))
    plot.multi_line('x', 'y', legend = 'legend', line_color = 'color', line_width = 3, source = source)
    bp.show(plot)
    

    结果(两个选项):

    【讨论】:

    • 谢谢,当我运行第一个例子时,我得到一个错误'dict_keys'对象没有属性'index'。第二个示例运行良好,但没有标识 A-C 的图例。有没有办法修复第一个示例中的错误,或者在第二个示例中添加图例?抱歉,我在写问题时没有明确提及传说。
    • 如果将名称包装在 list() 中似乎可以工作,但我想知道是否有一种方法可以使用第二种方法添加图例,因为它看起来更优雅。
    • 在我的系统上一切正常,但我在任何情况下都添加了强制转换列表。我希望您使用的是最新的 Bokeh v1.1.0?我可以在第二个选项中添加一个图例,但 multi_line 在逻辑上是一个字形,因此您将无法单独隐藏或静音这些行。
    • 仅供参考,当我在实际数据集上执行此操作时,我遇到了一个奇怪的错误,颜色都相同,并且在我使用时已修复 - plot_df['color'] = plot_df['name' ].map(dict(zip(names,palette))) 我认为这对 Pandas 来说性能更好。
    猜你喜欢
    • 2020-05-18
    • 1970-01-01
    • 1970-01-01
    • 2019-02-20
    • 1970-01-01
    • 2012-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多