Как правильно обрабатывать дату и время по категориальным осям на графике тепловой карты bokeh / holoviews? - PullRequest
0 голосов
/ 09 июля 2019

Я пытаюсь построить простую тепловую карту, используя bokeh / holoviews. Мои данные (pandas dataframe) имеют категории (по y) и дату / время (по x). Проблема состоит в том, что количество категориальных элементов составляет> 3000, и результирующий график отображается с перепутанными перекрывающимися тикерами на оси y, что делает его абсолютно бесполезным. В настоящее время существует ли надежный способ в боке выбрать только подмножество тикеров в зависимости от уровня масштабирования?

Я уже пробовал сюжетно, и результат выглядит идеально, но мне нужно использовать bokeh / holoviews и datashader. Я также хочу избегать замены категорий на числовые тикеры.

Я также пробовал это решение , но на самом деле оно не работает (боке 1.2.0).

Это игрушечный пример, представляющий мой вариант использования (на самом деле здесь #y равен 1000, но это дает идею)

from datetime import datetime
import pandas as pd
import numpy as np
from bokeh.plotting import figure, show
from bokeh.transform import linear_cmap
from bokeh.io import output_notebook

output_notebook()

# build sample data
index = pd.date_range(start='1/1/2019', periods=1000, freq='T')
data = np.random.rand(1000,100)
columns = ['col'+ str(n) for n in range(100)]

# initial data format
df = pd.DataFrame(data=data, index=index, columns=columns)

# bokeh
df = df.stack().reset_index()
df.rename(columns={'level_0':'x','level_1':'y', 0:'z'},inplace=True)
df.sort_values(by=['y'],inplace=True)

x = [
     date.to_datetime64().astype('M8[ms]').astype('O')
     for date in df.x.to_list()
]

data = {
    'value': df.z.to_list(),
    'x': x,
    'y': df.y.to_list(), 
    'date' : df.x.to_list()
}

p = figure(x_axis_type='datetime', y_range=columns, width=900, tooltips=[("x", "@date"), ("y", "@y"), ("value", "@value")])

p.rect(x='x', y='y', width=60*1000, height=1, line_color=None, 
                   fill_color=linear_cmap('value', 'Viridis256', low=df.z.min(), high=df.z.max()), source=data)

show(p)

Это ожидаемый результат, полученный в Plotly

plotly

Пока это результаты, полученные в Боке

бок

1 Ответ

1 голос
/ 13 июля 2019

Наконец, я частично последовал предложению Джеймса и сумел заставить его работать, используя обратный вызов python для тикера. Это решение было трудно найти для меня. Я действительно искал все документы, примеры и исходные коды Bokeh в течение нескольких дней.

Основная проблема для меня заключается в том, что в документе не упоминается, как я могу использовать объекты "ColumnDataSource" в пользовательском обратном вызове. https://bokeh.pydata.org/en/1.2.0/docs/reference/models/formatters.html#bokeh.models.formatters.FuncTickFormatter.from_py_func

Наконец, это очень помогло: https://bokeh.pydata.org/en/1.2.0/docs/user_guide/interaction/callbacks.html#customjs-with-a-python-function.

Итак, я изменил исходный код следующим образом в надежде, что он кому-нибудь пригодится:

from datetime import datetime
import pandas as pd
import numpy as np
from bokeh.plotting import figure, show
from bokeh.transform import linear_cmap
from bokeh.io import output_notebook
from bokeh.models import FuncTickFormatter
from bokeh.models import ColumnDataSource

output_notebook()

# build sample data
index = pd.date_range(start='1/1/2019', periods=1000, freq='T')
data = np.random.rand(1000,100)
columns_labels = ['col'+ str(n) for n in range(100)]
columns = [n for n in range(100)]

# initial data format
df = pd.DataFrame(data=data, index=index, columns=columns)

# bokeh
df = df.stack().reset_index()
df.rename(columns={'level_0':'x','level_1':'y', 0:'z'},inplace=True)
df.sort_values(by=['y'],inplace=True)

x = [
     date.to_datetime64().astype('M8[ms]').astype('O')
     for date in df.x.to_list()
]

data = {
    'value': df.z.to_list(),
    'x': x,
    'y': df.y.to_list(), 
    'y_labels_tooltip' : [columns_labels[k] for k in df.y.to_list()],
    'y_ticks' : columns_labels*1000,
    'date' : df.x.to_list()
}

cd = ColumnDataSource(data=data)

def ticker(source=cd):
    labels = source.data['y_ticks']
    return "{}".format(labels[tick])

#p = figure(x_axis_type='datetime', y_range=columns, width=900, tooltips=[("x", "@date{%F %T}"), ("y", "@y_labels"), ("value", "@value")])

p = figure(x_axis_type='datetime', width=900, tooltips=[("x", "@date{%F %T}"), ("y", "@y_labels_tooltip"), ("value", "@value")])

p.rect(x='x', y='y', width=60*1000, height=1, line_color=None, 
                   fill_color=linear_cmap('value', 'Viridis256', low=df.z.min(), high=df.z.max()), source=cd)

p.hover.formatters = {'date': 'datetime'}
p.yaxis.formatter = FuncTickFormatter.from_py_func(ticker)
p.yaxis[0].ticker.desired_num_ticks = 20

show(p)

Результат таков:

бок

...