★★★ 本文源自AlStudio社区精品项目,【点击此处】查看更多精品内容 >>>

1. 数据可视化技术的背景和意义

一图胜千言!在数据分析和数据挖掘项目报告中,相对于那些枯燥乏味的数据,切合主题的可视化图表往往能让人眼前一亮。

靓丽的图表不但能让人快速理解数据内部的相关性,也能让读者充分理解作者的通过数据要表达的观点和意图。
基于此,数据可视化技术也流行起来。

数据可视化技术致力于将抽象数据转换为图形、图像等可视化形式,帮助人们更好地理解和分析数据,从而发现问题、做出决策。

随着大数据时代的到来,数据可视化技术得到了广泛应用,包括商业分析、金融、医疗、科研、教育等多个领域。而Python是一种高效、易用的编程语言,因其语法简单、功能强大、可读性高、社区支持丰富等优点,被广泛应用于数据可视化技术。

人们常说的Python数据分析三大件: numpy, pandas, matplotlib,其中matplotlib就是主攻数据可视化。matplotlib通过内置的丰富的图表和易用的接口,帮助数据科学家进行数据关系挖掘。

本项目立足于实际的分析需求,总结一些常用的可视化图表,帮助读者理清常用的数据关系与图表之间的对应关系,让读者在数据分析报告中引入相关图表时能做到得心应手,事半功倍。

2. 数据集

一个完整的数据分析报告其实是在讲一个完整的故事。

你的目的就是用逻辑和数据来说明你的观点。而一份有价值的数据报告除了故事的逻辑经得起推敲以外,重要的是你的客户能从数据中发现经营或者业务问题,能提供改善的建议。

因此,对于一份数据分析报告,
第一,要明确自己的分析目的和服务的对象
第二,就是要掌握一些常用的套路,这个是技术层面的东西,这个也是本项目所要关注的。

下面我们来用一个具体案例来说明这些常用的套路。

本项目使用一份GDP(从世界银行官方网站上下载)数据一份混凝土强度数据。数据集已经挂载在data目录中,fork项目即可查看。

# 解压数据集
!unzip /home/aistudio/data/data223782/GDP_data.zip -d gdp_data # 解压一次即可
Archive:  /home/aistudio/data/data223782/GDP_data.zip
  inflating: gdp_data/CN_Metadata_Country_API_NY.GDP.MKTP.KD.ZG_DS2_zh_csv_v2_511634.csv  
  inflating: gdp_data/GDP_API_NY.GDP.MKTP.CD_DS2_en_csv_v2_559588.csv  
  inflating: gdp_data/Growth_API_NY.GDP.MKTP.KD.ZG_DS2_en_csv_v2_511423.csv  
  inflating: gdp_data/Metadata_Country_API_NY.GDP.MKTP.CD_DS2_en_csv_v2_559588.csv  
!mv /home/aistudio/data/data223786/Concrete_Data.xls  /home/aistudio
!tree - L /home/aistudio
- [error opening dir]
L [error opening dir]
/home/aistudio
├── Concrete_Data.xls
├── data
│   ├── data223782
│   │   └── GDP_data.zip
│   └── data223786
├── gdp_data
│   ├── CN_Metadata_Country_API_NY.GDP.MKTP.KD.ZG_DS2_zh_csv_v2_511634.csv
│   ├── GDP_API_NY.GDP.MKTP.CD_DS2_en_csv_v2_559588.csv
│   ├── Growth_API_NY.GDP.MKTP.KD.ZG_DS2_en_csv_v2_511423.csv
│   └── Metadata_Country_API_NY.GDP.MKTP.CD_DS2_en_csv_v2_559588.csv
├── main.ipynb
└── SimHei.ttf

4 directories, 8 files
  • GDP数据集中包含了4个csv文件,大致如下

  • 混凝土数据较为简单,后面有详细介绍

3. 单变量常用图形

对于一个单一变量,经常会通过横向比较(比较不同样本)和纵向比较(同一样本不同时期)来展示相关信息。结合图形要表达的内容,按照图表的属性,主要可以分成以下4种图形:

    1. 排序:使用柱状图,棉棒图等直观的表达大小,排名等数量关系, 同时可以方便的筛选TopK成员
    1. 组成(占比):表达不同样本在总体中的成分,经常用于2-8分析,即少量的样本占据了总数的大部分份额。使用饼图等占比图更加直观
    1. 分布: 常用统计直方图、概率密度图、箱线图等表达一个变量分布的紧密程度
    1. 变化: 使用折线图或者坡度图等表达变量的变化趋势

下面就分别举例说明

# 先导入必要的计算包并查看版本,最好将pandas升级到0.24以上
import numpy as np
import pandas as pd
import matplotlib as mpl
import seaborn as sns
import matplotlib.pyplot as plt

# 去掉警告,不影响程序运行
import warnings
warnings.filterwarnings('ignore')
plt.style.use('bmh') #图形显示风格, ggplot或者bmh, 比默认的好看

#省去plt.show(),直接出图
%matplotlib inline 

for model in np,pd,mpl,sns:
    print(model.__name__, model.__version__)
numpy 1.19.5
pandas 1.1.5
matplotlib 2.2.3
seaborn 0.10.0
  • 备注: aistudio中matplotlib不支持中文字体,需要单独下载后使用matplotlib中的font_manager进行设置
  • 本项目已经下载了好了黑体中文字体,保存在项目根目录,fork项目后即可使用
import matplotlib.font_manager as font_manager
fontpath = 'SimHei.ttf'
prop = font_manager.FontProperties(fname=fontpath)
print(prop.get_name())
SimHei

4. 单变量-数量比较及排序

数据可视化任务中,最基础的一类任务是展示不同样本的同一属性的数量关系(比较大小,排序等)。
例如,展示不同地区的销售额,不同城市的人口,GDP等。

相对于枯燥的数字,柱状图可以更加形象直观的表达这些数量关系。

柱状图作为最常见的数据可视化方法,经常用于单个变量的数值横向比较。使用柱状图可以直观的感受到数据间的差异以及TopK成员。
下面举例说明

  1. gdp数据中的Metadata_Country表中包含了各个国家按照人均GDP以及所在地区分组情况,使用柱状图进行简单分析
  2. GDP_API数据中包含了各个国家1960-2018所有的年份的GDP数据,使用柱状图输出2018Top10的国家
# 使用国家和地区的分组数据
country_group = pd.read_csv('gdp_data/Metadata_Country_API_NY.GDP.MKTP.CD_DS2_en_csv_v2_559588.csv')
country_group.head()
Country Code Region IncomeGroup SpecialNotes TableName Unnamed: 5
0 ABW Latin America & Caribbean High income NaN Aruba NaN
1 AFG South Asia Low income NaN Afghanistan NaN
2 AGO Sub-Saharan Africa Lower middle income NaN Angola NaN
3 ALB Europe & Central Asia Upper middle income NaN Albania NaN
4 AND Europe & Central Asia High income NaN Andorra NaN
print('国家按照地区划为为:\n', country_group['Region'].dropna().unique())
print('*' * 50)
print('国家按照收入等级划分为:\n', country_group['IncomeGroup'].dropna().unique())
国家按照地区划为为:
 ['Latin America & Caribbean' 'South Asia' 'Sub-Saharan Africa'
 'Europe & Central Asia' 'Middle East & North Africa'
 'East Asia & Pacific' 'North America']
**************************************************
国家按照收入等级划分为:
 ['High income' 'Low income' 'Lower middle income' 'Upper middle income']

le income’ ‘Upper middle income’]

可以看到该数据集将国家:

  • 按照地区分成7个区域, 包括东亚及泛太平洋、拉丁美洲和加勒比、欧洲及中亚、北美等
  • 按照人均GDP划分为高收入、中高收入、中低收入和低收入国家
# 按照收入统计各个国家的数量, 使用matplotlib柱状图展示
incoming_sta = country_group["IncomeGroup"].value_counts()
plt.figure(figsize=(10,6))
plt.bar(incoming_sta.index, incoming_sta.values, width=0.5)
plt.ylabel("国家数量", fontproperties=prop, fontsize=12)

# 添加数值标签
x = list(range(len(incoming_sta.index)))
y = incoming_sta.values
for i, j in zip(x, y):
    plt.text(i, j+0.5, j, ha='center', va='bottom', fontsize=16)

在这里插入图片描述

  • plt.bar是matplotlib的柱状图接口,另外可以使用plt.hbar展示横向的状态图
# 分别查看各个收入等级的国家都分布在哪些区域
incoming_class = country_group["IncomeGroup"].unique().tolist()
plt.figure(figsize=(15,8))

for i, cls in enumerate(incoming_class[:4]):
    plt.subplot(2,2,i+1)
    incoming_sta = country_group[country_group['IncomeGroup']==cls]["Region"].value_counts(ascending=True)
    plt.barh(incoming_sta.index, incoming_sta.values)
    plt.xticks(rotation=60)
    plt.ylabel("地区", fontproperties=prop, fontsize=10)
    plt.xlabel("国家数量", fontproperties=prop, fontsize=10)
    plt.title(cls + "_分布区域统计", fontproperties=prop, fontsize=12 )

plt.subplots_adjust(wspace=0.5, hspace=0.3)

在这里插入图片描述

  • 从上面这张统计图上可以看出高收入国家主要分布的欧洲和中东(这个符合印象)以及拉丁美洲地区(这点与常识不太符合,拉美很富裕吗?)
  • 低收入国家主要在非洲, 而且国家数量遥遥领先其他地区。非洲农业不发达(狗头)
  • 下面在具体到国家,使用柱状图可视化GDP Top10
# 统计2018GDP Top10国家, 由于GDP数据表包含地区,只统计国家可以将该表和地区表合并后将Income_Group列中na的数据删掉,这样只保留国家数据
df_gdp = pd.read_csv('gdp_data/GDP_API_NY.GDP.MKTP.CD_DS2_en_csv_v2_559588.csv' ) # gdp数据
df_group = pd.read_csv('gdp_data/CN_Metadata_Country_API_NY.GDP.MKTP.KD.ZG_DS2_zh_csv_v2_511634.csv' )
df_new = df_gdp.merge(df_group, how = 'left', on = 'Country Code')
df_new.head()
Country Name_x Country Code Indicator Name Indicator Code 1960 1961 1962 1963 1964 1965 ... 2014 2015 2016 2017 2018 2019 Country Name_y Region Income_Group Unnamed: 4
0 Aruba ABW GDP (current US$) NY.GDP.MKTP.CD NaN NaN NaN NaN NaN NaN ... 2.649721e+09 2.691620e+09 2.646927e+09 2.700559e+09 NaN NaN 阿鲁巴 NaN 高收入国家 NaN
1 Afghanistan AFG GDP (current US$) NY.GDP.MKTP.CD 537777811.1 548888895.6 546666677.8 751111191.1 800000044.4 1.006667e+09 ... 2.048487e+10 1.990711e+10 1.936264e+10 2.019176e+10 1.936297e+10 NaN 阿富汗 南亚 低收入国家 NaN
2 Angola AGO GDP (current US$) NY.GDP.MKTP.CD NaN NaN NaN NaN NaN NaN ... 1.457120e+11 1.161940e+11 1.011240e+11 1.221240e+11 1.057510e+11 NaN 安哥拉 撒哈拉以南非洲地区(不包括高收入) 中低等收入国家 NaN
3 Albania ALB GDP (current US$) NY.GDP.MKTP.CD NaN NaN NaN NaN NaN NaN ... 1.322825e+10 1.138693e+10 1.186135e+10 1.302506e+10 1.505888e+10 NaN 阿尔巴尼亚 欧洲与中亚地区(不包括高收入) 中高等收入国家 NaN
4 Andorra AND GDP (current US$) NY.GDP.MKTP.CD NaN NaN NaN NaN NaN NaN ... 3.350736e+09 2.811489e+09 2.877312e+09 3.013387e+09 3.236544e+09 NaN 安道尔共和国 NaN 高收入国家 NaN

5 rows × 68 columns

# 只展示数据中的最后10年
years = [str(i) for i in range(2009,2019)]
df_gdp_10year = df_new[['Country Name_x','Country Code' ,'Income_Group'] + years].dropna(subset = ['Income_Group']) # 筛选最近十年数据
# 数据太大,使用10billion作为单位
for year in years:
    df_gdp_10year[year] = df_gdp_10year[year] / 1e10

df_gdp_2018 = df_gdp_10year.sort_values(by = ['2018'], ascending= False).head(10).reset_index()
df_gdp_2018
index Country Name_x Country Code Income_Group 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018
0 249 United States USA 高收入国家 1444.890 1499.210 1554.260 1619.700 1678.480 1752.170 1821.930 1870.720 1948.540 2049.410
1 38 China CHN 中高等收入国家 510.170 608.716 755.150 853.223 957.041 1043.850 1101.550 1113.790 1214.350 1360.820
2 117 Japan JPN 高收入国家 523.138 570.010 615.746 620.321 515.572 485.041 438.948 492.667 485.995 497.092
3 53 Germany DEU 高收入国家 341.801 341.709 375.770 354.398 375.251 389.873 338.139 349.516 369.320 399.676
4 79 United Kingdom GBR 高收入国家 239.479 245.290 263.490 267.661 275.357 303.473 289.642 265.924 263.787 282.521
5 75 France FRA 高收入国家 269.022 264.261 286.141 268.383 281.108 285.217 243.821 247.129 258.629 277.754
6 107 India IND 中低等收入国家 134.189 167.562 182.305 182.764 185.672 203.913 210.359 229.043 265.255 272.632
7 114 Italy ITA 高收入国家 218.516 212.506 227.629 207.282 213.049 215.173 183.227 186.920 194.657 207.390
8 27 Brazil BRA 中高等收入国家 166.702 220.887 261.620 246.519 247.281 245.599 180.221 179.628 205.359 186.863
9 33 Canada CAN 高收入国家 137.115 161.354 178.914 182.397 184.202 180.148 155.290 152.671 164.687 171.251
  • GDP Top10中有中国,印度,巴西3个中等收入国家
plt.figure(figsize=(10,6))
plt.bar(df_gdp_2018["Country Name_x"], df_gdp_2018['2018'], width=0.5)
plt.xticks(rotation=45)
plt.ylabel('10 billion')
Text(0,0.5,'10 billion')

在这里插入图片描述

  • 如果觉得柱状图比较单调, 可以使用棉棒图,让图表更加简洁
  • 在棉棒图中可以将Baseline设置成平均值,即设置 bottom=data.mean,这样可以很容易区分出哪些国家在平均值之上
  • matplotlib中使用stem接口生成棉棒图,如下
# 棉棒图stem, 也成为茎图,火柴棒图等
plt.figure(figsize= (20,5))
plt.subplot(121)
markerline, stemlines, baseline = plt.stem(df_gdp_2018['Country Name_x'], df_gdp_2018['2018'], linefmt= '-', markerfmt= 'o', basefmt = '--',label='GDP/10 billion')

plt.xticks(rotation = 60)
plt.title("2018GDP排名前十国家,bottom=0", fontproperties=prop, fontsize=12)

#设置坐标标签标注和字体大小
plt.ylabel("10 billion",fontsize=10)
plt.xticks(fontsize=12,rotation=60)

# 添加数值标签
x = list(range(df_gdp_2018.shape[0]))
y = df_gdp_2018['2018']
for i, j in zip(x, y):
    plt.text(i, j+20, j, ha='center', va='bottom', fontsize=10)
plt.legend()

plt.subplot(122)
markerline, stemlines, baseline = plt.stem(df_gdp_2018['Country Name_x'], df_gdp_2018['2018'],linefmt='-',markerfmt='o',bottom = (df_gdp_2018['2018']).mean(),basefmt='--',label='GDP/10 billion')

plt.xticks(rotation = 30)
plt.title("2018GDP排名前十国家,bottom=Top10.mean", fontproperties=prop, fontsize=12)

#设置坐标标签标注和字体大小
plt.ylabel("10 billion",fontsize=10)
plt.xticks(fontsize=12,rotation=60)

plt.legend()
<matplotlib.legend.Legend at 0x7feef638cb90>

在这里插入图片描述

上面的图形可以看到以Top10GDP的平均值作为基准,只有中美在均线之上

这也从侧面反映中美两国的GDP体量和其他选手不在一个数量级上通过此直观的感受到两个GDP超级大国对其他国家的碾压

另外,柱状图除了以上的常规表达外,还有水平对比,堆叠,3D等变体,举例如下:

# 欧亚大陆历来是人类活动的中心,分别统计欧亚大陆两个地区的收入国家统计,并进行对比
country_group = pd.read_csv('gdp_data/Metadata_Country_API_NY.GDP.MKTP.CD_DS2_en_csv_v2_559588.csv')
gp_pivot = country_group.pivot_table(index = "IncomeGroup",columns = "Region",values = 'Country Code',aggfunc="count")
gp_pivot=gp_pivot.fillna(0)
gp_pivot
Region East Asia & Pacific Europe & Central Asia Latin America & Caribbean Middle East & North Africa North America South Asia Sub-Saharan Africa
IncomeGroup
High income 13.0 37.0 17.0 8.0 3.0 0.0 1.0
Low income 1.0 1.0 1.0 2.0 0.0 2.0 24.0
Lower middle income 13.0 4.0 4.0 5.0 0.0 4.0 17.0
Upper middle income 10.0 16.0 20.0 6.0 0.0 2.0 6.0
# 数据
data1 = gp_pivot['Europe & Central Asia'] # 欧洲和中东地区数据
data2 = gp_pivot['East Asia & Pacific'] # 东亚和泛太平洋地区数据
colors = ['#1f77b4', '#ff7f0e']

# 绘图
fig, ax = plt.subplots()
ax.barh(np.arange(data1.shape[0]), data1, color=colors[0], label='Europe & Central Asia')
ax.barh(np.arange(data2.shape[0]), -data2, color=colors[1], label='East Asia & Pacific')

# 坐标轴标签
ax.set_xlabel('国家数量', fontproperties=prop, fontsize=12)
ax.set_ylabel('收入等级', fontproperties=prop, fontsize=12)

# y轴刻度标签
ax.set_yticks(np.arange(data1.shape[0]))
ax.set_yticklabels(gp_pivot.columns.tolist())
# 美化
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.legend()
<matplotlib.legend.Legend at 0x7feef64683d0>

在这里插入图片描述

  • 通过对比就可以发现欧洲和中东地区的高收入国家数量占有绝对优势(占了工业革命和石油的光),但两者的低收入国家都比较少,亚洲地区发展相对均衡
  • 除了对比以外,柱状图也可以实现码垛堆叠效果,如下
# 数据
data = gp_pivot.values
# 类别
categories, labels = gp_pivot.columns,gp_pivot.index
# 颜色
colors = ['r', 'g', 'b','y']

#  将每个类别数据转换为百分比
totals = np.sum(data, axis=0)
data_percent = (data / totals) * 100

# 绘制百分比堆积柱形图
plt.figure(figsize=(18,6))
bottom = np.zeros(len(categories))
for i in range(len(data_percent)):
    plt.bar(categories, data_percent[i], bottom=bottom, color=colors[i % len(colors)], label=labels[i], width=0.4)
    bottom += data_percent[i]

# 设置坐标轴标签
plt.xlabel('Country&Region')
plt.ylabel('Percentage')
plt.legend()
<matplotlib.legend.Legend at 0x7feefe5d7b10>

在这里插入图片描述

  • 通过这个堆叠图,可以很容易的看到各个区域的收入国家分布情况
  • 另外,还可以使用3D柱状图展示,如下:
from mpl_toolkits.mplot3d import Axes3D
# 数据
data = gp_pivot.values
# 颜色
colors = ['r', 'g', 'b','y']
# 宽度
width = 0.4

# 绘图
fig = plt.figure(figsize=(15,8))
ax = fig.add_subplot(111,projection='3d')
for i in range(data.shape[0]):
    ax.bar(np.arange(data.shape[1]), data[i], zs=i, zdir='y', width=width, alpha=0.8)

# x轴刻度标签
ax.set_xticks(np.arange(data.shape[1]))
ax.set_xticklabels(gp_pivot.columns)

# y轴刻度标签
ax.set_yticks(np.arange(data.shape[0]))
ax.set_yticklabels(gp_pivot.index)

# 美化
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)

在这里插入图片描述

5. 单变量-组成(占比)

通过上面的柱状图可以很容易TopK成员,很自然就会想到TopK的占比情况。
因为现实情况中,经常会发现二八法则,即20%的成员贡献80%的成果。

在实际的分析中,经常使用华夫饼或者饼图等来分析占比情况,特别是在各个成员贡献差异较大的情况下更加直观。下面举例说明

# matplotlib中没有华夫饼图,需要单独安装
!pip install pywaffle
incoming_sta = country_group["IncomeGroup"].value_counts()
income_statistic = pd.DataFrame(incoming_sta).rename(columns={"IncomeGroup": "counts"})
income_statistic['percent'] = income_statistic['counts'] / sum(income_statistic['counts'].values.tolist())
income_statistic 
counts percent
High income 79 0.364055
Upper middle income 60 0.276498
Lower middle income 47 0.216590
Low income 31 0.142857
# 使用饼图分别查看各个收入国家的数量和GDP占比情况
import matplotlib.gridspec as gridspec
fig = plt.figure(figsize=(15,4))
gs = gridspec.GridSpec(1,6,wspace=2,hspace=0.5,)
# ax1 = fig.add_subplot(1,2,1)
ax1 = fig.add_subplot(gs[0,:2])
plt.pie(income_statistic['percent'], labels=income_statistic.index, explode= (0,0,0,0),autopct='%2.2f%%',textprops={'fontsize':10,'color':'black'},wedgeprops=dict(width=0.65,edgecolor='b'))
plt.title(" 按人均GDP划分收入国家总数占比-饼图", fontproperties=prop, fontsize=14)

from pywaffle import Waffle

# ax2 = fig.add_subplot(1,2,2)
ax2 = fig.add_subplot(gs[0,2:])
Waffle.make_waffle(
    ax=ax2,  # pass axis to make_waffle
    rows=10, 
    columns=100, 
    values=income_statistic['counts'].to_list(),
    vertical=True, 
    colors = ['b', 'r', 'y', 'g'],
    title={ 'label' : '按人均GDP划分收入国家总数占比-华夫饼图', 'fontproperties': prop, "fontsize":14},
    legend={'loc': 'upper right', 'labels':income_statistic.index.to_list()}
    )

在这里插入图片描述

  • 按照收入级别分类,统计GDP总量的百分比
res = df_new.groupby(['Income_Group'])["2018"].sum().reset_index()
res['2018'] = res['2018'] / df_gdp[df_gdp["Country Name"] == "World"]['2018'].values[0]
res['Income_Group'] = ["Lower-Mid Income", 'Upper-Mid Income', "Low Income", "High Income"] # 改成英文
res.iloc[2,1] = 1-res.iloc[0,1]-res.iloc[1,1]-res.iloc[3,1] # 数据中有部分国家收入等级为na, 更改low
res

Income_Group 2018
0 Lower-Mid Income 0.078172
1 Upper-Mid Income 0.271745
2 Low Income 0.027500
3 High Income 0.622583
import matplotlib.gridspec as gridspec
fig = plt.figure(figsize=(12,6))

ax1 = fig.add_subplot(121)
ax1.pie(income_statistic['percent'], labels=income_statistic.index, explode= (0.05,0.05,0.05,0.05),autopct='%2.2f%%',textprops={'fontsize':10,'color':'black'},wedgeprops=dict(width=0.65,edgecolor='b'))
ax1.set_title(" 按人均GDP划分收入国家总数占比", fontproperties=prop, fontsize=14)

ax2 = fig.add_subplot(122)
ax2.pie(res['2018'],labels=res['Income_Group'], explode= (0.,0.,0.,0.1), autopct='%2.2f%%', textprops={'fontsize':10,'color':'black'}, wedgeprops=dict(width=0.65,edgecolor='b'))
ax2.set_title(" 不同收入国家的总GDP占比", fontproperties=prop, fontsize=14)
Text(0.5,1,' 不同收入国家的总GDP占比')

在这里插入图片描述

  • 饼图中可以设置explode这个参数来强调某一部分

  • 结合这两张饼图就可以发现,总数占比36%的高收入国家创造了世界GDP总量的62%+!而总数将近15%的低收入国家总共创造的GDP不到1%!贫富差距很大!

我们可以不关心其他国家,但是中美两个超级腕选手需要给予特别的关注。
下面使用饼图来表达中美两国GDP在世界总量中的占比及其变化情况
要完成此,需要使用一开始GDP中world数据计算占比情况

# 先获取世界GDP2009-2018总量
years = [str(i) for i in range(2009,2019)]
world = df_gdp[df_gdp["Country Name"] == "World"]
world = world[["Country Name"] + years].reset_index()
for y in years:
    world[y] = world[y] / 1e10
world
index Country Name 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018
0 257 World 6034.01 6603.69 7335.74 7504.57 7718.96 7929.61 7500.31 7610.28 8089.13 8580.44
# 占比计算
us_percent, china_percent,us_china_percent = [],[],[]
years= ['2009', '2010','2011','2012','2013','2014','2015','2016','2017','2018']
for year in years:
    us_percent.append(df_gdp_2018[year][0]/world[year][0])
    china_percent.append(df_gdp_2018[year][1]/world[year][0])
    us_china_percent.append((df_gdp_2018[year][0] + df_gdp_2018[year][1])/world[year][0])
gdp_percent = pd.DataFrame({"Year": years, "US": us_percent, "China": china_percent, "Us+China": us_china_percent})
gdp_percent['other'] = 1- gdp_percent['Us+China']
gdp_percent
Year US China Us+China other
0 2009 0.239458 0.084549 0.324007 0.675993
1 2010 0.227026 0.092178 0.319204 0.680796
2 2011 0.211875 0.102941 0.314816 0.685184
3 2012 0.215828 0.113694 0.329522 0.670478
4 2013 0.217449 0.123986 0.341435 0.658565
5 2014 0.220965 0.131640 0.352605 0.647395
6 2015 0.242914 0.146867 0.389781 0.610219
7 2016 0.245815 0.146353 0.392168 0.607832
8 2017 0.240884 0.150121 0.391005 0.608995
9 2018 0.238847 0.158596 0.397442 0.602558
plt.figure(figsize=(21,4))
for index, year in enumerate([ '2010','2012','2014','2016','2018']):
    plt.subplot(1,5, index+1)
    plt.pie(gdp_percent.loc[index*2 + 1][3:], labels=["US+China", "Others"], explode= (0,0),autopct='%2.2f%%',textprops={'fontsize':12,'color':'black'},wedgeprops=dict(width=0.7,edgecolor='b'))
    plt.title("Year:" + year, fontsize=14, weight=500)

在这里插入图片描述

plt.figure(figsize=(21,4))
for index, year in enumerate([ '2010','2012','2014','2016','2018']):
    plt.subplot(1,5, index+1)
    plt.pie(gdp_percent.iloc[index*2 + 1,[1,2,4]], labels=["US", "China", "Others"], explode= (0,0,0),autopct='%2.2f%%',textprops={'fontsize':10,'color':'black'},wedgeprops=dict(width=0.7,edgecolor='b'))
    plt.title("Year:" + year, fontsize=14, weight=500)

在这里插入图片描述

  • 从上面的图中可以明显感受中美两个超级大国GDP占了世界总量将近40%, 而且在逐年递增,这个是比较吓人的。似乎牌桌上就两个玩家,其他都是打酱油的
  • 第二张图中可以看到中国GDP在世界GDP中的占比在稳步的提升,但是美国的比重保持稳定,变化较小
  • 使用饼图可以直观的感受二八法则,但是对于变化趋势的描述不够细致,这个需要用到下面所描述的折线图,从中可以直观感受趋势的变化
  • 另外,在实际演示时,也可以使用饼图叠加字符串,对某一占比进行描述,进行特殊强调,如下:
actual_value = 1361
colours = ['#3da9d4', '#063b63']
 
fig = plt.figure(figsize=(10,10), facecolor='#25253c')
ax = fig.add_subplot(1,1,1)
 
pie = ax.pie([15.86, 100-15.86], colors=colours, startangle=90, labeldistance=1.15, counterclock=False)
 
pie[0][1].set_alpha(0.4)
 
# 添加内圆环
centre_circle = plt.Circle((0, 0), 0.6, fc='#25253c')
 
# Adding the circles to the chart
fig.gca().add_artist(centre_circle)
 
# 添加文字
centre_text = f'${actual_value}(10 Billion)'
centre_text_line_2 = f'Total GDP 2018'
 
ax.text(0,0.1, centre_text, horizontalalignment='center', 
            verticalalignment='center', 
            fontsize=25, fontweight='bold',
            color='white')
ax.text(0,-0.1, centre_text_line_2, horizontalalignment='center', 
            verticalalignment='center', 
            fontsize=23, fontweight='bold',
            color='grey')
Text(0,-0.1,'Total GDP 2018')

在这里插入图片描述

6. 单变量-变化

上面阐述的几种图形均为单一变量不同样本之间的横向比较。

对于同一个样本同一个变量在不同时期不同地点等比较可以使用折线图进行展示其变化趋势。

同时在折线图中可以同时展示多个样本,便于比较。在上面的饼图中,我们看到中美GDP占比情况,下面使用折线图查看其变化趋势

# 仍然使用上面GDP Top10的数据来演示
df_gdp_2018
index Country Name_x Country Code Income_Group 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018
0 249 United States USA 高收入国家 1444.890 1499.210 1554.260 1619.700 1678.480 1752.170 1821.930 1870.720 1948.540 2049.410
1 38 China CHN 中高等收入国家 510.170 608.716 755.150 853.223 957.041 1043.850 1101.550 1113.790 1214.350 1360.820
2 117 Japan JPN 高收入国家 523.138 570.010 615.746 620.321 515.572 485.041 438.948 492.667 485.995 497.092
3 53 Germany DEU 高收入国家 341.801 341.709 375.770 354.398 375.251 389.873 338.139 349.516 369.320 399.676
4 79 United Kingdom GBR 高收入国家 239.479 245.290 263.490 267.661 275.357 303.473 289.642 265.924 263.787 282.521
5 75 France FRA 高收入国家 269.022 264.261 286.141 268.383 281.108 285.217 243.821 247.129 258.629 277.754
6 107 India IND 中低等收入国家 134.189 167.562 182.305 182.764 185.672 203.913 210.359 229.043 265.255 272.632
7 114 Italy ITA 高收入国家 218.516 212.506 227.629 207.282 213.049 215.173 183.227 186.920 194.657 207.390
8 27 Brazil BRA 中高等收入国家 166.702 220.887 261.620 246.519 247.281 245.599 180.221 179.628 205.359 186.863
9 33 Canada CAN 高收入国家 137.115 161.354 178.914 182.397 184.202 180.148 155.290 152.671 164.687 171.251
# 使用折线图分别查看这10个国家的GDP走势
plt.figure(figsize=(18,6))
color = ['g', 'r', 'b', 'y', 'c']
mark = [".", 'o', "D", 's', 'p']
for i in range(10):
    plt.subplot(2,5,i+1)
    df_gdp_2018.iloc[i,4:].plot(label = df_gdp_2018['Country Name_x'][i], marker = mark[i%5], color = color[i%5])
    plt.legend()

在这里插入图片描述

  • 上面使用10张子图分别显示,简单明了
  • 通过这张图可以看到这10年,除了中美一直在增长外,印度也在稳步崛起,如果这种趋势保持不变,印度会慢慢超越英法进入前5

另外,也可以将他们放在一张图上,将数据放在同一个坐标轴下分析

# 为了图表美观,将前3名(美中日放在一起), 另外成员放在另外一张图
plt.figure(figsize=(20,6))
marks = ["o", "s", "*",".","D","o", "s",]
ls = ['-', "-.", "--",":","-.",'-', "-."]
plt.subplot(121)
for i in range(3):
    
    df_gdp_2018.iloc[i,4:].plot(label = df_gdp_2018['Country Name_x'][i], marker = marks[i], ls=ls[i])
plt.legend()

plt.subplot(122)
for i in range(3,10):
    df_gdp_2018.iloc[i,4:].plot(label = df_gdp_2018['Country Name_x'][i],marker = marks[i-3], ls=ls[i-3])

plt.legend()
<matplotlib.legend.Legend at 0x7ff1dc3bcf90>

在这里插入图片描述

  • 在同一张图上刻画,更容易发现:
  1. 中美强劲增长,其他玩水摸鱼
  2. 中国在2010超过日本后,与日本的差距越来越大
  3. 中国赶超加快,与美国的差距越来越小

除了上面的折线图,有时也可以使用坡道图表达前后两种状态的变化,下面表达2013和2018前后的GDP变化,为了显示,只选择Top5进行展示

import matplotlib.lines as mlines
fig, ax = plt.subplots(1,1,figsize=(8,6))
# Vertical Lines
ax.vlines(x=1, ymin=50, ymax=2500, color='black', alpha=0.7, linewidth=1, linestyles='dotted')
ax.vlines(x=3, ymin=50, ymax=2500, color='black', alpha=0.7, linewidth=1, linestyles='dotted')

# Points
ax.scatter(y=df_gdp_2018['2013'][:5], x=np.repeat(1, 5), s=10, color='black', alpha=0.7)
ax.scatter(y=df_gdp_2018['2018'][:5], x=np.repeat(3, 5), s=10, color='black', alpha=0.7)

def newline(p1, p2, color='black'):
    ax = plt.gca()
    l = mlines.Line2D([p1[0],p2[0]], [p1[1],p2[1]], color='red' if p1[1]-p2[1] > 0 else 'green', marker='o', markersize=6)
    ax.add_line(l)
    return l

# Line Segmentsand Annotation
for p1, p2, c in zip(df_gdp_2018['2013'][:5], df_gdp_2018['2018'][:5], df_gdp_2018['Country Name_x'][:5]):
    newline([1,p1], [3,p2])
    ax.text(1-0.05, p1, c + ', ' + str(round(p1)), horizontalalignment='right', verticalalignment='center', fontdict={'size':10})
    ax.text(3+0.05, p2, c + ', ' + str(round(p2)), horizontalalignment='left', verticalalignment='center', fontdict={'size':10})

# 'Before' and 'After' Annotations
ax.text(1-0.05, 2400, 'YEAR-2013', horizontalalignment='right', verticalalignment='center', fontdict={'size':12, 'weight':500})
ax.text(3+0.05, 2400, 'YEAR-2018', horizontalalignment='left', verticalalignment='center', fontdict={'size':12, 'weight':500})

# Decoration
ax.set_title("Slopechart: Top 5 GDP Comparingbetween 2013 vs 2018", fontdict={'size':14})
ax.set(xlim=(0,4), ylim=(0,2500), ylabel='GDP')
ax.set_xticks([1,3])
ax.set_xticklabels(["2013", "2018"])
plt.yticks(np.arange(200, 2500, 800), fontsize=10)

# Lighten borders
plt.gca().spines["top"].set_alpha(.0)
plt.gca().spines["bottom"].set_alpha(.0)
plt.gca().spines["right"].set_alpha(.0)
plt.gca().spines["left"].set_alpha(.0)
plt.show()

在这里插入图片描述

上面的坡道图本质上就是使用matplotlib中的基本的点、线等元素组合在一起。

7. 单变量 - 分布

在表达一个变量的分布情况时,需要使用统计直方图,或者概率密度图。

另外也可以使用箱线图,它除了能观察数据分布是否集中以外,还能观察到离群点。

下面使用另外一份数据集进行演示

df = pd.read_excel("/home/aistudio/Concrete_Data.xls")
df.head()
Cement (component 1)(kg in a m^3 mixture) Blast Furnace Slag (component 2)(kg in a m^3 mixture) Fly Ash (component 3)(kg in a m^3 mixture) Water (component 4)(kg in a m^3 mixture) Superplasticizer (component 5)(kg in a m^3 mixture) Coarse Aggregate (component 6)(kg in a m^3 mixture) Fine Aggregate (component 7)(kg in a m^3 mixture) Age (day) Concrete compressive strength(MPa, megapascals)
0 540.0 0.0 0.0 162.0 2.5 1040.0 676.0 28 79.986111
1 540.0 0.0 0.0 162.0 2.5 1055.0 676.0 28 61.887366
2 332.5 142.5 0.0 228.0 0.0 932.0 594.0 270 40.269535
3 332.5 142.5 0.0 228.0 0.0 932.0 594.0 365 41.052780
4 198.6 132.4 0.0 192.0 0.0 978.4 825.5 360 44.296075

这是一份混凝土数据,各个字段的具体含义如下:

原数据集中的字段名称(列明)太长,我们可以将其简化,变化后期展示:可以简化字段名称

df.columns = ['Cement', 'FurSlag', 'FlyAsh', 'Water', 'Superplasticizer', \
              'CoAggregate', 'FineAggregate', 'Age', 'Strength/Mpa']
df.head()
Cement FurSlag FlyAsh Water Superplasticizer CoAggregate FineAggregate Age Strength/Mpa
0 540.0 0.0 0.0 162.0 2.5 1040.0 676.0 28 79.986111
1 540.0 0.0 0.0 162.0 2.5 1055.0 676.0 28 61.887366
2 332.5 142.5 0.0 228.0 0.0 932.0 594.0 270 40.269535
3 332.5 142.5 0.0 228.0 0.0 932.0 594.0 365 41.052780
4 198.6 132.4 0.0 192.0 0.0 978.4 825.5 360 44.296075
# 查看强度的分布情况
plt.figure(figsize=(15,6))
plt.subplot(121)
df['Strength/Mpa'].plot(kind = 'hist', width = 3.5)
plt.xlabel('强度/Mpa', fontproperties=prop, fontsize=13) # 中文字体显示
plt.title('产品强度的概率密度分布',fontproperties=prop, fontsize=15)

plt.subplot(122)
plt.boxplot(df['Strength/Mpa'])
plt.title('强度的箱线图',fontproperties=prop, fontsize=15)
Text(0.5,1,'强度的箱线图')

在这里插入图片描述

因为强度是一个单一的变量,我们可以使用折线图(变化缺失)、概率密度图(分布情况)、箱线图等进行展示。对于折线图,通常会有一个时间维度来查看变量随着时间的变化的变化趋势,这里数据集中没有时间数据。我们就用概率分布和箱线图来查看,从中我们可以发现:

第一张图中,由1000多个样本组成的概率分布,大概可以看到强度数值成正态分布,均值大概位置30-40之间,但是有个别强度较大(大于70),远离均值形成一个长尾趋势

在第二张的箱线图中,直接将大于80的那一部分样本作为异常值来处理。同样的,可以将每个变量的箱线图画出进行分析

# 使用箱线图查看一下各个变量的分布
plt.figure(figsize=(15,6)) # 设置一张较大的图,容得下8张小图

for i, feature in enumerate(list(df.columns[:-1])):
    plt.subplot(2,4,i+1)
    plt.boxplot(df[feature], widths=0.2)
    plt.title(feature,  fontsize=13)

在这里插入图片描述

  • 在实际应用中,箱线图常用于判断异常值。相对于概率分布图而言,箱形图判断异常值的标准以四分位数和四分位距为基础,四分位数具有一定的耐抗性,多达25%的数据可以变得任意远而不会很大地扰动四分位数,所以异常值不能对这个标准施加影响,箱形图识别异常值的结果比较客观。
  • 对比上面的两幅图可以看到产品的强度值有一定的长尾现象,表现的箱线图上就是有部分的产品强度较高,超出上限
  • 另外,在统计直方图中也可以将概率密度图叠加
plt.figure(figsize=(15,6))
plt.subplot(121)
plt.hist(df['Cement'], bins = 10, rwidth=0.9, density=True, label = "Hist")
sns.kdeplot(df['Cement'], label = 'Density')
plt.title("直方图和概率密度图叠加展示", fontproperties=prop, fontsize=13)

plt.subplot(122)
plt.hist(df['Cement'], bins = 15, rwidth=0.9, density=True, cumulative=True)
plt.ylabel("累计频率", fontproperties=prop, fontsize=11)
plt.title("累积直方图", fontproperties=prop, fontsize=13)
Text(0.5,1,'累积直方图')

在这里插入图片描述

以上是本项目中所有单变量相关的数据可视化图表, 展示了常用的柱状图,棉棒图,饼图,折现图,统计直方图,和箱线图的等使用

在数据分析除了查看单变量的分布和趋势外,经常会涉及到两个或多个变量的相关性分析,下面举例说明

8. 多变量相关性分析

对个多个变量之间的相关性分析,常用散点图,热力图等进行分析,下面举例说明

8.1 散点图

散点图常用于两个变量之间的相关性分析展示。在散点图中,一个变量为横坐标,另一个变量为纵坐标,利用散点的分布形态反映变量统计关系

散点图能直观表现出影响因素和预测对象之间的总体关系趋势。它不仅可传递变量间关系类型的信息,还能反映变量间关系的明确程度(即相关性的强弱)。

在有多个因变量的数据分析中,常使用散点图快速找出关键的变量以便于筛选数据进行下一步的分析。

但是要注意,散点图的相关性分析属于定性分析,可以直观的感受相关性的强弱,但是要定量分析,还需要配合其他工具。下面举例说明

# 对于混凝土数据,使用定性分析查看各个变量与因变量(强度)的关系
df = pd.read_excel("/home/aistudio/Concrete_Data.xls")
df.columns = ['Cement', 'FurSlag', 'FlyAsh', 'Water', 'Superplasticizer', \
              'CoAggregate', 'FineAggregate', 'Age', 'Strength/Mpa']

plt.figure(figsize=(20,12)) # 设置一张较大的图,容得下8张小图

for i, feature in enumerate(list(df.columns[:-1])):
    plt.subplot(2,4,i+1)
    plt.scatter(df[feature], df['Strength/Mpa'])
    plt.xlabel(feature, fontproperties=prop, fontsize=13 )
    plt.ylabel('强度/Mpa',fontproperties=prop, fontsize=13)

在这里插入图片描述

从这些图大概可以得知:

水泥含量cement和superplasticizer(减水剂含量)和强度有较好的正相关关系

water(含水量)和强度有较好的负相关关系

矿渣和龄期和强度没有关系,并且龄期好像还是一个离散型的变量

另外,也可以使用seaborn中的相关散点图接口,功能更多, 举例如下

df.head()
Cement FurSlag FlyAsh Water Superplasticizer CoAggregate FineAggregate Age Strength/Mpa
0 540.0 0.0 0.0 162.0 2.5 1040.0 676.0 28 79.986111
1 540.0 0.0 0.0 162.0 2.5 1055.0 676.0 28 61.887366
2 332.5 142.5 0.0 228.0 0.0 932.0 594.0 270 40.269535
3 332.5 142.5 0.0 228.0 0.0 932.0 594.0 365 41.052780
4 198.6 132.4 0.0 192.0 0.0 978.4 825.5 360 44.296075
# 选择"水泥含量", "减水剂含量", "含水量"三个变量, 并添加和强度的拟合线进行展示
features = ["Cement", "Superplasticizer", "Water"]
for index, feature in enumerate(features):
    ax = sns.lmplot(x = features[index], y = "Strength/Mpa", data=df, height=4, aspect=1.5)
    plt.xlabel(features[index], fontproperties=prop, fontsize=13)
    plt.ylabel("强度/Mpa",fontproperties=prop, fontsize=13 )


在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

# 另外,还可以分类变量, 比如在原始数据中我们筛选龄期=(3,7,28,56)几个值后将龄期作为分类变量
con1 = df["Age"] <= 56
con2 = df["Age"] >=3
new_data = df.loc[con1 & con2].reset_index()
new_data.head()
index Cement FurSlag FlyAsh Water Superplasticizer CoAggregate FineAggregate Age Strength/Mpa
0 0 540.0 0.0 0.0 162.0 2.5 1040.0 676.0 28 79.986111
1 1 540.0 0.0 0.0 162.0 2.5 1055.0 676.0 28 61.887366
2 7 380.0 95.0 0.0 228.0 0.0 932.0 594.0 28 36.447770
3 8 266.0 114.0 0.0 228.0 0.0 932.0 670.0 28 45.854291
4 9 475.0 0.0 0.0 228.0 0.0 932.0 594.0 28 39.289790
# 另外,还可以分类变量, 比如在原始数据中我们筛选龄期=(3,7,28,56)几个值后将龄期作为分类变量
sns.lmplot(x = "Water", y="Strength/Mpa", data=new_data, col="Age" )
<seaborn.axisgrid.FacetGrid at 0x7ff1dc382350>

在这里插入图片描述

# 另外,还可以分类变量, 比如在原始数据中我们筛选龄期=(3,7,28,56)几个值后将龄期作为分类变量
sns.lmplot(x = "Cement", y="Strength/Mpa", data=new_data, col="Age" )
<seaborn.axisgrid.FacetGrid at 0x7ff1dc754690>

在这里插入图片描述

  • 可以发现,当固定一个变量后,分析另外两个变量之间的关系会更加的合理, 相关性也更加明显

8.2 相关性热力图

绘制相关性热力图需要先计算相关系数,pandas中直接使用df.corr即可, 默认采用的是pearson系数。

corr = df.corr()
corr
Cement FurSlag FlyAsh Water Superplasticizer CoAggregate FineAggregate Age Strength/Mpa
Cement 1.000000 -0.275193 -0.397475 -0.081544 0.092771 -0.109356 -0.222720 0.081947 0.497833
FurSlag -0.275193 1.000000 -0.323569 0.107286 0.043376 -0.283998 -0.281593 -0.044246 0.134824
FlyAsh -0.397475 -0.323569 1.000000 -0.257044 0.377340 -0.009977 0.079076 -0.154370 -0.105753
Water -0.081544 0.107286 -0.257044 1.000000 -0.657464 -0.182312 -0.450635 0.277604 -0.289613
Superplasticizer 0.092771 0.043376 0.377340 -0.657464 1.000000 -0.266303 0.222501 -0.192717 0.366102
CoAggregate -0.109356 -0.283998 -0.009977 -0.182312 -0.266303 1.000000 -0.178506 -0.003016 -0.164928
FineAggregate -0.222720 -0.281593 0.079076 -0.450635 0.222501 -0.178506 1.000000 -0.156094 -0.167249
Age 0.081947 -0.044246 -0.154370 0.277604 -0.192717 -0.003016 -0.156094 1.000000 0.328877
Strength/Mpa 0.497833 0.134824 -0.105753 -0.289613 0.366102 -0.164928 -0.167249 0.328877 1.000000
plt.figure(figsize=(12,10))
sns.heatmap(corr, annot=True)
<matplotlib.axes._subplots.AxesSubplot at 0x7fccb4e16910>

在这里插入图片描述

  • 相对于散点图,相关系数更加精确,可以将模糊的概念数据化
  • 从热力图中可以看到,和强度最相关的是水泥含量,减水剂,龄期以及含水量,这个和散点图得到结果稍微不一样,这个是因为原始数据中龄期有部分数据太大,属于离群点(干扰数据, 可以结合箱线图得出该结论)
  • 下面可将龄期大于56天数据去掉再查看分析
df_age56 = df[df['Age'] <= 56]
df_age56.head()
Cement FurSlag FlyAsh Water Superplasticizer CoAggregate FineAggregate Age Strength/Mpa
0 540.0 0.0 0.0 162.0 2.5 1040.0 676.0 28 79.986111
1 540.0 0.0 0.0 162.0 2.5 1055.0 676.0 28 61.887366
7 380.0 95.0 0.0 228.0 0.0 932.0 594.0 28 36.447770
8 266.0 114.0 0.0 228.0 0.0 932.0 670.0 28 45.854291
9 475.0 0.0 0.0 228.0 0.0 932.0 594.0 28 39.289790
# 先定量分析,通过pariplot查看
plt.figure(figsize=(18,10))

for i, feature in enumerate(list(df_age56.columns[:-1])):
    plt.subplot(2,4,i+1)
    plt.scatter(df_age56[feature], df_age56['Strength/Mpa'])
    plt.xlabel(feature, fontsize=13)
    plt.ylabel('Strength/Mpa', fontsize=13)

在这里插入图片描述

将龄期较大的样本去除后,龄期和强度的相关性明朗许多了!在定量计算看一看

corr = df_age56.corr()
plt.figure(figsize=(12,10))
sns.heatmap(corr, annot=True)
<matplotlib.axes._subplots.AxesSubplot at 0x7fefa6ffc150>

在这里插入图片描述

  • 去除噪声后,龄期和强度相关性更强了。和上面的定性分析统一起来了

9. 总结:

本项目主要是总结一些数据可视化的思路,让读者在做数据可视化时有一个方向。

参考文献

  1. Top 50 matplotlib Visualizations
  2. 透过各国GDP指标的变迁,窥探世界政治格局的演变

此文章为搬运
原项目链接

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐