玖叶教程网

前端编程开发入门

Python中的交互式数据可视化与Bokeh(系列四)

添加交互

将Bokeh与众不同的功能是它能够在您的可视化中轻松实现交互性。Bokeh甚至将自己描述为交互式可视化库

Bokeh是一个交互式可视化库,面向现代Web浏览器进行演示。(来源)

在本节中,我们将介绍五种可以添加交互性的方法:

  • 配置工具栏
  • 选择数据点
  • 添加悬停操作
  • 链接轴和选择
  • 使用图例突出显示数据

实现这些交互式元素为探索静态可视化本身无法完成的数据开辟了可能性。

配置工具栏

正如您在“ 生成您的第一个图”中所看到的那样,默认的Bokeh figure()带有一个开箱即用的工具栏。默认工具栏附带以下工具(从左到右):

  • 盒子缩放
  • 轮缩放
  • 保存
  • 重启
  • 指向Bokeh配置绘图工具的用户指南的链接
  • Bokeh主页的链接

该工具栏可通过使除去toolbar_location=None实例化时figure()对象,或通过传递的任何重新定位'above','below','left',或'right'。

此外,工具栏可以配置为包含您需要的任何工具组合。Bokeh提供五种类别的18种特定工具:

  • 潘/阻力:box_select,box_zoom,lasso_select,pan,xpan,ypan,resize_select
  • 点击/点按:poly_select,tap
  • 滚动/捏:wheel_zoom,xwheel_zoom,ywheel_zoom
  • 操作:undo,redo,reset,save
  • 督察:crosshair,hover

要了解工具,请务必访问“ 指定工具”。否则,它们将在覆盖本文所涵盖的各种交互中进行说明。

选择数据点

在声明字形时,实现选择行为就像添加一些特定关键字一样简单。

下一个示例将创建一个散点图,将玩家的三分球命中总数与所取得的百分比相关联(对于至少有100次三分球命中的球员)。

数据可以从player_statsDataFrame汇总:

# Find players who took at least 1 three-point shot during the season
three_takers = player_stats[player_stats['play3PA'] > 0]
# Clean up the player names, placing them in a single column
three_takers['name'] = [f'{p["playFNm"]} {p["playLNm"]}' 
 for _, p in three_takers.iterrows()]
# Aggregate the total three-point attempts and makes for each player
three_takers = (three_takers.groupby('name')
 .sum()
 .loc[:,['play3PA', 'play3PM']]
 .sort_values('play3PA', ascending=False))
# Filter out anyone who didn't take at least 100 three-point shots
three_takers = three_takers[three_takers['play3PA'] >= 100].reset_index()
# Add a column with a calculated three-point percentage (made/attempted)
three_takers['pct3PM'] = three_takers['play3PM'] / three_takers['play3PA']

以下是结果的示例DataFrame:

>>> three_takers.sample(5)
 name play3PA play3PM pct3PM
229 Corey Brewer 110 31 0.281818
78 Marc Gasol 320 109 0.340625
126 Raymond Felton 230 81 0.352174
127 Kristaps Porzi??is 229 90 0.393013
66 Josh Richardson 336 127 0.377976

假设您想要在分布中选择一组玩家,并且这样做会使代表未选择玩家的字形颜色静音:

# Bokeh Libraries
from bokeh.plotting import figure, show
from bokeh.io import output_file
from bokeh.models import ColumnDataSource, NumeralTickFormatter
# Output to file
output_file('three-point-att-vs-pct.html',
 title='Three-Point Attempts vs. Percentage')
# Store the data in a ColumnDataSource
three_takers_cds = ColumnDataSource(three_takers)
# Specify the selection tools to be made available
select_tools = ['box_select', 'lasso_select', 'poly_select', 'tap', 'reset']
# Create the figure
fig = figure(plot_height=400,
 plot_width=600,
 x_axis_label='Three-Point Shots Attempted',
 y_axis_label='Percentage Made',
 title='3PT Shots Attempted vs. Percentage Made (min. 100 3PA), 2017-18',
 toolbar_location='below',
 tools=select_tools)
# Format the y-axis tick labels as percentages
fig.yaxis[0].formatter = NumeralTickFormatter(format='00.0%')
# Add square representing each player
fig.square(x='play3PA',
 y='pct3PM',
 source=three_takers_cds,
 color='royalblue',
 selection_color='deepskyblue',
 nonselection_color='lightgray',
 nonselection_alpha=0.3)
# Visualize
show(fig)

首先,指定要提供的选择工具。在上面的例子中,'box_select','lasso_select','poly_select',和'tap'(加一个复位按钮)在一个被称为列表中指定select_tools。实例化图形时,工具栏将定位'below'到图表中,并传递列表tools以使上面选择的工具可用。

每个玩家最初由皇家蓝色方形字形表示,但是当选择一个玩家或一组玩家时,设置以下配置:

  • 将所选的播放器转到 deepskyblue
  • 将所有未选中的玩家的字形更改为lightgray具有0.3不透明度的颜色

而已!只需几个快速添加,可视化现在看起来像这样:

有关选择时可以执行的操作的更多信息,请查看选定和未选择的字形。

添加悬停操作

因此,能够选择在我的散点图中感兴趣的特定玩家数据点,但是如果你想快速查看字形所代表的单个玩家怎么办?一种选择是HoverTool()当光标与带有字形的路径交叉时,使用Bokeh 来显示工具提示。您需要做的就是将以下内容附加到上面的代码段:

# Bokeh Library
from bokeh.models import HoverTool
# Format the tooltip
tooltips = [
 ('Player','@name'),
 ('Three-Pointers Made', '@play3PM'),
 ('Three-Pointers Attempted', '@play3PA'),
 ('Three-Point Percentage','@pct3PM{00.0%}'),
 ]
# Add the HoverTool to the figure
fig.add_tools(HoverTool(tooltips=tooltips))
# Visualize
show(fig)

这HoverTool()与您在上面看到的选择工具略有不同,特别是它具有属性tooltips。

首先,您可以通过创建包含描述和引用的元组列表来配置格式化的工具提示ColumnDataSource。此列表作为输入传递给HoverTool(),然后使用简单地添加到图中add_tools()。这是发生的事情:

请注意,在工具栏中添加了悬停按钮,可以打开和关闭。

如果你想进一步强调悬停的玩家,Bokeh可以通过悬停检查实现这一点。以下是添加工具提示的代码段的略微修改版本

# Format the tooltip
tooltips = [
 ('Player','@name'),
 ('Three-Pointers Made', '@play3PM'),
 ('Three-Pointers Attempted', '@play3PA'),
 ('Three-Point Percentage','@pct3PM{00.0%}'),
 ]
# Configure a renderer to be used upon hover
hover_glyph = fig.circle(x='play3PA', y='pct3PM', source=three_takers_cds,
 size=15, alpha=0,
 hover_fill_color='black', hover_alpha=0.5)
# Add the HoverTool to the figure
fig.add_tools(HoverTool(tooltips=tooltips, renderers=[hover_glyph]))
# Visualize
show(fig)

这是通过创建一个全新的字形来完成的,在这种情况下是圆形而不是方形,并将其分配给hover_glyph。请注意,初始不透明度设置为零,以便在光标触摸它之前它是不可见的。悬停时显示的属性通过设置hover_alpha为0.5同时捕获hover_fill_color。

现在,当您将鼠标悬停在各种标记上时,您会看到原始方块上出现一个小黑圈:

要进一步了解其功能HoverTool(),请参阅HoverTool和Hover Inspections指南。

链接轴和选择

链接是在布局中同步不同可视化元素的过程。例如,您可能希望链接多个图的轴,以确保如果放大一个图则反映在另一个图上。让我们看看它是如何完成的。

对于此示例,可视化将能够平移到团队计划的不同部分并检查各种游戏统计数据。每个统计数据将由它自己的情节以2×2表示gridplot()。

可以从team_statsDataFrame 收集数据,选择Philadelphia 76ers作为感兴趣的团队:

# Isolate relevant data
phi_gm_stats = (team_stats[(team_stats['teamAbbr'] == 'PHI') & 
 (team_stats['seasTyp'] == 'Regular')]
 .loc[:, ['gmDate', 
 'teamPTS', 
 'teamTRB', 
 'teamAST', 
 'teamTO', 
 'opptPTS',]]
 .sort_values('gmDate'))
# Add game number
phi_gm_stats['game_num'] = range(1, len(phi_gm_stats)+1)
# Derive a win_loss column
win_loss = []
for _, row in phi_gm_stats.iterrows():
 # If the 76ers score more points, it's a win
 if row['teamPTS'] > row['opptPTS']:
 win_loss.append('W')
 else:
 win_loss.append('L')
# Add the win_loss data to the DataFrame
phi_gm_stats['winLoss'] = win_loss

以下是76人前5场比赛的结果:

>>> phi_gm_stats.head()
 gmDate teamPTS teamTRB teamAST teamTO opptPTS game_num winLoss
10 2017-10-18 115 48 25 17 120 1 L
39 2017-10-20 92 47 20 17 102 2 L
52 2017-10-21 94 41 18 20 128 3 L
80 2017-10-23 97 49 25 21 86 4 W
113 2017-10-25 104 43 29 16 105 5 L

首先导入必要的Bokeh库,指定输出参数,然后将数据读入ColumnDataSource:

# Bokeh Libraries
from bokeh.plotting import figure, show
from bokeh.io import output_file
from bokeh.models import ColumnDataSource, CategoricalColorMapper, Div
from bokeh.layouts import gridplot, column
# Output to file
output_file('phi-gm-linked-stats.html',
 title='76ers Game Log')
# Store the data in a ColumnDataSource
gm_stats_cds = ColumnDataSource(phi_gm_stats)

每个游戏都由一个列表示,如果结果为胜利则为绿色,为亏损则为红色。为此,CategoricalColorMapper可以使用Bokeh 来将数据值映射到指定的颜色:

# Create a CategoricalColorMapper that assigns a color to wins and losses
win_loss_mapper = CategoricalColorMapper(factors = ['W', 'L'], 
 palette=['green', 'red'])

对于此用例,将指定要映射的分类数据值factors的列表传递给具有预期颜色的列表palette。有关详细信息CategoricalColorMapper,请参阅Bokeh用户指南中处理分类数据的颜色部分。

在2比2中可以看到四个数据gridplot:得分,助攻,篮板和失误。在创建四个图形并配置其各自的图表时,属性中存在大量冗余。因此,为了简化代码,for可以使用循环

# Create a dict with the stat name and its corresponding column in the data
stat_names = {'Points': 'teamPTS',
 'Assists': 'teamAST',
 'Rebounds': 'teamTRB',
 'Turnovers': 'teamTO',}
# The figure for each stat will be held in this dict
stat_figs = {}
# For each stat in the dict
for stat_label, stat_col in stat_names.items():
 # Create a figure
 fig = figure(y_axis_label=stat_label, 
 plot_height=200, plot_width=400,
 x_range=(1, 10), tools=['xpan', 'reset', 'save'])
 # Configure vbar
 fig.vbar(x='game_num', top=stat_col, source=gm_stats_cds, width=0.9, 
 color=dict(field='winLoss', transform=win_loss_mapper))
 # Add the figure to stat_figs dict
 stat_figs[stat_label] = fig

正如您所看到的,需要调整的唯一参数是y-axis-label图中的数据和将top在其中指定的数据vbar。这些值很容易存储在dict迭代中,以创建每个统计数据。

您还可以CategoricalColorMapper在vbar字形配置中看到该实现。该color属性dict与ColumnDataSource要映射的字段和CategoricalColorMapper上面创建的名称一起传递。

最初的观点只会显示76人队赛季的前10场比赛,因此需要有一种方法可以横向平移以在本赛季的其他比赛中进行导航。因此,将工具栏配置为具有xpan工具允许在整个绘图中平移,而不必担心沿垂直轴意外地倾斜视图。

现在创建了数字,gridplot可以通过引用dict上面创建的数字来设置:

# Create layout
grid = gridplot([[stat_figs['Points'], stat_figs['Assists']], 
 [stat_figs['Rebounds'], stat_figs['Turnovers']]])

链接四个图的轴就像设置x_range每个图相互相等一样简单:

# Link together the x-axes
stat_figs['Points'].x_range = \
 stat_figs['Assists'].x_range = \
 stat_figs['Rebounds'].x_range = \
 stat_figs['Turnovers'].x_range

要向可视化添加标题栏,您可以尝试在点图上执行此操作,但它可能仅限于该图的空间。因此,一个很好的技巧是使用Bokeh的能力来解释HTML以插入Div包含标题信息的元素。一旦被创建,简单地结合起来,与将gridplot()在column布局:

# Add a title for the entire visualization using Div
html = """<h3>Philadelphia 76ers Game Log</h3>
<b><i>2017-18 Regular Season</i>
<br>
</b><i>Wins in green, losses in red</i>
"""
sup_title = Div(text=html)
# Visualize
show(column(sup_title, grid))

将所有部分放在一起会产生以下结果:

同样,您可以轻松实现链接选择,其中一个绘图上的选择将反映在其他绘图上。

为了了解这是如何工作的,下一个可视化将包含两个散点图:一个显示76人的两分与三分投篮命中率,另一个显示76人队的分数与比赛中的对手分数基础。

目标是能够在左侧散点图上选择数据点,并能够快速识别右侧散点图上的相应数据点是赢还是输。

此可视化的DataFrame与第一个示例的DataFrame非常相似:

# Isolate relevant data
phi_gm_stats_2 = (team_stats[(team_stats['teamAbbr'] == 'PHI') & 
 (team_stats['seasTyp'] == 'Regular')]
 .loc[:, ['gmDate', 
 'team2P%', 
 'team3P%', 
 'teamPTS', 
 'opptPTS']]
 .sort_values('gmDate'))
# Add game number
phi_gm_stats_2['game_num'] = range(1, len(phi_gm_stats_2) + 1)
# Derive a win_loss column
win_loss = []
for _, row in phi_gm_stats_2.iterrows():
 # If the 76ers score more points, it's a win
 if row['teamPTS'] > row['opptPTS']:
 win_loss.append('W')
 else:
 win_loss.append('L')
# Add the win_loss data to the DataFrame
phi_gm_stats_2['winLoss'] = win_loss

这是数据的样子:

>>> phi_gm_stats_2.head()
 gmDate team2P% team3P% teamPTS opptPTS game_num winLoss
10 2017-10-18 0.4746 0.4286 115 120 1 L
39 2017-10-20 0.4167 0.3125 92 102 2 L
52 2017-10-21 0.4138 0.3333 94 128 3 L
80 2017-10-23 0.5098 0.3750 97 86 4 W
113 2017-10-25 0.5082 0.3333 104 105 5 L

创建可视化的代码如下

# Bokeh Libraries
from bokeh.plotting import figure, show
from bokeh.io import output_file
from bokeh.models import ColumnDataSource, CategoricalColorMapper, NumeralTickFormatter
from bokeh.layouts import gridplot
# Output inline in the notebook
output_file('phi-gm-linked-selections.html',
 title='76ers Percentages vs. Win-Loss')
# Store the data in a ColumnDataSource
gm_stats_cds = ColumnDataSource(phi_gm_stats_2)
# Create a CategoricalColorMapper that assigns specific colors to wins and losses
win_loss_mapper = CategoricalColorMapper(factors = ['W', 'L'], palette=['Green', 'Red'])
# Specify the tools
toolList = ['lasso_select', 'tap', 'reset', 'save']
# Create a figure relating the percentages
pctFig = figure(title='2PT FG % vs 3PT FG %, 2017-18 Regular Season',
 plot_height=400, plot_width=400, tools=toolList,
 x_axis_label='2PT FG%', y_axis_label='3PT FG%')
# Draw with circle markers
pctFig.circle(x='team2P%', y='team3P%', source=gm_stats_cds, 
 size=12, color='black')
# Format the y-axis tick labels as percenages
pctFig.xaxis[0].formatter = NumeralTickFormatter(format='00.0%')
pctFig.yaxis[0].formatter = NumeralTickFormatter(format='00.0%')
# Create a figure relating the totals
totFig = figure(title='Team Points vs Opponent Points, 2017-18 Regular Season',
 plot_height=400, plot_width=400, tools=toolList,
 x_axis_label='Team Points', y_axis_label='Opponent Points')
# Draw with square markers
totFig.square(x='teamPTS', y='opptPTS', source=gm_stats_cds, size=10,
 color=dict(field='winLoss', transform=win_loss_mapper))
# Create layout
grid = gridplot([[pctFig, totFig]])
# Visualize
show(grid)

这是使用a的强大功能ColumnDataSource。只要字形渲染器(在这种情况下,circle百分比的字形和square胜负的字形)共享相同ColumnDataSource,则默认情况下将链接选择。

以下是它在行动中的表现,您可以看到在任何一个图上所做的选择将反映在另一个上:

通过选择左散点图的右上象限中的数据点的随机样本,对应于高两点和三点场目标百分比的数据点,右侧散点图上的数据点被突出显示。

类似地,在左散点图上选择右侧散点图上与损失相对应的数据点往往更靠左下方,较低的射击百分比。

有关链接图的所有详细信息,请参阅Bokeh用户指南中的链接图。

Bokeh 网站文档https://bokeh.pydata.org/en/latest/docs/reference.html

本文结束,下一章,请关注头条号,查看系列

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言