боке - многострочный график с гибким отображением

У меня есть 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')

Мне интересно, как наиболее эффективно сделать то же самое с помощью боке?

Я могу построить одиночный игрок, как это.

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)

Мне интересно, как заставить это работать для произвольного количества разных имен. Опять же, мне нужно, чтобы он был устойчивым к изменению количества различных имен.

Я думаю, что мне следует как-то использовать цветовую карту, но я не понимаю, как именно я ее включаю? Я также вижу, что есть еще один ответ здесь, который жестко кодирует переменную --> сопоставление цветов и пытается подумайте о самом простом способе обобщить это.

РЕДАКТИРОВАТЬ - многострочной диаграмме также потребуется легенда для каждого имени, как в примере Seaborn.

Следующим шагом будет заставить это работать, чтобы вы могли динамически изменять имена и диапазон дат с помощью ползунка + переключателей, но я хочу, чтобы сначала работал этот более простой график. Вот почему я не просто придерживаюсь Seaborn.


person oli5679    schedule 29.04.2019    source источник


Ответы (1)


Может как-то так (для Боке 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)

Результат (оба варианта):

введите здесь описание изображения

person Tony    schedule 29.04.2019
comment
Спасибо, когда я запускаю первый пример, я получаю сообщение об ошибке: объект dict_keys не имеет атрибута index. Второй пример работает хорошо, но не имеет легенды, идентифицирующей A-C. Есть ли способ исправить ошибку в первом примере или добавить легенду во второй пример? Извините, я не упомянул легенду, когда писал вопрос. - person oli5679; 29.04.2019
comment
Кажется, работает, если вы заключаете имена в list(), но мне интересно, есть ли способ добавить легенду, используя второй метод, потому что он кажется немного более элегантным. - person oli5679; 29.04.2019
comment
В моей системе все работает нормально, но я добавил кастинг в список на всякий случай. Надеюсь, вы используете последнюю версию Bokeh v1.1.0? Я могу добавить легенду ко второму варианту, но multi_line логически является одним глифом, поэтому вы не сможете скрыть или заглушить строки по отдельности. - person Tony; 29.04.2019
comment
К вашему сведению, у меня возникла странная ошибка, когда все цвета были одинаковыми, когда я делал это в своем реальном наборе данных, и она была исправлена, когда я использовал - plot_df['color'] = plot_df['name'].map(dict(zip( имена, палитра))) Я думаю, что это более производительно для панд в любом случае. - person oli5679; 29.04.2019