Визуализация данных неотъемлемая часть работы DS. Визуальный анализ помогает выявить интересные зависимости и лучше понять состояние данных. Также очень часто необходимо представлять результаты своей работы руководству, и лучшим способом оказываются графики: наглядно, красиво, понятно. И чем выше руководство тем графики должны быть более впечатляющие :). Удобная и функциональная библиотека Bokeh позволяет быстро и в удобной форме решать эти задачи. А обилие инструментов позволяет достигать невероятных результатов.
Библиотека Bokeh в первую очередь предназначена для построения графиков в веб интерфейсе. Основным выводом является html файл, который можно встраивать в любую часть уже готовой страницы или использовать отдельно. Такой формат данных позволяет очень удобно и быстро делиться графиками, достаточно отправить html файл, и любой у кого есть браузер сможет его открыть.
Установить Bokeh быстро и просто можно в Anaconda conda install bokeh
или с помощью pip pip install bokeh
Для начала проверим версию
import bokeh
bokeh.__version__
"Hello world" для проверки работоспособности Bokeh. Заодно сразу рассмотрим вариации вывода. Как было сказано ранее Bokeh предназначен для построения графиков для веба, поэтому основной формой вывода является html файл который можно использовать как отдельно, так и вставляя в готовую веб страницу. В начале сеанса работы необходимо определить куда будет выводиться график:
from bokeh.plotting import figure, output_file, output_notebook, save, show
# подготовим немного данных
x = [1, 2, 3, 4, 10]
y = [6, 7, 2, 4, 10]
# определим конфигурацию вывода
output_notebook() # вывод в тетрадку
#output_file("lines.html") # вывод в файл
# создадим объект графика и ограничим размеры для удобного рассмотрения
p = figure(title="simple line example", x_axis_label='x', y_axis_label='y', plot_width = 400, plot_height = 400)
# добавим на него объект line
p.line(x, y, legend="Temp.", line_width=2)
#сохранить по указанному имени и открыть в браузере если вывод в файл
#вывести в тетрадку если вывод в тетрадку
show(p)
#сохранить по указанному имени если вывод в файл
#сохранить во временный файл '/tmp/tmpkctayz0p.html' если вывод в тетрадку
save(p)
Это самый простой график в Bokeh. Все что нужно обычному графику: оси, легенда, заголовок - здесь присутствуют. Плюс справа панель с инструментами:
import numpy as np
import pandas as pd
df = pd.read_csv('../../data/video_games_sales.csv')
df = df.dropna()
df['User_Score'] = df.User_Score.astype('float64')
df['Year_of_Release'] = df.Year_of_Release.astype('int64')
df['User_Count'] = df.User_Count.astype('int64')
df['Critic_Count'] = df.Critic_Count.astype('int64')
useful_cols = ['Name', 'Platform', 'Year_of_Release', 'Genre',
'Global_Sales', 'Critic_Score', 'Critic_Count',
'User_Score', 'User_Count', 'Rating'
]
df[useful_cols].head()
sales_df = df[[x for x in df.columns if 'Sales' in x] + ['Year_of_Release']]
sales_df = sales_df.groupby('Year_of_Release',as_index=True).sum()
data = sales_df.to_dict(orient='list')
platform_year_sales = df.pivot_table(
index='Year_of_Release',
columns='Platform',
values='Global_Sales',
aggfunc=sum).fillna(0).applymap(float)
platform_year_sales_dict = platform_year_sales.to_dict(orient='list')
platform_year_critic = df[['Critic_Score','Year_of_Release']].groupby('Year_of_Release',as_index=True).mean()
platform_year_critic_dict = platform_year_critic.to_dict(orient='list')
genre_year_sales = df.pivot_table(
index='Year_of_Release',
columns='Genre',
values='Global_Sales',
aggfunc=sum).fillna(0).applymap(float)
genre_year_sales_dict = genre_year_sales.to_dict(orient='list')
Для примера построим график продаж видео игр в различных странах в зависимости от года.
# импортируем палитру, простой доступ к нужному количеству цветов()
from bokeh.palettes import viridis
p = figure(plot_width=800, plot_height=400,title="Sales")
for key,color in zip(data,viridis(len(data))):
p.line(y=data[key],x=sales_df.index.values,legend=key,color=color,)
p.xaxis.axis_label = 'Year'
p.yaxis.axis_label = 'Sales'
show(p)
На данном этапе график ничем не примечателен, но уже имеет базовый набор инструментов и интерактивен. Добавим в него красоты и функциональности. Сначала сделаем линии толще, перенесем легенду влево и наделим ее функцией скрывания выбранного графика. При нажатии на запись в легенде, соответствующий график можно скрывать - hide, или уменьшать его насыщеность - mute. При использовании mute необходимо в объекте указать muted_color и muted_alpha. Также добавим размерность оси Y
from bokeh.models import PrintfTickFormatter
p = figure(plot_width=800, plot_height=400,title="Sales")
for key,color in zip(data,viridis(len(data))):
p.line(y=data[key],x=sales_df.index.values,legend=key,color=color,line_width = 2,
muted_color = color, # параметр для mute
muted_alpha = 0.3) # параметр для mute
p.xaxis.axis_label = 'Year'
p.yaxis.axis_label = 'Sales'
p.yaxis[0].formatter = PrintfTickFormatter(format="%0u mln")
#расположение легенды
p.legend.location = "top_left"
#поведение легенды hide-скрыть, mute-понизить насыщеность
p.legend.click_policy= "mute"
show(p)
Теперь добавим новый инструмент. HoverTool - инструмент позволяющий привязывать к точкам дополнительную информацию. Например выводить значения конкретного положения, или любую другую информацию.
from bokeh.models import HoverTool
#добавим новый инструмент, также придется перечислить все остальные
_tools_to_show = 'box_zoom,pan,save,hover,reset,wheel_zoom'
p = figure(plot_width=800, plot_height=400,title="Sales",tools=_tools_to_show)
for key,color in zip(data,viridis(len(data))):
p.line(y=data[key],x=sales_df.index.values,legend=key,color=color,line_width = 2,
muted_color = color,
muted_alpha = 0.3,)
#конфигурация инструмента
hover = p.select(dict(type=HoverTool))
# Поле - значение, поддержка форматирования вывода.
hover.tooltips = [("Sale", "@y{0.0f} mln"), ("Year", "@x")]
p.xaxis.axis_label = 'Year'
p.yaxis.axis_label = 'Sales'
p.legend.location = "top_left"
p.legend.click_policy= "mute"
show(p)
У HoverTool есть интересные режимы работы vline, hline,они очень хорошо смотрятся когда не накладываются.
from bokeh.models import HoverTool
_tools_to_show = 'box_zoom,pan,save,hover,reset,wheel_zoom'
p = figure(plot_width=800, plot_height=400,title="Sales",tools=_tools_to_show)
for key,color in zip(data,viridis(len(data))):
p.line(y=data[key],x=sales_df.index.values,legend=key,color=color,line_width = 2,
muted_color = color,
muted_alpha = 0.3,)
hover = p.select(dict(type=HoverTool))
hover.tooltips = [("Sale", "@y{0.0f} mln"), ("Year", "@x")]
#Добавляем режимы работы
hover.mode = 'vline'
p.xaxis.axis_label = 'Year'
p.yaxis.axis_label = 'Sales'
p.legend.location = "top_left"
p.legend.click_policy= "mute"
show(p)
Теперь добавим еще пару графиков динамики, в жанрах и платформах. Организуем это в столбик. Кстати, вы заметили, что при скроле возле конкретной оси, скейл происходит только данной оси.
from bokeh.layouts import column
from bokeh.models import HoverTool
_tools_to_show = 'box_zoom,pan,save,hover,reset,wheel_zoom'
########################## Первый график #####################################################################
p = figure(plot_width=950, plot_height=400,title="Sales",tools=_tools_to_show)
for key,color in zip(data,viridis(len(data))):
p.line(y=data[key],x=sales_df.index.values,legend=key,color=color,line_width = 2,
muted_color = color,
muted_alpha = 0.3,)
hover = p.select(dict(type=HoverTool))
hover.tooltips = [("Sale", "@y{0.0f} mln"), ("Year", "@x")]
p.xaxis.axis_label = 'Year'
p.yaxis.axis_label = 'Sales'
p.legend.location = "top_left"
p.legend.click_policy= "mute"
########################## Второй график #####################################################################
p1 = figure(plot_width=950, plot_height=400,title="Sales platforms",tools=_tools_to_show)
for key,color in zip(platform_year_sales_dict,viridis(len(platform_year_sales_dict))):
p1.line(y=platform_year_sales_dict[key],x=platform_year_sales.index.values,legend=key,color=color,line_width = 2,
muted_color = color,
muted_alpha = 0.3,)
hover = p1.select(dict(type=HoverTool))
hover.tooltips = [("Sale", "@y{0.0f} mln"), ("Year", "@x")]
p1.xaxis.axis_label = 'Year'
p1.yaxis.axis_label = 'Sales'
# Подкорректируем большую легенду, чтобы влезала.
p1.legend.location = "top_left"
p1.legend.click_policy= "mute"
p1.legend.orientation = "horizontal"
p1.legend.label_text_font_size = '8pt'
########################## Третий график #####################################################################
p2 = figure(plot_width=950, plot_height=400,title="Sales genres",tools=_tools_to_show)
for key,color in zip(genre_year_sales_dict,viridis(len(genre_year_sales_dict))):
p2.line(y=genre_year_sales_dict[key],x=genre_year_sales.index.values,legend=key,color=color,line_width = 2,
muted_color = color,
muted_alpha = 0.3,)
hover = p2.select(dict(type=HoverTool))
hover.tooltips = [("Sale", "@y{0.0f} mln"), ("Year", "@x")]
p2.xaxis.axis_label = 'Year'
p2.yaxis.axis_label = 'Sales'
p2.legend.location = "top_left"
p2.legend.click_policy= "mute"
p2.legend.orientation = "horizontal"
p2.legend.label_text_font_size = '8pt'
########################## Layout #####################################################################
# конфигурация Layuot
show(column(p,p1,p2))
Добавим связность графиков. При зуме одного, также будут зумиться и другие. Так как второй и третий график имеют примерно один диапазон по Y, то свяжем их по Y. Также свяжем все графики по Х. Инструмент Box_zoom при этом работает исправно и зуммирует все графики.
_tools_to_show = 'box_zoom,pan,save,hover,reset,wheel_zoom'
########################## Первый график #####################################################################
p = figure(plot_width=950, plot_height=400,title="Sales",tools=_tools_to_show)
for key,color in zip(data,viridis(len(data))):
p.line(y=data[key],x=sales_df.index.values,legend=key,color=color,line_width = 2,
muted_color = color,
muted_alpha = 0.3,)
hover = p.select(dict(type=HoverTool))
hover.tooltips = [("Sale", "@y{0.0f} mln"), ("Year", "@x")]
p.xaxis.axis_label = 'Year'
p.yaxis.axis_label = 'Sales'
p.legend.location = "top_left"
p.legend.click_policy= "mute"
########################## Второй график #####################################################################
#Добавим связь с первым графиком по х, x_range=p.x_range
p1 = figure(plot_width=950, plot_height=400,title="Sales platforms",tools=_tools_to_show,x_range=p.x_range)
for key,color in zip(platform_year_sales_dict,viridis(len(platform_year_sales_dict))):
p1.line(y=platform_year_sales_dict[key],x=platform_year_sales.index.values,legend=key,color=color,line_width = 2,
muted_color = color,
muted_alpha = 0.3,)
hover = p1.select(dict(type=HoverTool))
hover.tooltips = [("Sale", "@y{0.0f} mln"), ("Year", "@x")]
p1.xaxis.axis_label = 'Year'
p1.yaxis.axis_label = 'Sales'
p1.legend.location = "top_left"
p1.legend.click_policy= "mute"
p1.legend.orientation = "horizontal"
p1.legend.label_text_font_size = '8pt'
########################## Третий график #####################################################################
#Добавим связь с первым графиком по х, x_range=p.x_range, и со вторым графиком по y y_range=p1.y_range
p2 = figure(plot_width=950, plot_height=400,title="Sales generes",tools=_tools_to_show,x_range=p.x_range,y_range=p1.y_range)
for key,color in zip(genre_year_sales_dict,viridis(len(genre_year_sales_dict))):
p2.line(y=genre_year_sales_dict[key],x=genre_year_sales.index.values,legend=key,color=color,line_width = 2,
muted_color = color,
muted_alpha = 0.3,)
hover = p2.select(dict(type=HoverTool))
hover.tooltips = [("Sale", "@y{0.0f} mln"), ("Year", "@x")]
p2.xaxis.axis_label = 'Year'
p2.yaxis.axis_label = 'Sales'
p2.legend.location = "top_left"
p2.legend.click_policy= "mute"
p2.legend.orientation = "horizontal"
p2.legend.label_text_font_size = '8pt'
########################## Layout #####################################################################
show(column(p,p1,p2))
В завершении, для визуализации динамики оценок критиков, добавим на первый график вторую ось Y . Для валидного отображения необходимо переопределить HoverTool.
from bokeh.models import LinearAxis, Range1d
_tools_to_show = 'box_zoom,pan,save,hover,reset,wheel_zoom'
########################## Первый график #####################################################################
p = figure(plot_width=950, plot_height=400,title="Sales",tools=_tools_to_show)
for key,color in zip(data,viridis(len(data))):
p.line(y=data[key],x=sales_df.index.values,legend=key,color=color,line_width = 2,
muted_color = color,
muted_alpha = 0.3)
hover = p.select(dict(type=HoverTool))
hover.tooltips = [("Sale", "@y{0.0f} mln"), ("Year", "@x")]
p.xaxis.axis_label = 'Year'
p.yaxis.axis_label = 'Sales'
p.legend.location = "top_left"
p.legend.click_policy= "mute"
#Добавление дополнительной оси.
p.extra_y_ranges = {"Critic": Range1d(start=0, end=100)}
# Рисование нового графика
add_line = p.line(platform_year_critic.index.values, platform_year_critic.values.reshape(-1),
color="blue", y_range_name="Critic", legend="Critic score",
muted_color = "blue",
muted_alpha = 0.3,
line_dash="4 4")
p.add_layout(LinearAxis(y_range_name="Critic"), 'left')
#Переопределение HoverTool для нового графика
p.add_tools(HoverTool(tooltips=[("Critic score", "@y{0.0f}"), ("Year", "@x")] , renderers=[add_line]))
########################## Второй график #####################################################################
p1 = figure(plot_width=950, plot_height=400,title="Sales platforms",tools=_tools_to_show,x_range=p.x_range)
for key,color in zip(platform_year_sales_dict,viridis(len(platform_year_sales_dict))):
p1.line(y=platform_year_sales_dict[key],x=platform_year_sales.index.values,legend=key,color=color,line_width = 2,
muted_color = color,
muted_alpha = 0.3)
hover = p1.select(dict(type=HoverTool))
hover.tooltips = [("Sale", "@y{0.0f} mln"), ("Year", "@x")]
p1.xaxis.axis_label = 'Year'
p1.yaxis.axis_label = 'Sales'
p1.legend.location = "top_left"
p1.legend.click_policy= "mute"
p1.legend.orientation = "horizontal"
p1.legend.label_text_font_size = '8pt'
########################## Третий график #####################################################################
p2 = figure(plot_width=950, plot_height=400,title="Sales generes",tools=_tools_to_show,x_range=p.x_range,y_range=p1.y_range)
for key,color in zip(genre_year_sales_dict,viridis(len(genre_year_sales_dict))):
p2.line(y=genre_year_sales_dict[key],x=genre_year_sales.index.values,legend=key,color=color,line_width = 2,
muted_color = color,
muted_alpha = 0.3,)
hover = p2.select(dict(type=HoverTool))
hover.tooltips = [("Sale", "@y{0.0f} mln"), ("Year", "@x")]
p2.xaxis.axis_label = 'Year'
p2.yaxis.axis_label = 'Sales'
p2.legend.location = "top_left"
p2.legend.click_policy= "mute"
p2.legend.orientation = "horizontal"
p2.legend.label_text_font_size = '8pt'
########################## Layout #####################################################################
show(column(p,p1,p2))
Bokeh очень мощный инструмент для построения графиков. В данном ноутбуке я показал только то, что мне очень понравилось при первом знакомстве с этой библиотекой. Возможно, есть еще более крутые фишки до которых я не добрался. Целью ноутбука ставлю - заинтересовать вас, если вам после просмотра захотелось разобраться получше, то вот ссылки: