赞
踩
★★★ 本文源自AlStudio社区精品项目,【点击此处】查看更多精品内容 >>>
一图胜千言!在数据分析和数据挖掘项目报告中,相对于那些枯燥乏味的数据,切合主题的可视化图表往往能让人眼前一亮。
靓丽的图表不但能让人快速理解数据内部的相关性,也能让读者充分理解作者的通过数据要表达的观点和意图。
基于此,数据可视化技术也流行起来。
数据可视化技术致力于将抽象数据转换为图形、图像等可视化形式,帮助人们更好地理解和分析数据,从而发现问题、做出决策。
随着大数据时代的到来,数据可视化技术得到了广泛应用,包括商业分析、金融、医疗、科研、教育等多个领域。而Python是一种高效、易用的编程语言,因其语法简单、功能强大、可读性高、社区支持丰富等优点,被广泛应用于数据可视化技术。
人们常说的Python数据分析三大件: numpy, pandas, matplotlib,其中matplotlib就是主攻数据可视化。matplotlib通过内置的丰富的图表和易用的接口,帮助数据科学家进行数据关系挖掘。
本项目立足于实际的分析需求,总结一些常用的可视化图表,帮助读者理清常用的数据关系与图表之间的对应关系,让读者在数据分析报告中引入相关图表时能做到得心应手,事半功倍。
一个完整的数据分析报告其实是在讲一个完整的故事。
你的目的就是用逻辑和数据来说明你的观点。而一份有价值的数据报告除了故事的逻辑经得起推敲以外,重要的是你的客户能从数据中发现经营或者业务问题,能提供改善的建议。
因此,对于一份数据分析报告,
第一,要明确自己的分析目的和服务的对象
第二,就是要掌握一些常用的套路,这个是技术层面的东西,这个也是本项目所要关注的。
下面我们来用一个具体案例来说明这些常用的套路。
本项目使用一份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
对于一个单一变量,经常会通过横向比较(比较不同样本)和纵向比较(同一样本不同时期)来展示相关信息。结合图形要表达的内容,按照图表的属性,主要可以分成以下4种图形:
下面就分别举例说明
# 先导入必要的计算包并查看版本,最好将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
import matplotlib.font_manager as font_manager
fontpath = 'SimHei.ttf'
prop = font_manager.FontProperties(fname=fontpath)
print(prop.get_name())
SimHei
数据可视化任务中,最基础的一类任务是展示不同样本的同一属性的数量关系(比较大小,排序等)。
例如,展示不同地区的销售额,不同城市的人口,GDP等。
相对于枯燥的数字,柱状图可以更加形象直观的表达这些数量关系。
柱状图作为最常见的数据可视化方法,经常用于单个变量的数值横向比较。使用柱状图可以直观的感受到数据间的差异以及TopK成员。
下面举例说明
# 使用国家和地区的分组数据
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’]
可以看到该数据集将国家:
# 按照收入统计各个国家的数量, 使用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)
# 分别查看各个收入等级的国家都分布在哪些区域
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)
# 统计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 |
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')
# 棉棒图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>
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)
通过上面的柱状图可以很容易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()} )
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)
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')
上面阐述的几种图形均为单一变量不同样本之间的横向比较。
对于同一个样本同一个变量在不同时期不同地点等比较可以使用折线图进行展示其变化趋势。
同时在折线图中可以同时展示多个样本,便于比较。在上面的饼图中,我们看到中美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()
另外,也可以将他们放在一张图上,将数据放在同一个坐标轴下分析
# 为了图表美观,将前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>
除了上面的折线图,有时也可以使用坡道图表达前后两种状态的变化,下面表达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中的基本的点、线等元素组合在一起。
在表达一个变量的分布情况时,需要使用统计直方图,或者概率密度图。
另外也可以使用箱线图,它除了能观察数据分布是否集中以外,还能观察到离群点。
下面使用另外一份数据集进行演示
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)
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,'累积直方图')
以上是本项目中所有单变量相关的数据可视化图表, 展示了常用的柱状图,棉棒图,饼图,折现图,统计直方图,和箱线图的等使用
在数据分析除了查看单变量的分布和趋势外,经常会涉及到两个或多个变量的相关性分析,下面举例说明
对个多个变量之间的相关性分析,常用散点图,热力图等进行分析,下面举例说明
散点图常用于两个变量之间的相关性分析展示。在散点图中,一个变量为横坐标,另一个变量为纵坐标,利用散点的分布形态反映变量统计关系
散点图能直观表现出影响因素和预测对象之间的总体关系趋势。它不仅可传递变量间关系类型的信息,还能反映变量间关系的明确程度(即相关性的强弱)。
在有多个因变量的数据分析中,常使用散点图快速找出关键的变量以便于筛选数据进行下一步的分析。
但是要注意,散点图的相关性分析属于定性分析,可以直观的感受相关性的强弱,但是要定量分析,还需要配合其他工具。下面举例说明
# 对于混凝土数据,使用定性分析查看各个变量与因变量(强度)的关系
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>
绘制相关性热力图需要先计算相关系数,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>
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>
本项目主要是总结一些数据可视化的思路,让读者在做数据可视化时有一个方向。
参考文献
此文章为搬运
原项目链接
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。