Python:matplotlib 和 Seaborn 之热图、小提琴图和箱线图 (三十四)

热图

热图是直方图的二维版本,可以替代散点图。和散点图一样,要绘制的两个数字变量的值位于坐标轴上。和直方图类似,图形区域被划分为网格,并将每个网格的点数加起来。因为没有空间表示长条高度,因此用网格颜色表示计数。你可以通过 Matplotlib 的 hist2d 函数实现热图。

plt.figure(figsize = [12, 5])

# left plot: scatterplot of discrete data with jitter and transparency
plt.subplot(1, 2, 1)
sb.regplot(data = df, x = 'disc_var1', y = 'disc_var2', fit_reg = False,
           x_jitter = 0.2, y_jitter = 0.2, scatter_kws = {'alpha' : 1/3})

# right plot: heat map with bin edges between values
plt.subplot(1, 2, 2)
bins_x = np.arange(0.5, 10.5+1, 1)
bins_y = np.arange(-0.5, 10.5+1, 1)
plt.hist2d(data = df, x = 'disc_var1', y = 'disc_var2',
           bins = [bins_x, bins_y])
plt.colorbar();

注意,因为有两个变量,因此 "bins" 参数接受了包含两个分箱边缘规格的列表,每个维度一个规格。和单变量直方图一样,选择合适的分箱尺寸很重要。我们添加了 colorbar 函数调用,以向图形的一侧添加色条,显示从计数到颜色的映射。
file

随着热图中的颜色越来越亮,从蓝色变成黄色,相应单元格中的点计数越来越高。

热图还可以用作条形图的二维版本,按照两个分类变量(而不是数字变量)的计数绘制图形。seaborn 中的函数 heatmap 专门用于绘制分类热图。稍后我们将在这节课的“聚类条形图”部分详细讲解这方面的知识。

其他版本

要选择其他调色板,可以在 hist2d 中设置 "cmap" 参数。设置调色板的最简单方式是使用字符串引用内置 Matplotlib 调色板。你可以在 Pyplot API 文档的此部分找到有效字符串列表。下节课将详细讨论图形中的颜色。暂时我将通过一个示例介绍如何通过设置 cmap = 'viridis_r' 更改默认的 "viridis" 调色板。

此外,我想区分计数为零的单元格和计数非零的单元格。"cmin" 参数指定了单元格要达到什么样的最低值才能绘制出来。在 hist2d 调用中添加 cmin = 0.5 参数后,只有至少包含一个数据点的单元格才会有颜色。

bins_x = np.arange(0.5, 10.5+1, 1)
bins_y = np.arange(-0.5, 10.5+1, 1)
plt.hist2d(data = df, x = 'disc_var1', y = 'disc_var2',
           bins = [bins_x, bins_y], cmap = 'viridis_r', cmin = 0.5)
plt.colorbar()

file

如果你有大量数据,可能需要向图形中的单元格添加注释,表示每个单元格的点数。在 hist2d 调用中,我们必须挨个地添加文本元素,就像在上节课挨个地向条形图中添加文本元素一样。我们可以直接通过 hist2d 返回的结果得出要注释的计数,该函数返回的结果不仅包括图形对象,还包括计数数组和两个分箱边缘向量。

# hist2d returns a number of different variables, including an array of counts
bins_x = np.arange(0.5, 10.5+1, 1)
bins_y = np.arange(-0.5, 10.5+1, 1)
h2d = plt.hist2d(data = df, x = 'disc_var1', y = 'disc_var2',
               bins = [bins_x, bins_y], cmap = 'viridis_r', cmin = 0.5)
counts = h2d[0]

# loop through the cell counts and add text annotations for each
for i in range(counts.shape[0]):
    for j in range(counts.shape[1]):
        c = counts[i,j]
        if c >= 7: # increase visibility on darkest cells
            plt.text(bins_x[i]+0.5, bins_y[j]+0.5, int(c),
                     ha = 'center', va = 'center', color = 'white')
        elif c > 0:
            plt.text(bins_x[i]+0.5, bins_y[j]+0.5, int(c),
                     ha = 'center', va = 'center', color = 'black')

file

如果热图中有太多的单元格,注释将太多,无法看清。在这种情形下,建议不要添加注释,直接通过数据和色条传达信息。通常你会在单元格很少的分类热图中看到注释。实际上,seaborn 的 heatmap 函数中内置了一个添加注释的参数,稍后我们将讲解。

小提琴图

我们可以通过几种方式绘制一个定量变量和另一个定量变量之间的关系,并表示不同抽象级别的数据。小提琴图是较低级别的抽象。对于分类变量的每个级别,都会绘制数字变量的值分布情况。分布情况绘制为核密度估计,有点像平滑的直方图。上节课结尾的补充内容详细讲解了核密度估计。

Seaborn 的 violinplot 函数可以用来创建将小提琴图和箱线图相结合的图形,我们将在下个页面讨论箱线图。

sb.violinplot(data = df, x = 'cat_var', y = 'num_var')

file

在此图中可以看出,数字数据在每个分类级别都具有不同的形状:α 组呈双峰,β 组的变化相对更明显,γ 组和 δ 组分别呈负偏态和正偏态。还可以看出,每个级别都以不同的颜色呈现,就像上节课的普通 countplot 一样。如果没必要区分颜色,我们可以使用 "color" 参数使所有曲线的颜色都一样。

在每个曲线内,有一个黑色形状,其中包含一个白点。这就是上面提到的箱线图的迷你版。下个页面将详细讨论箱线图。如果你想删除箱线图,可以在 violinplot 调用中设置 inner = None 参数,使最终的图形看起来更简单。

base_color = sb.color_palette()[0]
sb.violinplot(data = df, x = 'cat_var', y = 'num_var', color = base_color,
              inner = None)

file

其他版本

就像条形图可以呈现为横条一样,小提琴图也可以水平呈现。Seaborn 很机智,可以根据是 "x" 还是 "y" 接受的分类变量来推断按照哪个屏幕方向绘制图形。但是如果两个变量都是数字(例如一个是离散值),则可以使用“orient”参数指定图形方向。

base_color = sb.color_palette()[0]
sb.violinplot(data = df, x = 'num_var', y = 'cat_var', color = base_color,
              inner = None)

file

箱线图

线图是展示数字变量和分类变量之间关系的另一种方式。与小提琴图相比,箱线图更侧重于数据的摘要,主要报告每个分类级别数字值的一组描述统计学信息。可以使用 seabornboxplot 函数创建箱线图。

plt.figure(figsize = [10, 5])
base_color = sb.color_palette()[0]

# left plot: violin plot
plt.subplot(1, 2, 1)
ax1 = sb.violinplot(data = df, x = 'cat_var', y = 'num_var', color = base_color)

# right plot: box plot
plt.subplot(1, 2, 2)
sb.boxplot(data = df, x = 'cat_var', y = 'num_var', color = base_color)
plt.ylim(ax1.get_ylim()) # set y-axis limits to be same as left plot

注意,这里使用了 "color" 参数使每个方框的颜色一样。为了更好地比较小提琴图和箱线图,我们在第二个图形上添加了 ylim 表达式,使两个图形的 y 轴范围相符。我们将 violinplot返回的 Axes 对象分配给一个变量,并使用 ax1 以程序方式获得这些范围值。

file

小提琴图的内部方框和线条对应于箱线图的方框和须线。在箱线图中,方框的中间线条表示分布的中值,方框的顶部和底部分别表示数据的第三四分位数和第一四分位数。因此,方框的高度是四分位差 (IQR)。从方框的顶部和底部,须线表示从第一四分位数或第三四分位数到最低值或最大值的范围。通常,须线长度会设置最大范围;默认情况下,设为 1.5 乘以 IQR。对于 γ 级别,底部须线下方有数据,表示在第一四分位数下方有大于 1.5x IQR 的单个离群值。

对比两个图形后发现,箱线图对数据的总结比小提琴图要整洁。使用箱线图更容易比较不同组的统计值。如果你有很多组数据要比较,或者要构建解释性图形,则箱线图更值得考虑。可以从箱线图中清晰地看出,δ 组的中值最低。另一方面,箱线图对数据分布的细节描绘没有小提琴图清晰:无法在 α 级别的值中看到微弱的双峰性。小提琴图可能更适合探索数据,尤其因为 seaborn 还默认地包含箱线图实现。

其他版本

和 violinplot 一样,boxplot 也可以水平地呈现箱线图,将数字和分类特征设为相应的参数即可。

base_color = sb.color_palette()[0]
sb.boxplot(data = df, x = 'num_var', y = 'cat_var', color = base_color)

file

在 violinplot 中,除了默认的迷你箱线图之外,还有一个在小提琴图中绘制摘要统计信息的其他选项。通过设置 inner = 'quartile’,将在三个中间四分位数的小提琴区域绘制三条线。很粗的破折线表示中值,分别位于两侧的两个破折线更短的线条表示第一四分位数和第三四分位数。

base_color = sb.color_palette()[0]
sb.violinplot(data = df, x = 'cat_var', y = 'num_var', color = base_color,
              inner = 'quartile')

file

为者常成,行者常至