暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Bokeh教程:条形图和分类数据绘图

旺德福居 2020-06-01
479

本文翻译自 bokeh/bokeh-notebooks 项目,并经过修改。

from bokeh.io import output_notebook, show
from bokeh.plotting import figure
output_notebook()

基本条形图

条形图是常见且重要的绘图类型。通过 Bokeh,可以轻松创建各种堆积或嵌套的条形图,并通常处理分类数据。

下面的示例显示了一个简单的条形图,它使用 vbar
 方法创建,用于绘制垂直条形图。(有一个相应的 hbar
 对应水平条。) 我们还设置了一些绘图属性以使图表看起来更好,有关视觉属性的信息,请参见《Bokeh教程:样式和主题》。

# 下面是分类数值
fruits = [
'Apples',
'Pears',
'Nectarines',
'Plums',
'Grapes',
'Strawberries',
]

# 将 x_range 设置为上面的分类
p = figure(
x_range=fruits,
plot_height=250,
title="Fruit Counts"
)

# 分类数据同样可以用于坐标
p.vbar(
x=fruits,
top=[5, 3, 4, 2, 4, 6],
width=0.9
)

# 设置属性,让绘图更美观
p.xgrid.grid_line_color = None
p.y_range.start = 0

show(p)

当我们想创建一个具有分类范围的图时,我们将分类值的有序列表传递给 figure
,例如 x_range = ['a', 'b', 'c']
。在上面的图中,我们将水果列表传递给 x_range
,我们可以看到这些水果被设置为 x 轴。

vbar
 glyph 方法在需要一个定位条形中心的 x 位置,一个 top
,一个 bottom
(默认为0),和一个 width
。当我们像这里这样使用分类范围时,每个类别的宽度隐式为 1,因此,如我们在此处设置的那样,设置 width = 0.9
 会使条形图彼此分开。(另一种选择是在范围内添加一些间隔。)

练习

创建你自己的简单条形图

由于 vbar
 是一种 glyph 方法,因此我们可以将其与 ColumnDataSource
 一起使用,就像我们可以与任何其他 glyph 一样。在下面的示例中,我们将数据(包括颜色数据)放入 ColumnDataSource
 中,并使用它来驱动绘图。我们还添加了图例,有关图例和其他标注的更多信息,请参见 《Bokeh教程:添加标注》。

from bokeh.models import ColumnDataSource
from bokeh.palettes import Spectral6

fruits = [
'Apples',
'Pears',
'Nectarines',
'Plums',
'Grapes',
'Strawberries'
]
counts = [5, 3, 4, 2, 4, 6]

source = ColumnDataSource(
data=dict(
fruits=fruits,
counts=counts,
color=Spectral6
)
)

p = figure(
x_range=fruits,
plot_height=250,
y_range=(0, 9),
title="Fruit Counts"
)
p.vbar(
x='fruits',
top='counts',
width=0.9,
color='color',
legend_field="fruits",
source=source
)

p.xgrid.grid_line_color = None
p.legend.orientation = "horizontal"
p.legend.location = "top_center"

show(p)

练习

创建自己的使用 ColumnDataSource
 驱动的简单条形图

堆叠条形图

通常需要将条码堆叠在一起。Bokeh使用 vbar_stack
 和 hbar_stack
 方法使这一过程变得简单。将数据传递给这些方法之一时,堆栈中的每个“行”都应在数据源中对应一个序列。您将提供一个列名称的有序列表,以从数据源中读取并堆叠在一起。

在下面的示例中,我们看到使用两次调用 hbar_stack
 堆叠的水果出口(正值)和进口(负值)的模拟数据。每年各列中的值是根据 fruites
 排序的,即,这不是“整洁”的数据格式。

from bokeh.palettes import GnBu3, OrRd3

years = ['2015', '2016', '2017']

exports = {
'fruits' : fruits,
'2015' : [2, 1, 4, 3, 2, 4],
'2016' : [5, 3, 4, 2, 4, 6],
'2017' : [3, 2, 4, 4, 5, 3]
}
imports = {
'fruits' : fruits,
'2015' : [-1, 0, -1, -3, -2, -1],
'2016' : [-2, -1, -3, -1, -2, -2],
'2017' : [-1, -2, -1, 0, -2, -2]
}

p = figure(
y_range=fruits,
plot_height=250,
x_range=(-16, 16),
title="Fruit import/export, by year"
)

p.hbar_stack(
years,
y='fruits',
height=0.9,
color=GnBu3,
source=ColumnDataSource(exports),
legend_label=["%s exports" % x for x in years]
)

p.hbar_stack(
years,
y='fruits',
height=0.9,
color=OrRd3,
source=ColumnDataSource(imports),
legend_label=["%s imports" % x for x in years]
)

p.y_range.range_padding = 0.1
p.ygrid.grid_line_color = None
p.legend.location = "center_left"

show(p)

请注意,我们还通过指定以下内容在分类范围(例如,轴的两端)周围 添加了一些填充

p.y_range.range_padding = 0.1

练习

创建堆叠条形图,使用单次 vbar_stack
 调用

分组条形图

有时我们希望将条形图分组在一起,而不是将它们堆叠在一起。Bokeh 最多可以处理三个级别的嵌套(层次)类别,并将根据最外面的级别自动对输出进行分组。要指定网状分类坐标,数据源的列应包含元组,例如:

x = [
("Apples", "2015"),
("Apples", "2016"),
("Apples", "2017"),
("Pears", "2015),
...
]

其他列中的值对应于 x
 中的每个项目,与其他情况完全相同。当使用这些类型的嵌套坐标进行绘制时,我们必须通过将 FactorRange
 显式传递给 figure
 来告知 Bokeh 内容并排序轴范围。在下面的示例中,可以看到下面的代码

p = figure(x_range=FactorRange(*x), ....)

from bokeh.models import FactorRange

fruits = [
'Apples',
'Pears',
'Nectarines',
'Plums',
'Grapes',
'Strawberries'
]
years = [
'2015',
'2016',
'2017'
]

data = {
'fruits' : fruits,
'2015' : [2, 1, 4, 3, 2, 4],
'2016' : [5, 3, 3, 2, 4, 6],
'2017' : [3, 2, 4, 4, 5, 3]
}

# this creates:
# [
# ("Apples", "2015"),
# ("Apples", "2016"),
# ("Apples", "2017"),
# ("Pears", "2015),
# ...
# ]
x = [ (fruit, year) for fruit in fruits for year in years ]
counts = sum(
zip(
data['2015'],
data['2016'],
data['2017']
),
()
) # 类似 hstack

source = ColumnDataSource(
data=dict(
x=x,
counts=counts
)
)

p = figure(
x_range=FactorRange(*x),
plot_height=250,
title="Fruit Counts by Year"
)

p.vbar(
x='x',
top='counts',
width=0.9,
source=source
)

p.y_range.start = 0
p.x_range.range_padding = 0.1
p.xaxis.major_label_orientation = 1
p.xgrid.grid_line_color = None

show(p)

练习

通过为 ColumnDataSource
 添加颜色,使上面的图表每年具有不同的颜色

我们可以设置条形颜色的另一种方法是使用变换。我们在上一章 《Bokeh教程:数据源和转换》 中首先看到了一些转换。在这里,我们使用一个新的 factor_cmap
,它接受要用于颜色映射的列的名称,以及定义颜色映射的调色板和因子。

另外,如果需要,我们可以将其配置为仅映射子因子。例如,在这种情况下,我们不想给每个 (fruit, year)
 对的颜色不同。取而代之的是,我们只希望基于 year
 进行着色。因此,我们传递 start = 1
 和 end = 2
 来指定颜色映射时要使用的每个因子的范围。然后我们将结果传递为 fill_color
 值:

fill_color=factor_cmap(
'x',
palette=['firebrick', 'olive', 'navy'],
factors=years,
start=1,
end=2
)

from bokeh.transform import factor_cmap

p = figure(
x_range=FactorRange(*x),
plot_height=250,
title="Fruit Counts by Year"
)

p.vbar(
x='x',
top='counts',
width=0.9,
source=source,
line_color="white",

# 使用调色板基于 x[1:2] 值进行颜色映射
fill_color=factor_cmap(
'x',
palette=['firebrick', 'olive', 'navy'],
factors=years,
start=1,
end=2
)
)

p.y_range.start = 0
p.x_range.range_padding = 0.1
p.xaxis.major_label_orientation = 1
p.xgrid.grid_line_color = None

show(p)

也可以使用另一种称为 “视觉闪避(visual dodge)” 的技术来实现分组条形图。如果您只想用水果类型标记轴,而不在轴上包括年份,该技术会很有用。本教程不涉及该技术,但是您可以在 用户指南 中找到信息。

https://bokeh.pydata.org/en/dev/docs/user_guide/categorical.html#visual-dodge

混合分类级别

如果您使用上述嵌套类别创建了范围,则可以根据需要仅使用“外部”类别来绘制 glyphs。下图显示了按季度划分的月度值条形图。这些数据的格式为:

factors = [("Q1", "jan"), ("Q1", "feb"), ("Q1", "mar"), ....]

该图还覆盖了代表平均季度值的线,这可以通过仅使用每个下一个类别的“季度”部分来完成:

p.line(x=["Q1", "Q2", "Q3", "Q4"], y=....)

factors = [
("Q1", "jan"), ("Q1", "feb"), ("Q1", "mar"),
("Q2", "apr"), ("Q2", "may"), ("Q2", "jun"),
("Q3", "jul"), ("Q3", "aug"), ("Q3", "sep"),
("Q4", "oct"), ("Q4", "nov"), ("Q4", "dec")
]

p = figure(
x_range=FactorRange(*factors),
plot_height=250
)

x = [
10, 12, 16,
9, 10, 8,
12, 13, 14,
14, 12, 16
]
p.vbar(
x=factors,
top=x,
width=0.9,
alpha=0.5
)

qs, aves = ["Q1", "Q2", "Q3", "Q4"], [12, 9, 13, 14]
p.line(
x=qs,
y=aves,
color="red",
line_width=3
)
p.circle(
x=qs,
y=aves,
line_color="red",
fill_color="white",
size=10
)

p.y_range.start = 0
p.x_range.range_padding = 0.1
p.xgrid.grid_line_color = None

show(p)

使用 Pandas GroupBy

我们可能要根据 “group by” 操作的结果制作图表。Bokeh 可以直接利用 Pandas GroupBy
 对象来简化此过程。让我们通过检查“汽车”数据集来了解 Bokeh 如何处理 GroupBy
 对象。

from bokeh.sampledata.autompg import autompg_clean as df

df.cyl = df.cyl.astype(str)
df.head()

假设我们要显示一些根据 "cyl"
 分组的值。如果我们创建 df.groupby(('cyl'))
 然后调用 group.describe()
,我们可以看到 Pandas 自动为每个组计算各种统计信息。

group = df.groupby(('cyl'))

group.describe()

Bokeh 允许我们直接从 Pandas 的 GroupBy
 对象创建一个 ColumnDataSource
,当发生这种情况时,数据源将自动填充来自 group.desribe()
 的摘要值。观察下面的列名,它们与上面的输出相对应。

source = ColumnDataSource(group)

",".join(source.column_names)

‘cyl,mpg_count,mpg_mean,mpg_std,mpg_min,mpg_25%,mpg_50%,mpg_75%,mpg_max,displ_count,displ_mean,displ_std,displ_min,displ_25%,displ_50%,displ_75%,displ_max,hp_count,hp_mean,hp_std,hp_min,hp_25%,hp_50%,hp_75%,hp_max,weight_count,weight_mean,weight_std,weight_min,weight_25%,weight_50%,weight_75%,weight_max,accel_count,accel_mean,accel_std,accel_min,accel_25%,accel_50%,accel_75%,accel_max,yr_count,yr_mean,yr_std,yr_min,yr_25%,yr_50%,yr_75%,yr_max’

了解了这些列名后,我们可以立即基于 Pandas GroupBy
 对象创建条形图。下面的示例绘制了每个气缸的平均 MPG,即 "mpg_mean"
 列与 "cyl"
 列

from bokeh.palettes import Spectral5

cyl_cmap = factor_cmap(
'cyl',
palette=Spectral5,
factors=sorted(df.cyl.unique())
)

p = figure(
plot_height=350,
x_range=group)
p.vbar(
x='cyl',
top='mpg_mean',
width=1,
line_color="white",
fill_color=cyl_cmap,
source=source
)

p.xgrid.grid_line_color = None
p.xaxis.axis_label = "number of cylinders"
p.yaxis.axis_label = "Mean MPG"
p.y_range.start = 0

show(p)

练习

使用相同的数据集按起点绘制相似的平均马力(hp)图

分类散点图

到目前为止,我们已经看到分类数据与各种条形 glyphs 一起使用。但是 Bokeh 可以对大多数 glyphs 使用分类坐标。让我们创建在一个轴上具有分类坐标的散点图。 commits
 数据集只包含一系列 GitHub commit 的日期时间。已经添加了其他列来表示每次提交的日期和时间。

from bokeh.sampledata.commits import data

data.head()

要创建散点图,我们像以前一样将类别列表作为范围传递

p = figure(y_range=DAYS, ...)

然后,我们可以为每次提交绘制圆点,其中 "time"
 驱动 x 坐标,而 "day"
 驱动 y 坐标。

p.circle(x='time', y='day', ...)

为了使这些值更容易区分,我们还可以向 y 坐标添加一个 jitter
 变换,如下面的完整示例所示。

from bokeh.transform import jitter

DAYS = ['Sun', 'Sat', 'Fri', 'Thu', 'Wed', 'Tue', 'Mon']

source = ColumnDataSource(data)

p = figure(
plot_width=800,
plot_height=300,
y_range=DAYS,
x_axis_type='datetime',
title="Commits by Time of Day (US/Central) 2012—2016"
)

p.circle(
x='time',
y=jitter(
'day',
width=0.6,
range=p.y_range
),
source=source,
alpha=0.3
)

p.xaxis[0].formatter.days = ['%Hh']
p.x_range.range_padding = 0
p.ygrid.grid_line_color = None

show(p)

练习

使用分类坐标和任何非 “bar” glyphs 创建图

参考

https://bokeh.org

https://github.com/bokeh/bokeh-notebooks

Bokeh教程:数据源和转换

Bokeh教程:样式和主题

Bokeh教程:添加标注



题图由 Couleur 在 Pixabay 上发布。
文章转载自旺德福居,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论