数据可视化的图表种类繁多,各式各样,因此我们需要掌握如何在特定场景下使用特定的图表。
数据可视化是为业务目的服务的,好的可视化图表可以起到清晰准确反映业务结果的目的,在选择使用何种图表时,通常我们需要首先考虑你想通过可视化阐述什么样的故事,受众是谁,以及打算如何分析结果。
关于如何利用数据创造出吸引人的、信息量大的、有说服力的故事,进而达到有效沟通的目的,可以进一步阅读这本书《用数据讲故事》学习。
本章将介绍不同场景适合的可视化图表类型,使用注意事项,以及如何用现成的绘图接口来呈现。

我们将常见的场景分为5大类:
1)展示趋势变化(Evolution)
2)展示分布关系(Distribution)
3)展示相关关系(Correlation)
4)展示排序信息(Ranking)
5)展示组成关系(Part of a whole)

1. 展示趋势变化(Evolution)

1.1 折线图 - Line chart

线图(也叫折线图)是众多图表中的基本图形。它由一系列的数据点和连接这些数据点的线段组成。
它的形式和散点图类似,区别是
①线图的数据点通常是有序排列的(一般按X轴的值顺序)
②线图多了线段连接数据点。
线图是描述趋势变化的,散点图是描述两个变量的相关性的。

线图常用来呈现时间趋势的变化(时间序列),所以X轴通常代表某个时间间隔。

注意事项:

  1. X轴的数据必须是有序
  2. 是否需要截断Y轴,即Y轴是否必须要从0点开始?
  3. 如果要比较两个或多个不同的变量的变化趋势,不要使用双Y轴图表
  4. 小心有很多线条的线图(spaghetti chart-意大利面条图),太多的线条会让图表变得混乱、无法阅读;建议使用多子图形式或重点突出某一个种类

线图可以直接用pyplot.plot函数绘制,只输入一列数的话会默认为Y轴的值,然后自动生成X轴;亦可输入两列数分别代表X轴和Y轴。

参考示例:

示例1:简单线图

  1. import matplotlib.pyplot as plt
  2. import numpy as np
  3. import pandas as pd
  1. # 创建数据,分别对应X轴和Y轴,注意X轴要是有序排列的
  2. df=pd.DataFrame({'xdata': range(1,101), 'ydata': np.random.randn(100) })
  3. # 绘图
  4. plt.style.use('seaborn-darkgrid') # 也可以选择其他的风格式样 seaborn-whitegrid
  5. plt.figure(figsize=(15, 10)) # 设置画布大小
  6. # color: 控制线条颜色,red/skyblue/blue 等
  7. # alpha: 控制线条透明度
  8. # linestyle:控制线条式样,'--', '-', '-.', ':' 等
  9. # linewidth:控制线条粗细大小
  10. plt.plot( 'xdata', 'ydata', data=df, color='blue',alpha=0.3, linestyle='-.', linewidth=2, label='linestyle')
  11. plt.legend(loc='upper left', frameon=False) # 设置标签
  12. plt.title('Basic line plot') # 设置标题
  13. plt.show()

Task06:场景案例显神通 - 图1

示例2:突出某一重点的多线图

下面是正常的多线图:
需要注意的是,这里只有5条线,尚能看清楚,但是如果如果超过5条,10条甚至更多的时候,花在一张图上基本看不到什么有效信息。这时候我们可以采取两种办法:

  1. 还是在一张图上,突出其中一条或两条线,其他都是作为背景的灰色
  2. 几条线画几个子图 ```python

    导入数据集并转成方便作图的格式

    Dataset = pd.read_csv(‘data/Drugs.csv’) group = Dataset.groupby([‘YYYY’,’State’]).agg(‘sum’).reset_index() df = group.pivot(index=’YYYY’, columns=’State’, values=’DrugReports’).reset_index()

    设定式样

    plt.style.use(‘seaborn-darkgrid’)

创建调色板, 色卡用来控制每条线的颜色

palette = plt.get_cmap(‘Set1’)

绘图

plt.figure(figsize=(15, 7)) num=0 for column in df.drop(‘YYYY’, axis=1): num += 1 plt.plot(df[‘YYYY’], df[column], marker=’’, color=palette(num), linewidth=2, alpha=0.9, label=column)

plt.legend(loc=2, ncol=2) plt.title(“Multiple line plot”, loc=’center’, fontsize=12, fontweight=0, color=’orange’) plt.xlabel(“year”) plt.ylabel(“DrugReports”) plt.show()

  1. ![](https://cdn.nlark.com/yuque/0/2020/png/8398054/1608888233675-e7ab899c-1604-49f8-8350-21c0d70055b0.png#align=left&display=inline&height=434&margin=%5Bobject%20Object%5D&originHeight=434&originWidth=896&status=done&style=none&width=896)<br />下面是突出某一重点的多线图:<br />(设置`color`参数的不同参数值,突出的PA为橘色`color='orange'`,其他的都为灰色`color='grey'`。)
  2. ```python
  3. # 导入数据集并转成方便作图的格式
  4. Dataset = pd.read_csv('data/Drugs.csv')
  5. group = Dataset.groupby(['YYYY','State']).agg('sum').reset_index()
  6. df = group.pivot(index='YYYY', columns='State', values='DrugReports').reset_index()
  7. # 设定式样
  8. plt.style.use('seaborn-darkgrid')
  9. # 绘图
  10. plt.figure(figsize=(10, 10), dpi=70)
  11. # 所有的线条都画成灰色
  12. for column in df.drop('YYYY', axis=1):
  13. plt.plot(df['YYYY'], df[column], marker='', color='grey', linewidth=1, alpha=0.4)
  14. # PA的特殊处理,用橘色且加粗
  15. plt.plot(df['YYYY'], df['PA'], marker='', color='orange', linewidth=4, alpha=0.7)
  16. # 设定每条线的label的位置,其他的都为灰色,PA的为橘色
  17. num=0
  18. for i in df.values[7][1:]:
  19. num+=1
  20. name=list(df)[num]
  21. if name != 'PA':
  22. plt.text(2017.02, i, name, horizontalalignment='left', size='small', color='grey')
  23. # 特殊处理PA
  24. plt.text(2017.02, df.PA.tail(1), 'PA', horizontalalignment='left', size='small', color='orange')
  25. # 添加图的标题和XY轴的标签
  26. plt.title("Evolution of PA vs other states", loc='left', fontsize=12, fontweight=0, color='orange')
  27. plt.xlabel("Year")
  28. plt.ylabel("DrugReports")
  29. plt.show()

Task06:场景案例显神通 - 图2


示例3:多子图

一张图显示多条线过于拥挤的时候,可以分成多个小的子图来显示。
多个子图对比的时候,需要注意,X轴和Y轴的刻度大小需要严格一致,不然会带来误导。

  1. # 导入数据集并转成方便作图的格式
  2. Dataset = pd.read_csv('data/Drugs.csv')
  3. group = Dataset.groupby(['YYYY','State']).agg('sum').reset_index()
  4. df = group.pivot(index='YYYY', columns='State', values='DrugReports').reset_index()
  5. # 初始化画布的设定
  6. plt.style.use('seaborn-darkgrid') # 风格
  7. palette = plt.get_cmap('Set1') # 颜色卡
  8. plt.figure(figsize=(15, 10)) # 画布大小
  9. # 绘制
  10. num=0
  11. for column in df.drop('YYYY', axis=1):
  12. num+=1
  13. # 设定子图在画布的位置
  14. plt.subplot(3,3, num)
  15. # 画线图
  16. plt.plot(df['YYYY'], df[column], marker='', color=palette(num), linewidth=1.9, alpha=0.9, label=column)
  17. # 设定子图的X轴和Y轴的范围,注意,这里所有的子图都是用同一套X轴和Y轴
  18. plt.xlim(2009.3,2017.3)
  19. plt.ylim(0,50000)
  20. # 添加每个子图的标题
  21. plt.title(column, loc='left', fontsize=12, fontweight=0, color=palette(num) )
  22. # 添加整个画布的标题
  23. plt.suptitle("How many DrugReports the 5 states have in past few years?", fontsize=13, fontweight=0, color='black', style='italic', y=0.95)
  24. # 添加整个画布的横纵坐标的名称
  25. plt.text(2014, -9500, 'Year', ha='center', va='center')
  26. plt.text(1998, 60000, 'DrugReports', ha='center', va='center', rotation='vertical')
  27. plt.show()

Task06:场景案例显神通 - 图3

1.2 面积图 - Area chart

面积图和折线图从形式上看,非常相似。区别只是面积图在折线图的基础上,填充了折线下面的区域。可以用颜色或阴影去填充。
有很多人钟情于面积图,因为区域的填充可以让人更直观的看出数据的变化趋势。

注意事项:

  1. 是否要截断Y轴,见折线图部分。
  2. 如果需要对比两个或以上的类别,建议使用堆积面积图;如果一定要在单一面积图上表示,注意填充颜色一定要是透明色的,可以看到所有的线条。
  3. 注意图形的长宽比,让图形更易读一点。
  4. 一个好的做法是,将线条和填充的颜色保持统一,填充的颜色设置一些透明度,这些的图形会更美观一点。

面积跟折线图作图非常类似,我们只需多一步填充的操作。

pyplot接口中,可以用fill_betweenstackplot方法来实现。
这里更推荐使用fill_between,在之后的定制化操作中更方便一点;stackplot更多的是用在堆积面积图中。
fill_betweenstackplot的输入都是两个数值变量。

具体用法见下面参考代码

# 创建数据
x=range(1,15)
y=[1,4,6,7,4,9,3,2,4,1,5,4,8,7]

# 绘图
# facecolor:控制填充颜色,red/skyblue/blue 等
# alpha:    控制填充透明度
# hatch:     控制阴影式样{'/', '\', '|', '-', '+', 'x', 'o', 'O', '.', '*'}
plt.fill_between( x, y, facecolor="skyblue", alpha=0.4, hatch='/')
plt.show()

# 在填充的基础上,添加一条折线,图形更加清晰
plt.fill_between( x, y, facecolor="skyblue", alpha=0.2)
# 线的更多设置可以参考 line plot文档
plt.plot(x, y, color="skyblue", alpha=0.6, linewidth=1.5)
plt.show()

Task06:场景案例显神通 - 图4
Task06:场景案例显神通 - 图5

fill_between的语法如下:

matplotlib.pyplot.fill_between(x, y1, y2=0, where=None,
             interpolate=False, step=None, *, data=None, **kwargs)

实际上是填充两个函数之间的区域,只是如果不指定y2的话,就是默认填充到坐标轴的面积。

示例:

x = np.linspace(0, 5 * np.pi, 1000)
y1 = np.sin(x)
y2 = np.sin(2 * x)

plt.figure(figsize=(8,6))
plt.plot(x, y1, c="b")
plt.plot(x, y2, c='r')

# fill_between 填充两个函数之间的区域
# 两个函数之间的区域用黄色填充
plt.fill_between(x, y1, y2, facecolor="yellow")
plt.show()

下载.png


1.3 堆积面积图 - Stacked area chart

堆积面积图是基础面积图的一个延伸,它将多个类别的数据变化都显示在了一个图形中。
它有以下特点:

  1. 不同于多折线图的线条可能相互交叉,堆积面积图不会出现不同分类的数据点被遮盖、被隐藏的状况。每个类别都是都是堆积在下面类别面积图之上的。
  2. 堆积面积图与标准面积图不同,某一分类的值并非与纵坐标完全对应,而是通过折线之间的相对高度来表达。
  3. 堆积面积图不仅可以展示各类的发展趋势(面积图和折线图都能表示这个), 可以表达总体的发展趋势和个种类间的关系,比如重要程度,大致占比等。

堆积图可以用stackplot函数绘制,它的数据输入方式可以是一个X和多个Y,也可以将多列Y的数据合并成一个,如下

示例1:

plt.style.use('seaborn-darkgrid') # 风格
plt.figure(figsize=(10, 6)) # 画布大小
# 方式一, y由三个序列组成
x=range(1,6)
y=[ [1,4,6,8,9], [2,2,7,10,12], [2,8,5,10,6] ]

# 绘图
plt.stackplot(x,y, labels=['A','B','C'])
plt.legend(loc='upper left')
plt.show()

Task06:场景案例显神通 - 图7

示例2:

# 导入包
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# 导入数据集并转成方便作图的格式
Dataset = pd.read_csv('data/Drugs.csv')
group = Dataset.groupby(['YYYY','State']).agg('sum').reset_index()
df = group.pivot(index='YYYY', columns='State', values='DrugReports').reset_index()
plt.style.use('seaborn-darkgrid') # 风格
plt.figure(figsize=(10, 6)) # 画布大小
plt.stackplot(df['YYYY'],df['KY'],df['OH'],df['PA'],df['VA'],df['WV'], labels=df.iloc[:, 1:6].columns)
plt.legend(loc='upper left')
plt.show()

Task06:场景案例显神通 - 图8


2. 展示分布关系(Distribution)

2.1 小提琴图 - Violin plot

小提琴图是用来展示多组数据的分布状态以及概率密度。这种图表结**合了箱形图和密度图的特征**。小提琴图的功能与箱型图类似。 它显示了一个(或多个)分类变量多个属性上的定量数据的分布,从而可以比较这些分布。与箱形图不同,其中所有绘图单元都与实际数据点对应,小提琴图描述了基础数据分布的核密度估计。

注意事项:
1.不适合展示只有很少组别的数据
2.按照中位数排序能让数据看起来更直观

小提琴图可以用seaborn包的violinplot方法实现,具体用法如下

示例**1:根据分类变量分组绘制一个纵向的小提琴图**

# !pip install seaborn  # 在jupyter notebook或者IPython中安装seaborn库

# 约定俗成的导入别名
import seaborn as sns
df = pd.read_csv('data/iris.csv')
# x代表不同的类别特征,y代表连续特征,inner代表在小提琴图中显示四分位数线
sns.violinplot( x=df['species'],y=df["sepal_length"],inner='quartile' )
plt.show()

Task06:场景案例显神通 - 图9

示例**2:**根据2个分类变量嵌套分组绘制一个小提琴图

tips = pd.read_csv('data/tips.csv')

ax = sns.violinplot(x="day", y="total_bill", hue="smoker",data=tips, palette="muted")
plt.show()

Task06:场景案例显神通 - 图10

示例3:**绘制分割的小提琴图以比较不同的色调变量**

ax = sns.violinplot(x="day", y="total_bill", hue="smoker",
                    data=tips, palette="muted", split=True)
plt.show()

Task06:场景案例显神通 - 图11


2.2 箱型图 - Box plot

箱形图(或盒须图)以一种利于变量之间比较或不同分类变量层次之间比较的方式来展示定量数据的分布。矩形框显示数据集的上下四分位数,而矩形框中延伸出的线段(触须)则用于显示其余数据的分布位置,剩下超过上下四分位间距的数据点则被视为“异常值”。

箱型图的基本作用如下:

  • 数据异常值

箱形图为我们提供了识别异常值的一个标准:异常值被定义为 小于Q1-1.5IQR 大于Q3+1.5IQR 的值。

  • 偏态和尾重

箱型图揭示了数据批分布偏态和尾重的部分信息,尽管它们不能给出偏态和尾重程度的精确度量,但可作为我们粗略估计的依据。

  • 数据的形状

同一数轴上,几批数据的箱形图并行排列,几批数据的中位数、尾长、异常值、分布区间等形状信息便一目了然。在一批数据中,哪几个数据点出类拔萃,哪些数据点表现不及一般,这些数据点放在同类其它群体中处于什么位置,可以通过比较各箱形图的异常值看出。

注意事项

  1. 箱型图隐藏了每个分组的数据量信息,可以通过标注或箱子宽度来展现
  2. 箱型图隐藏了背后的分布信息,当数据量较少时可以使用数据抖动(jitter),当数据量较大时可以使用小提琴图来展现(数字通信系统对抖动的定义是“数字信号的各个有效瞬时对其当时的理想位置的短期性偏离”。在这里指图像表示的位置和实际数值存在误差。这里指人为添加了扰动数据。)

箱型图可以直接使用seaborn的boxplot方法来实现。

示例1:根据分类变量分组绘制一个纵向的箱型图

sns.set(style="whitegrid")
tips = pd.read_csv('data/tips.csv')
ax = sns.boxplot(x="day", y="total_bill", data=tips)
plt.show()

Task06:场景案例显神通 - 图12

示**例2:**根据2个分类变量嵌套分组绘制一个箱型图

ax = sns.boxplot(x="day", y="total_bill", hue="smoker",data=tips, palette="Set2")
plt.show()

Task06:场景案例显神通 - 图13

示例3:使用swarmplot 展示箱型图顶部的数据点

ax = sns.boxplot(x="day", y="total_bill", data=tips)
ax = sns.swarmplot(x="day", y="total_bill", data=tips, color=".25")

Task06:场景案例显神通 - 图14

2.3 直方图(HIstogram)

直方图只能接收数值类型的变量数据,该变量被切割成几个箱子,每个箱子的高度代表处于分箱中的数量。

注意事项

  1. 使用过程中要注意分箱数量的选择
  2. 不要用直方图展示超过5个变量的分布情况
  3. 避免使用彩色

可以使用seaborn中的histplot方法绘制直方图

import pandas as pd
from sklearn.datasets import load_boston


boston=load_boston()
y = boston['target']
f, axs = plt.subplots(3,1,figsize=(10,10))
# 计数标准直方图
sns.histplot(y,stat='count',ax=axs[0])
# 归一化的直方图
sns.histplot(y,stat='probability',ax=axs[1])
# 在直方图上同时画出密度曲线
sns.histplot(y,stat='probability',kde=True,ax=axs[2])
plt.show()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-14-26a625e76299> in <module>
      6 f, axs = plt.subplots(3,1,figsize=(10,10))
      7 # 计数标准直方图
----> 8 sns.histplot(y,stat='count',ax=axs[0])
      9 # 归一化的直方图
     10 sns.histplot(y,stat='probability',ax=axs[1])
AttributeError: module 'seaborn' has no attribute 'histplot'

Task06:场景案例显神通 - 图15

sns.__version__  # 查看当前的版本
'0.9.0'

注意:histplot 在 seaborn V0.11.1 版本才加入。
0.11.1 更新说明: https://seaborn.pydata.org/whatsnew.html?highlight=histplot
seaborn.histplot官方文档:https://seaborn.pydata.org/generated/seaborn.histplot.html?highlight=histplot
使用 !pip install --upgrade seaborn 命在jupyter notebook或者IPython中更新seaborn。
更新后再重新导入一次seaborn即可

import seaborn as sns
sns.__version__
'0.11.1'
import pandas as pd
from sklearn.datasets import load_boston


boston=load_boston()
y = boston['target']
f, axs = plt.subplots(3,1,figsize=(10,10))
# 计数标准直方图
sns.histplot(y,stat='count',ax=axs[0])
# 归一化的直方图
sns.histplot(y,stat='probability',ax=axs[1])
# 在直方图上同时画出密度曲线
sns.histplot(y,stat='probability',kde=True,ax=axs[2])
plt.show()

Task06:场景案例显神通 - 图16

2.4 密度图(Density)

密度图和直方图很类似,同样用来展示数值型变量的分布情况。

注意事项

  1. 注意密度函数的带宽
  2. 不要用直方图展示超过5个变量的分布情况
  3. 避免使用彩色

可以使用seaborn中的kdeplot方法绘制直方图
注意:”bw_method” 参数也是在seaborn V0.11.1 才加入的

#kdeplot()中的bw参数控制着估计值与真实数据之间的贴近程度
#它与我们的KDE图的宽度相关。它提供了默认的规则来确定一个取值
x = np.random.normal(size=100)
sns.kdeplot(x, label="bw: default")
sns.kdeplot(x, bw_method=0.2, label="bw: 0.2")
sns.kdeplot(x, bw_method=2, label="bw: 2")
plt.legend();

Task06:场景案例显神通 - 图17

二维变量的核密度估计:

mean, cov = [0, 1], [(1, .5), (.5, 1)]
data = np.random.multivariate_normal(mean, cov, 200)
df = pd.DataFrame(data, columns=["x", "y"])
# 核密度估计也适用于二元的情况。在seaborn中,这种图会以等高线的方式展示出来,我们可以用jointplot(kind="kde")来绘制
sns.jointplot(x="x", y="y", data=df, kind="kde")
plt.show()

Task06:场景案例显神通 - 图18

3. 展示相关关系(Correlation)

3.1 散点图(Scatter plot)

散点图常用于查看数值型变量之间的相关性,同时可以利用不同颜色来区分样本所属的类别

注意事项
绘制散点图时
要避免Overplotting**,意思是由于散点数量过多导致图中的样例点过度重合。
为了避免overplotting,第一种方式可以通过抽样来作图,第二种方式可以用热力图代替,第三种方式是调节样本点的size。

可以直接用matplotlib的scatter方法绘制散点图

df = pd.read_csv('data\diamonds.csv').sample(1000)
# 绘制标准散点图
plt.scatter(df.carat, df.price, s=0.2)
plt.show()

Task06:场景案例显神通 - 图19

用不同颜色区别不同类别的散点图:

# 用颜色区别不同类别的散点
sns.lmplot(x='carat', y='price', data=df, hue='cut', fit_reg=False)
plt.show()

Task06:场景案例显神通 - 图20

3.2 热力图(Heatmap)

密度图对于一组数值变量,可以理解为先对其进行二维分箱,再分别统计每个箱子的对应指标;对于分类变量而言,箱子一般可以指定为对应的类别。但通常,热力图更多用来表示数值变量的总体信息。

注意事项

  1. 考虑到长尾分布等情况,经常需要对数据做标准化的预处理
  2. 经常需要对数据先进行分箱再绘图,对于类别变量而言,可以进行类别的合并;同时对于数值变量而言,既可以包含分位数分箱,也可以包含等间隔分箱

可以直接使用seaborn包的heatmap方法绘制热力图,用jointplot绘制蜂窝热力图

示例1:类别变量的统计

res = pd.crosstab(df.cut, df.clarity)
sns.heatmap(res, cmap='Greens', annot=True)
plt.show()

Task06:场景案例显神通 - 图21

示例2:类别变量和数值变量分箱统计

res = pd.crosstab(pd.qcut(df.price, 5), df.clarity)
sns.heatmap(res, cmap='Greens', annot=True)

Task06:场景案例显神通 - 图22

示例3:数值变量之间的密度图

sns.jointplot(x=df["price"], y=df["carat"], kind='hex')
plt.show()

Task06:场景案例显神通 - 图23

在上述密度图作图时,由于原来的特征是长尾分布的,所以导致密度图的偏向性很高,此时可以考虑使用对数变换、分位数截断和标准差截断。

示例4:使用对数变换改变数据分布

sns.jointplot(x=np.log(df["price"]), y=np.log(df["carat"]), kind='hex')
plt.show()

Task06:场景案例显神通 - 图24

示例5: 使用标准差截断
pandas中的 where 和mask方法:

  • where:表示不满足条件的,被设置为指定值(默认为None)。
  • mask:与where相反,满足条件的数据,被设置为指定值(默认为None)。
    s1, s2 = df.price, df.carat
    s1 = s1.mask((s1>(s1.median()+1*s1.std()))|(s1<(s1.median()-s1.std())))
    s2 = s2.mask((s2>(s2.median()+1*s2.std()))|(s2<(s2.median()-s2.std())))
    sns.jointplot(x=s1, y=s2, kind='hex')
    plt.show()
    
    Task06:场景案例显神通 - 图25

示例6:使用分位数截断

s1, s2 = df.price, df.carat
s1 = s1.mask((s1>(s1.quantile(0.5)))|(s1<(s1.quantile(0.05))))
s2 = s2.mask((s2>(s2.quantile(0.5)))|(s2<(s2.quantile(0.05))))
sns.jointplot(x=s1, y=s2, kind='hex')
plt.show()

Task06:场景案例显神通 - 图26

3.3 气泡图(Bubble plot)

气泡图适用于超过二维特征的可视化,一般可以用气泡的颜色和大小来表示第三维、第四维的特征,可以认为气泡图是散点图的衍生

注意事项

  1. 使用气泡面积而不是气泡的直径作为数值指标对比
  2. 和散点图类似,气泡图同样要注意overplotting的问题

可以使用matplotlib的scatter方法绘制气泡图,同时用颜色和尺寸参数控制表示第三,第四维度

示例1:

new_feature1 = np.random.randint(0, 10, 10) # 用气泡大小显示该feature大小
new_feature2 = np.random.randint(0, 10, 10) # 用气泡深浅显示该feature大小
plt.scatter(df.carat.sample(10), df.price.sample(10),
            s=new_feature1*100, c=new_feature2,
            cmap="Blues", alpha=0.8, edgecolors="grey", linewidth=2)
plt.show()

Task06:场景案例显神通 - 图27

示例2:

plt.scatter(df.cut.sample(10), df.price.sample(10),
            s=new_feature1*100, c=new_feature2,
            cmap="Blues", alpha=0.8, edgecolors="grey", linewidth=2)

Task06:场景案例显神通 - 图28

4. 展示排序信息(Ranking)

4.1 柱状图 - Barplot

柱状图用来展示一个类别变量和一个数值变量之间的关系,每个柱子代表一个类别,柱子的长度代表这个类别的数值。通常来说,柱状图是展示此类信息最有效的方式之一。

注意事项

  1. 不要和直方图混淆
  2. 类别标签较长时,可以采用横向柱状图
  3. 给柱子排序通常更有利于展示信息

可以直接用matplotlib的 bar方法绘制竖直柱状图,用 barh 方法绘制横向柱状图。

# 计算分类别的平均属性值
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt


pokemon = pd.read_csv('data/pokemon.csv')
data=pokemon.groupby('Type 1')['Total'].mean().sort_values(ascending=False).reset_index()
# 绘制柱状图
bars = data['Type 1']
pos = np.arange(len(bars))
plt.bar(pos, data['Total'])
plt.xticks(pos, bars,rotation=270)
plt.show()

Task06:场景案例显神通 - 图29

4.2 雷达图 - Radar

雷达图是一种展示多个定量变量的二维图表,所有变量交汇在中心同一点,由于使用限制较多,在可视化中一般较少用到。

注意事项

  1. 不要在一张图显示超过5个组别
  2. 当不同组别标度差别很大时,谨慎使用雷达图

可以使用极坐标系和多边形填充的方式绘制雷达图,具体用法如下

from math import pi  # π也可以用np.pi


# 绘制背景,选择2只口袋妖怪,比较六维属性值
data = pokemon.loc[[0,4]]
categories=['HP','Attack','Defense','Sp. Atk','Sp. Def','Speed']
N=6
angles = [n / float(N) * 2 * pi for n in range(N)]
angles += angles[:1]
ax = plt.subplot(111, polar=True)
ax.set_theta_offset(pi / 2)
ax.set_theta_direction(-1)
plt.xticks(angles[:-1], categories)
ax.set_rlabel_position(0)
plt.yticks([20,40,60,80], ["20","40","60","80"], color="grey", size=7)
plt.ylim(0,80)

# 分别添加两个变量的雷达曲线
values= data.loc[0, ['HP','Attack','Defense','Sp. Atk','Sp. Def','Speed','HP']]
ax.plot(angles, values, linewidth=1, linestyle='solid', label=data.loc[0,'Name'])
ax.fill(angles, values, 'b', alpha=0.1)

values= data.loc[4, ['HP','Attack','Defense','Sp. Atk','Sp. Def','Speed','HP']]
ax.plot(angles, values, linewidth=1, linestyle='solid', label=data.loc[4,'Name'])
ax.fill(angles, values, 'r', alpha=0.1)
# 图例
plt.legend(loc='upper right', bbox_to_anchor=(0.1, 0.1))
plt.show()

Task06:场景案例显神通 - 图30

4.3 平行坐标图 - Parallel coordinates

平行坐标图用来比较样本在一组数值型变量上的特征,它是雷达图的另一种表现形式,在可视化中更推荐被使用。

注意事项

  1. 不适合用于组别过多的情况
  2. 可以在X轴对数据排序,避免曲线之间的交叉

可以通过pandas.plotting中的parallel_coordinates方法绘制平行坐标图

from pandas.plotting import parallel_coordinates


data =pd.read_csv('data/iris.csv')

# Make the plot
parallel_coordinates(data, 'species', colormap=plt.get_cmap("Set2"))
plt.show()

Task06:场景案例显神通 - 图31

4.4 棒棒糖图 - Lollipop

棒棒糖图本质上是柱状图的另一种表现形式,区别是把柱子用线和点来代替,但是从视觉上表现效果更好

注意事项

  1. 排序会使得显示效果更好
  2. 如果因为某种原因不能保持排序状态,那么宁愿选择柱状图

可以使用pyplot.hlines方法来展示棒棒糖图

# 计算分类别的平均属性值
data=pokemon.groupby('Type 1')['Total'].mean().reset_index()

# 绘制棒棒糖图
data = data.sort_values(by='Total')
my_range=range(1,len(data.index)+1)
plt.hlines(y=my_range, xmin=0, xmax=data['Total'], color='skyblue')
plt.plot(data['Total'], my_range, "o")
plt.yticks(my_range, data['Type 1'])
plt.title("A vertical lolipop plot", loc='left')
plt.xlabel('Average value of Total')
plt.ylabel('Type')
plt.show()

Task06:场景案例显神通 - 图32

4.5 圆形柱状图 - Circular barplot

圆形柱状图相比于柱状图更吸引眼球,但同时也更难识别出柱子尺寸的差别,因此只有当你有大量类别需要展示,并且有一些明显突出的类别时才会使用。

注意事项

  1. 内圈的比例不能太小,一般须超过外圈的三分之一
  2. 通常只有当你有很多类别需要展示时才会用(>40)

在matplotlib中,可以通过极坐标系下的柱状图加上内圈的圆来绘制圆形柱状图

# 计算分类别的平均属性值
data=pokemon.groupby('Type 1')['Total'].mean().reset_index()

# 绘制圆形柱状图
N = len(data)
bottom = 250
value = data['Total']
theta = np.linspace(0.0, 2 * pi, N, endpoint=False)
width = (2*pi) / N-0.02
plt.figure(figsize = (16, 10))
ax = plt.subplot(111, polar=True)
bars = ax.bar(theta, value, width=width, bottom=bottom)
ax.set_theta_zero_location("N")
ax.set_theta_direction(-1)
ticks =data['Type 1']
for theta,tick,value in zip(theta,ticks,value):
    ax.text(theta+0.03, value+380,tick) 
plt.axis('off')
plt.show()

Task06:场景案例显神通 - 图33

5. 展示组成关系(Part of a whole)


5.1 饼图 - Pie chart

饼图在图像上是一个被分成若干部分的圆,用于反映每个部分对于整体所占的比重

注意事项

  1. 如果使用百分数,确保它加起来是100%
  2. 不要使用3d和图例,使得图的阅读性更差

饼图可以直接用pyplot.pie函数绘制,也可以调用pandas库的绘图接口dataframe.plot,具体用法如下

示例1:直接用 pyplot.pie 函数绘制

#绘制Pie chart
fig, ax = plt.subplots()  # 1*1画布
size = 0.3
vals = np.array([[60., 32.], [37., 40.], [29., 10.]])  # 3*2 array
cmap = plt.get_cmap("tab20c")  # Get a colormap instance, matplotlib.cm
outer_colors = cmap(np.arange(3)*4)  # cmap([0,4,8]), len(cmap.colors) -> 20
inner_colors = cmap(np.array([1,2,5,6,9,10]))
# 第一个环
ax.pie(vals.sum(axis=1))  # wedge object 控制圆环的宽度
plt.show()

Task06:场景案例显神通 - 图34

示例2:调用pandas库的绘图接口 dataframe.plot 进行绘制

# --- dataset 1: just 4 values for 4 groups:
df = pd.DataFrame([8,8,1,2], index=['a', 'b', 'c', 'd'], columns=['x'])

# make the plot
df.plot(kind='pie', subplots=True, figsize=(8, 8))
plt.show()

Task06:场景案例显神通 - 图35

5.2 甜甜圈图 - Donut chart

甜甜圈图和饼图极为类似,都是用来反映几个对象的组成比例,因而也有着相似的注意事项。(只是把饼图中间掏空,一个饼图中间加一个圆)

注意事项

  1. 如果使用百分数,确保它加起来是100%
  2. 不要使用3d和图例,使得图的阅读性更差

在绘图时可以通过在饼图的中心画一个和底色相同的同心圆方式来绘制,具体用法如下


示例1:基本甜甜圈图

# 创建数据
names='groupA', 'groupB', 'groupC', 'groupD',
size=[12,11,3,30]

# 在中心画一个白色的圆
my_circle=plt.Circle( (0,0), 0.7, color='white')

# 画外围的饼图
plt.pie(size, labels=names, colors=['red','green','blue','skyblue'])
p=plt.gcf()
p.gca().add_artist(my_circle)
plt.show()

Task06:场景案例显神通 - 图36

示例2:有子分组的甜甜圈图

matplotlib支持使用setp()(set property)以及 getp()(get property)设置和获取对象属性,以及对对象进行自省。

# Make data: I have 3 groups and 7 subgroups
group_names=['groupA', 'groupB', 'groupC']
group_size=[12,11,30]
subgroup_names=['A.1', 'A.2', 'A.3', 'B.1', 'B.2', 'C.1', 'C.2', 'C.3', 'C.4', 'C.5']
subgroup_size=[4,3,5,6,5,10,5,5,4,6]

# Create colors
a, b, c=[plt.cm.Blues, plt.cm.Reds, plt.cm.Greens]

# First Ring (outside)
fig, ax = plt.subplots()
ax.axis('equal')
mypie, _ = ax.pie(group_size, radius=1.3, labels=group_names,
                  colors=[a(0.6), b(0.6), c(0.6)] )
plt.setp(mypie, width=0.3, edgecolor='white')

# Second Ring (Inside)
mypie2, _ = ax.pie(subgroup_size, radius=1.3-0.3, labels=subgroup_names, labeldistance=0.7, colors=[a(0.5), a(0.4), a(0.3), b(0.5), b(0.4), c(0.6), c(0.5), c(0.4), c(0.3), c(0.2)])
plt.setp( mypie2, width=0.4, edgecolor='white')
plt.margins(0,0)

plt.show()

Task06:场景案例显神通 - 图37

5.3 文氏图 - Venn diagram

文氏图用于表示不同集合的有限集合之间所有可能的逻辑关系,每个集合用一个圆表示,圆的大小反映了该组的重要性,组与组之间通常会有交叠,交叠的部分体现了不同组之间的交叉数据。

注意事项

  1. 不建议绘制超过3个集合的venn图,超过3个集合的venn图不便于理解
  2. 图中的数字指代集合之间交集的元素个数


文氏图可以利用matplotlib_venn包中的venn2和venn3方法绘制两个集合或三个集合的之间的逻辑关系。文氏图的数据类型可以是set或tuple

  • venn2方法中可以指定两个set的取值,venn2方法中可以指定3个set的取值
  • 可以通过一个tuple指定集合之间的重叠关系,且在venn2方法中tuple只有前3个元素会被用于venn图绘制,在venn3方法中tuple只有前7个元素会被用于venn图绘制

示例1:

from matplotlib_venn import venn2
from matplotlib_venn import venn3

venn3(subsets=[set([3, 2, 1,4,5,6]),set([2,3,4]),set([1,2,3,4,5])],
      set_labels=('A', 'B','C'),set_colors = ('lightpink','pink','pink'))
plt.show()

Task06:场景案例显神通 - 图38

示例2:

venn2(subsets=(3, 2,4,1), set_labels=('A', 'B'),set_colors = ('r','g'))
plt.show()

Task06:场景案例显神通 - 图39

示例3:

venn3(subsets=(1,2,3,4,5,6,0), set_labels=('A', 'B','C'),set_colors = ('r','g','b'))
plt.show()

Task06:场景案例显神通 - 图40

5.4 树图 - Treemap

树图将数据显示为一组嵌套的矩形,通过矩形的面积反映其取值大小,使用配色方案,可以表示多个维度:组、子组。
树图的优势是充分利用了空间,使得在有限的空间内展示大量数据。

注意事项

  1. 不要在层次结构中注释超过3个级别,这会使图形不可读。
  2. 优先考虑层次结构的最高层次

可以使用squarify包绘制树图,squarify的底层代码也是基于matplotlib实现的。

示例1:

#绘制treemap
import squarify # pip install squarify (algorithm for treemap)

# Change color
squarify.plot(sizes=[13,22,10,5], label=["group A", "group B", "group C", "group D"], color=["red","green","blue", "grey"], alpha=.4 )
plt.axis('off')
plt.show()

Task06:场景案例显神通 - 图41

示例2:

# Create a dataset:
my_values=[i**3 for i in range(1,100)]

# create a color palette, mapped to these values
cmap = matplotlib.cm.Blues
mini=min(my_values)
maxi=max(my_values)
norm = matplotlib.colors.Normalize(vmin=mini, vmax=maxi)
colors = [cmap(norm(value)) for value in my_values]

# Change color
squarify.plot(sizes=my_values, alpha=.8, color=colors )
plt.axis('off')
plt.show()

Task06:场景案例显神通 - 图42

6. 作业

6.1 用Drugs数据集,做出面积图的多子图形式

注意,需要添加如下要素:
①添加每个子图标题,在子图右上方;
②添加整个画布的总标题,在画布左上方;
③添加X和Y轴的标签。

回答:

Dataset = pd.read_csv('data/Drugs.csv')
reports = Dataset.groupby(['YYYY', 'State'])['DrugReports'].sum().unstack('State')
palette = plt.get_cmap('tab10')
plt.figure(figsize=(15, 10))

for n, col in enumerate(reports.columns):
    plt.subplot(3,3, n+1)
    plt.fill_between(reports.index, reports[col], alpha=0.4)
    plt.plot(reports.index, reports[col])
    plt.xlim(2009.3,2017.3)
    plt.ylim(0,50000)

    plt.title(col, loc='right', fontsize=15, fontweight=0, color=palette(n))

plt.suptitle("DrugReports amounts of 5 states in past few years", 
             ha='right', fontsize=17)
plt.text(2014, -9500, 'Year', fontsize=15)
plt.text(1998, 60000, 'DrugReports', rotation='vertical', va='center', fontsize=15)
plt.subplots_adjust(hspace=0.3)
plt.show()

drure01.png

【拓展】使用面积图画堆叠面积图:

Dataset = pd.read_csv('data/Drugs.csv')
reports = Dataset.groupby(['YYYY', 'State'])['DrugReports'].sum().unstack('State')
reports_cumsum = reports.cumsum(axis=1)

palette = plt.get_cmap('tab10')
plt.figure(figsize=(10, 6))
for i, col in enumerate(reports_cumsum.columns[::-1]):
    plt.fill_between(reports_cumsum.index, reports_cumsum[col], color=palette(i), alpha=0.4)
    plt.plot(reports_cumsum.index, reports_cumsum[col], color=palette(i), label=col)

plt.xlabel('Year', fontsize=15)
plt.ylabel('DrugReports', fontsize=15)
plt.suptitle("DrugReports amounts of 5 states in past few years", 
             ha='right', fontsize=17)
plt.legend()
plt.show()

repo02.png