数据聚合、汇总和可视化是支撑数据分析领域的三大支柱。长久以来,数据可视化都是一个强有力的工具,被业界广泛使用,却受限于 2 维。在本文中,作者将探索一些有效的多维数据可视化策略(范围从 1 维到 6 维)。
一、可视化介绍
描述性分析(descriptive analytics)是任何分析生命周期的数据科学项目或特定研究的核心组成部分之一。数据聚合(aggregation)、汇总(summarization)和可视化(visualization)是支撑数据分析领域的主要支柱。从传统商业智能(Business Intelligence)开始,甚至到如今人工智能时代,数据可视化都是一个强有力的工具;由于其能有效抽取正确的信息,同时清楚容易地理解和解释结果,可视化被业界组织广泛使用。然而,处理多维数据集(通常具有 2 个以上属性)开始引起问题,因为我们的数据分析和通信的媒介通常限于 2 个维度。在本文中,我们将探索一些有效的多维数据可视化策略(范围从 1 维到 6 维)。
二、可视化动机
「一图胜千言」
这是一句我们熟悉的非常流行的英语习语,可以充当将数据可视化作为分析的有效工具的灵感和动力。永远记住:「有效的数据可视化既是一门艺术,也是一门科学。」在开始之前,我还要提及下面一句非常相关的引言,它强调了数据可视化的必要性。
「一张图片的最大价值在于,它迫使我们注意到我们从未期望看到的东西。」——John Tukey
三、快速回顾可视化
本文假设一般读者知道用于绘图和可视化数据的基本图表类型,因此这里不再赘述,但在本文随后的实践中,我们将会涉及大部分图表类型。著名的可视化先驱和统计学家 Edward Tufte 说过,数据可视化应该在数据的基础上,以清晰、精确和高效的方式传达数据模式和洞察信息。
结构化数据通常包括由行和特征表征的数据观测值或由列表征的数据属性。每列也可以被称为数据集的某特定维度。最常见的数据类型包括连续型数值数据和离散型分类数据。因此,任何数据可视化将基本上以散点图、直方图、箱线图等简单易懂的形式描述一个或多个数据属性。本文将涵盖单变量(1 维)和多变量(多维)数据可视化策略。这里将使用 Python 机器学习生态系统,我们建议先检查用于数据分析和可视化的框架,包括 pandas、matplotlib、seaborn、plotly 和 bokeh。除此之外,如果你有兴趣用数据制作精美而有意义的可视化文件,那么了解 D3.js(https://d3js.org/)也是必须的。有兴趣的读者可以阅读 Edward Tufte 的「The Visual Display of Quantitative Information」。
闲话至此,我们来看看可视化(和代码)吧!
别在这儿谈论理论和概念了,让我们开始进入正题吧。我们将使用 UCI 机器学习库(https://archive.ics.uci.edu/ml/index.php)中的 Wine Quality Data Set。这些数据实际上是由两个数据集组成的,这两个数据集描述了葡萄牙「Vinho Verde」葡萄酒中红色和白色酒的各种成分。本文中的所有分析都在我的 GitHub 存储库中,你可以用 Jupyter Notebook 中的代码来尝试一下!
这里插播一条粉丝福利,如果你在学习Python或者有计划学习Python,想在未来人工智能领域吃上一口饭的,可以点击这里获取最新的Python学习资料和学习路线规划(免费分享,记得关注)
我们将首先加载以下必要的依赖包进行分析。
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib as mpl
import numpy as np
import seaborn as sns
%matplotlib inline
我们将主要使用 matplotlib 和 seaborn 作为我们的可视化框架,但你可以自由选择并尝试任何其它框架。首先进行基本的数据预处理步骤。
white_wine = pd.read_csv('winequality-white.csv', sep=';')
red_wine = pd.read_csv('winequality-red.csv', sep=';')
# store wine type as an attribute
red_wine['wine_type'] = 'red'
white_wine['wine_type'] = 'white'
# bucket wine quality scores into qualitative quality labels
red_wine['quality_label'] = red_wine['quality'].apply(lambda value: 'low'
if value <= 5 else 'medium'
if value <= 7 else 'high')
red_wine['quality_label'] = pd.Categorical(red_wine['quality_label'],
categories=['low', 'medium', 'high'])
white_wine['quality_label'] = white_wine['quality'].apply(lambda value: 'low'
if value <= 5 else 'medium'
if value <= 7 else 'high')
white_wine['quality_label'] = pd.Categorical(white_wine['quality_label'],
categories=['low', 'medium', 'high'])
# merge red and white wine datasets
wines = pd.concat([red_wine, white_wine])
# re-shuffle records just to randomize data points
wines = wines.sample(frac=1, random_state=42).reset_index(drop=True)
我们通过合并有关红、白葡萄酒样本的数据集来创建单个葡萄酒数据框架。我们还根据葡萄酒样品的质量属性创建一个新的分类变量 quality_label。现在我们来看看数据前几行。
wines.head()
葡萄酒质量数据集
很明显,我们有几个葡萄酒样本的数值和分类属性。每个观测样本属于红葡萄酒或白葡萄酒样品,属性是从物理化学测试中测量和获得的特定属性或性质。如果你想了解每个属性(属性对应的变量名称一目了然)详细的解释,你可以查看 Jupyter Notebook。让我们快速对这些感兴趣的属性进行基本的描述性概括统计。
subset_attributes = ['residual sugar', 'total sulfur dioxide', 'sulphates',
'alcohol', 'volatile acidity', 'quality']
rs = round(red_wine[subset_attributes].describe(),2)
ws = round(white_wine[subset_attributes].describe(),2)
pd.concat([rs, ws], axis=1, keys=['Red Wine Statistics', 'White Wine Statistics'])
葡萄酒类型的基本描述性统计
比较这些不同类型的葡萄酒样本的统计方法相当容易。注意一些属性的明显差异。稍后我们将在一些可视化中强调这些内容。
1.单变量分析
单变量分析基本上是数据分析或可视化的最简单形式,因为只关心分析一个数据属性或变量并将其可视化(1 维)。
可视化 1 维数据(1-D)
使所有数值数据及其分布可视化的最快、最有效的方法之一是利用 pandas 画直方图(histogram)。
wines.hist(bins=15, color='steelblue', edgecolor='black', linewidth=1.0,
xlabelsize=8, ylabelsize=8, grid=False)
plt.tight_layout(rect=(0, 0, 1.2, 1.2))
将属性作为 1 维数据可视化
上图给出了可视化任何属性的基本数据分布的一个好主意。
让我们进一步可视化其中一个连续型数值属性。直方图或核密度图能够很好地帮助理解该属性数据的分布。
# Histogram
fig = plt.figure(figsize = (6,4))
title = fig.suptitle("Sulphates Content in Wine", fontsize=14)
fig.subplots_adjust(top=0.85, wspace=0.3)
ax = fig.add_subplot(1,1, 1)
ax.set_xlabel("Sulphates")
ax.set_ylabel("Frequency")
ax.text(1.2, 800, r'$\mu$='+str(round(wines['sulphates'].mean(),2)),
fontsize=12)
freq, bins, patches = ax.hist(wines['sulphates'], color='steelblue', bins=15,
edgecolor='black', linewidth=1)
# Density Plot
fig = plt.figure(figsize = (6, 4))
title = fig.suptitle("Sulphates Content in Wine", fontsize=14)
fig.subplots_adjust(top=0.85, wspace=0.3)
ax1 = fig.add_subplot(1,1, 1)
ax1.set_xlabel("Sulphates")
ax1.set_ylabel("Frequency")
sns.kdeplot(wines['sulphates'], ax=ax1, shade=True, color='steelblue')
可视化 1 维连续型数值数据
从上面的图表中可以看出,葡萄酒中硫酸盐的分布存在明显的右偏(right skew)。
可视化一个离散分类型数据属性稍有不同,条形图是(bar plot)最有效的方法之一。你也可以使用饼图(pie-chart),但一般来说要尽量避免,尤其是当不同类别的数量超过 3 个时。
# Histogram
fig = plt.figure(figsize = (6,4))
title = fig.suptitle("Sulphates Content in Wine", fontsize=14)
fig.subplots_adjust(top=0.85, wspace=0.3)
ax = fig.add_subplot(1,1, 1)
ax.set_xlabel("Sulphates")
ax.set_ylabel("Frequency")
ax.text(1.2, 800, r'$\mu$='+str(round(wines['sulphates'].mean(),2)),
fontsize=12)
freq, bins, patches = ax.hist(wines['sulphates'], color='steelblue', bins=15,
edgecolor='black', linewidth=1)
# Density Plot
fig = plt.figure(figsize = (6, 4))
title = fig.suptitle("Sulphates Content in Wine", fontsize=14)
fig.subplots_adjust(top=0.85, wspace=0.3)
ax1 = fig.add_subplot(1,1, 1)
ax1.set_xlabel("Sulphates")
ax1.set_ylabel("Frequency")
sns.kdeplot(wines['sulphates'], ax=ax1, shade=True, color='steelblue')
可视化 1 维离散分类型数据
现在我们继续分析更高维的数据。
2.多变量分析
多元分析才是真正有意思并且有复杂性的领域。这里我们分析多个数据维度或属性(2 个或更多)。多变量分析不仅包括检查分布,还包括这些属性之间的潜在关系、模式和相关性。你也可以根据需要解决的问题,利用推断统计(inferential statistics)和假设检验,检查不同属性、群体等的统计显著性(significance)。
可视化 2 维数据(2-D)
检查不同数据属性之间的潜在关系或相关性的最佳方法之一是利用配对相关性矩阵(pair-wise correlation matrix)并将其可视化为热力图。
# Correlation Matrix Heatmap
f, ax = plt.subplots(figsize=(10, 6))
corr = wines.corr()
hm = sns.heatmap(round(corr,2), annot=True, ax=ax, cmap="coolwarm",fmt='.2f',
linewidths=.05)
f.subplots_adjust(top=0.93)
t= f.suptitle('Wine Attributes Correlation Heatmap', fontsize=14)
用相关性热力图可视化 2 维数据
热力图中的梯度根据相关性的强度而变化,你可以很容易发现彼此之间具有强相关性的潜在属性。另一种可视化的方法是在感兴趣的属性之间使用配对散点图。
# Correlation Matrix Heatmap
f, ax = plt.subplots(figsize=(10, 6))
corr = wines.corr()
hm = sns.heatmap(round(corr,2), annot=True, ax=ax, cmap="coolwarm",fmt='.2f',
linewidths=.05)
f.subplots_adjust(top=0.93)
t= f.suptitle('Wine Attributes Correlation Heatmap', fontsize=14)
用配对散点图可视化 2 维数据
根据上图,可以看到散点图也是观察数据属性的 2 维潜在关系或模式的有效方式。另一种将多元数据可视化为多个属性的方法是使用平行坐标图。
# Correlation Matrix Heatmap
f, ax = plt.subplots(figsize=(10, 6))
corr = wines.corr()
hm = sns.heatmap(round(corr,2), annot=True, ax=ax, cmap="coolwarm",fmt='.2f',
linewidths=.05)
f.subplots_adjust(top=0.93)
t= f.suptitle('Wine Attributes Correlation Heatmap', fontsize=14)
用平行坐标图可视化多维数据
基本上,在如上所述的可视化中,点被表征为连接的线段。每条垂直线代表一个数据属性。所有属性中的一组完整的连接线段表征一个数据点。因此,趋于同一类的点将会更加接近。仅仅通过观察就可以清楚看到,与白葡萄酒相比,红葡萄酒的密度略高。与红葡萄酒相比,白葡萄酒的残糖和二氧化硫总量也较高,红葡萄酒的固定酸度高于白葡萄酒。查一下我们之前得到的统计表中的统计数据,看看能否验证这个假设!
因此,让我们看看可视化两个连续型数值属性的方法。散点图和联合分布图(joint plot)是检查模式、关系以及属性分布的特别好的方法。
# Scatter Plot
plt.scatter(wines['sulphates'], wines['alcohol'],
alpha=0.4, edgecolors='w')
plt.xlabel('Sulphates')
plt.ylabel('Alcohol')
plt.title('Wine Sulphates - Alcohol Content',y=1.05)
# Joint Plot
jp = sns.jointplot(x='sulphates', y='alcohol', data=wines,
kind='reg', space=0, size=5, ratio=4)
使用散点图和联合分布图可视化 2 维连续型数值数据
散点图在上图左侧,联合分布图在右侧。就像我们提到的那样,你可以查看联合分布图中的相关性、关系以及分布。
如何可视化两个连续型数值属性?一种方法是为分类维度画单独的图(子图)或分面(facet)。
# Using subplots or facets along with Bar Plots
fig = plt.figure(figsize = (10, 4))
title = fig.suptitle("Wine Type - Quality", fontsize=14)
fig.subplots_adjust(top=0.85, wspace=0.3)
# red wine - wine quality
ax1 = fig.add_subplot(1,2, 1)
ax1.set_title("Red Wine")
ax1.set_xlabel("Quality")
ax1.set_ylabel("Frequency")
rw_q = red_wine['quality'].value_counts()
rw_q = (list(rw_q.index), list(rw_q.values))
ax1.set_ylim([0, 2500])
ax1.tick_params(axis='both', which='major', labelsize=8.5)
bar1 = ax1.bar(rw_q[0], rw_q[1], color='red',
edgecolor='black', linewidth=1)
# white wine - wine quality
ax2 = fig.add_subplot(1,2, 2)
ax2.set_title("White Wine")
ax2.set_xlabel("Quality")
ax2.set_ylabel("Frequency")
ww_q = white_wine['quality'].value_counts()
ww_q = (list(ww_q.index), list(ww_q.values))
ax2.set_ylim([0, 2500])
ax2.tick_params(axis='both', which='major', labelsize=8.5)
bar2 = ax2.bar(ww_q[0], ww_q[1], color='white',
edgecolor='black', linewidth=1)
使用条形图和子图可视化 2 维离散型分类数据
虽然这是一种可视化分类数据的好方法,但正如所见,利用 matplotlib 需要编写大量的代码。另一个好方法是在单个图中为不同的属性画堆积条形图或多个条形图。可以很容易地利用 seaborn 做到。
# Multi-bar Plot
cp = sns.countplot(x="quality", hue="wine_type", data=wines,
palette={"red": "#FF9999", "white": "#FFE888"})
在一个条形图中可视化 2 维离散型分类数据
这看起来更清晰,你也可以有效地从单个图中比较不同的类别。
让我们看看可视化 2 维混合属性(大多数兼有数值和分类)。一种方法是使用分图\子图与直方图或核密度图。
# facets with histograms
fig = plt.figure(figsize = (10,4))
title = fig.suptitle("Sulphates Content in Wine", fontsize=14)
fig.subplots_adjust(top=0.85, wspace=0.3)
ax1 = fig.add_subplot(1,2, 1)
ax1.set_title("Red Wine")
ax1.set_xlabel("Sulphates")
ax1.set_ylabel("Frequency")
ax1.set_ylim([0, 1200])
ax1.text(1.2, 800, r'$\mu$='+str(round(red_wine['sulphates'].mean(),2)),
fontsize=12)
r_freq, r_bins, r_patches = ax1.hist(red_wine['sulphates'], color='red', bins=15,
edgecolor='black', linewidth=1)
ax2 = fig.add_subplot(1,2, 2)
ax2.set_title("White Wine")
ax2.set_xlabel("Sulphates")
ax2.set_ylabel("Frequency")
ax2.set_ylim([0, 1200])
ax2.text(0.8, 800, r'$\mu$='+str(round(white_wine['sulphates'].mean(),2)),
fontsize=12)
w_freq, w_bins, w_patches = ax2.hist(white_wine['sulphates'], color='white', bins=15,
edgecolor='black', linewidth=1)
# facets with density plots
fig = plt.figure(figsize = (10, 4))
title = fig.suptitle("Sulphates Content in Wine", fontsize=14)
fig.subplots_adjust(top=0.85, wspace=0.3)
ax1 = fig.add_subplot(1,2, 1)
ax1.set_title("Red Wine")
ax1.set_xlabel("Sulphates")
ax1.set_ylabel("Density")
sns.kdeplot(red_wine['sulphates'], ax=ax1, shade=True, color='r')
ax2 = fig.add_subplot(1,2, 2)
ax2.set_title("White Wine")
ax2.set_xlabel("Sulphates")
ax2.set_ylabel("Density")
sns.kdeplot(white_wine['sulphates'], ax=ax2, shade=True, color='y')
利用分面和直方图\核密度图可视化 2 维混合属性
虽然这很好,但是我们再一次编写了大量代码,我们可以通过利用 seaborn 避免这些,在单个图表中画出这些图。
# Using multiple Histograms
fig = plt.figure(figsize = (6, 4))
title = fig.suptitle("Sulphates Content in Wine", fontsize=14)
fig.subplots_adjust(top=0.85, wspace=0.3)
ax = fig.add_subplot(1,1, 1)
ax.set_xlabel("Sulphates")
ax.set_ylabel("Frequency")
g = sns.FacetGrid(wines, hue='wine_type', palette={"red": "r", "white": "y"})
g.map(sns.distplot, 'sulphates', kde=False, bins=15, ax=ax)
ax.legend(title='Wine Type')
plt.close(2)
利用多维直方图可视化 2 维混合属性
可以看到上面生成的图形清晰简洁,我们可以轻松地比较各种分布。除此之外,箱线图(box plot)是根据分类属性中的不同数值有效描述数值数据组的另一种方法。箱线图是了解数据中四分位数值以及潜在异常值的好方法。
# Box Plots
f, (ax) = plt.subplots(1, 1, figsize=(12, 4))
f.suptitle('Wine Quality - Alcohol Content', fontsize=14)
sns.boxplot(x="quality", y="alcohol", data=wines, ax=ax)
ax.set_xlabel("Wine Quality",size = 12,alpha=0.8)
ax.set_ylabel("Wine Alcohol %",size = 12,alpha=0.8)
2 维混合属性的有效可视化方法——箱线图
另一个类似的可视化是小提琴图,这是使用核密度图显示分组数值数据的另一种有效方法(描绘了数据在不同值下的概率密度)。
# Violin Plots
f, (ax) = plt.subplots(1, 1, figsize=(12, 4))
f.suptitle('Wine Quality - Sulphates Content', fontsize=14)
sns.violinplot(x="quality", y="sulphates", data=wines, ax=ax)
ax.set_xlabel("Wine Quality",size = 12,alpha=0.8)
ax.set_ylabel("Wine Sulphates",size = 12,alpha=0.8)
2 维混合属性的有效可视化方法——小提琴图
你可以清楚看到上面的不同酒品质类别的葡萄酒硫酸盐的密度图。
将 2 维数据可视化非常简单直接,但是随着维数(属性)数量的增加,数据开始变得复杂。原因是因为我们受到显示媒介和环境的双重约束。
对于 3 维数据,可以通过在图表中采用 z 轴或利用子图和分面来引入深度的虚拟坐标。
但是,对于 3 维以上的数据来说,更难以直观地表征。高于 3 维的最好方法是使用图分面、颜色、形状、大小、深度等等。你还可以使用时间作为维度,为随时间变化的属性制作一段动画(这里时间是数据中的维度)。看看 Hans Roslin 的精彩演讲就会获得相同的想法!
四、结论
数据可视化与科学一样重要。如果你看到这,我很欣慰你能坚持看完这篇长文。我们的目的不是为了记住所有数据,也不是给出一套固定的数据可视化规则。本文的主要目的是理解并学习高效的数据可视化策略,尤其是当数据维度增大时。希望你以后可以用本文知识可视化你自己的数据集。
这里,我为您精心准备了一份全面的Python学习大礼包,完全免费分享给每一位渴望成长、希望突破自我现状却略感迷茫的朋友。无论您是编程新手还是希望深化技能的开发者,都欢迎加入我们的学习之旅,共同交流进步!
🌟 学习大礼包包含内容:
Python全领域学习路线图:一目了然,指引您从基础到进阶,再到专业领域的每一步学习路径,明确各方向的核心知识点。
超百节Python精品视频课程:涵盖Python编程的必备基础知识、高效爬虫技术、以及深入的数据分析技能,让您技能全面升级。
实战案例集锦:精选超过100个实战项目案例,从理论到实践,让您在解决实际问题的过程中,深化理解,提升编程能力。
华为独家Python漫画教程:创新学习方式,以轻松幽默的漫画形式,让您随时随地,利用碎片时间也能高效学习Python。
互联网企业Python面试真题集:精选历年知名互联网企业面试真题,助您提前备战,面试准备更充分,职场晋升更顺利。
👉 立即领取方式:只需【点击这里】,即刻解锁您的Python学习新篇章!让我们携手并进,在编程的海洋里探索无限可能!