matplotlib散点图点大小_Matplotlib:数据可视化利器

Matplotlib:数据可视化利器

来源: https://towardsdatascience.com/matplotlib-making-data-visualization-interesting-8bac1eb3d25c

作者: Karan Bhanot

翻译:老齐


数据可视化是理解数据并从中得出推论的关键步骤,虽然我们可以逐行地仔细检查数据,但这是一项冗长乏味的工作,而且不会凸显全局特征。另一方面,视觉效果很重要,以一目了然的形式定义数据,让浏览者有直观体验。

Matplotlib是Python的图库,可以生成各种格式的高质量的出版物图形和跨平台的交互环境(matplotlib.org)。《跟老齐学Python:数据分析》一书中有专门的章节对其使用方法进行了详细阐述。

3f425bc17389156852e4c9d7c6dd8ee4.png

Matplotlib是一个基本的库,它能实现各种平面图形,并以标签、标题、字体大小等形式进行有个性的自定义。为了更好地理解Matplotlib,我从Kaggle网站获取了人口密度数据集,并开始创建自己的可视化效果。本文重点介绍我绘制的平面图,包括自定义和我从数据中得出的推断。

完整的代码保存在了GitHub仓库(https://github.com/kb22/Visualization-using-Matplotlib),供参考。开始吧!

导入库

import numpy as npimport pandas as pdimport matplotlib.pyplot as pltimport matplotlib.cm as cm%matplotlib inline

首先需要导入所有必需的库。导入NumpyPandas库来处理数据,然后导入matplotlib 并使用其模块pyplot来绘制图形,把cm用作调色板。语句%matplotlib inline 确保所有绘图都以嵌入方式显示在浏览器页面上。

导入数据集

文件dataset.csv的前四行不是必需的,因此我们可以在跳过前四行的同时将数据导入,然后使用head(5)函数查看部分数据。

我们立即看到了一些无关的列。保留Country Name ,删除Country Code列。正如我们所知道的,我们正在处理人口密度问题,所以可以删除 Indicator Name列和Indicator Code列。接下来,19602016 的列具有NaN 值,NaN 代表缺失值,应该删除这些列,因为它们没有提供任何信息。最后,还有一个未命名的列,它也有NaN 值,因此我们也可以删除Unnamed: 61

dataset.drop(['Country Code', 'Indicator Name', 'Indicator Code', '1960', '2016', 'Unnamed: 61'],             axis = 1, inplace = True)dataset.dropna(how = 'any', axis = 0, inplace = True)dataset.isnull().sum()

我们使用dropna方法删除可能有缺失值的行,并使用dataset.isnull().sum()检查所有的列是否有缺失值,结果显示所有的缺失值均显示为0。

已经做好了准备,数据可视化,只欠东风。

6784b7b72f19de784fb3c8783e79b42e.png

可视化

现在,我们将使用Matplotlib创建平面图,并使用可视化方法得出有意义的结论。

曲线图

我们首先用曲线图分析Aruba的多年来的人口密度,x轴是年份,y轴是人口密度。

dataset.columns[1:]作为X的值,它选择了除第一列之外的所有列,因为我们只需要年份,而不需要Country Name列。接下来,以dataset.iloc[0][1:]为y的值,它选择第一列,即Aruba这个国家名、以及除第一列之外的所有列。国家和地区名称可以使用dataset[0][0]得到。为了绘制图表,我们仅仅使用plot函数,并将x轴和y轴的参数分别定义为x和y。

x = dataset.columns[1:]y = dataset.iloc[0][1:]country = dataset.iloc[0][0]plt.plot(x, y)

恭喜!!第一个平面图完成了!!

390b8e8ff21c906459626cd18d357a67.png

这张图有一定作用,但很难理解。坐标轴上没有标签,并且横轴的值重叠了。这就是Matplotlib中自定义的功能派上用场的地方,让我们来把它变得更容易解读。

plt.rcParams['figure.figsize'] = (30, 30)plt.rcParams['font.size'] = '20'plt.title('Population density of ' + country + ' over the years')plt.xlabel('Years')plt.xticks(rotation = '90')plt.ylabel('Population density (People per sq. Km)')plt.plot(x, y, linewidth = 4)

rcParams修改图形大小、字体大小等等。然后我们添加一个标题、x标签和y标签。xticks允许我们定义文本的旋转角度,它设置为90度。然后再次绘制并把行的宽度设置为4。

4f451d025ad7815061e0beae18d4c776.png

现在可以看到,只要稍加修改,图形就更具描述性。

从上图可以看出,到20世纪80年代,人口密度一直在稳步上升。从20世纪90年代起,人口密度急剧上升,并继续保持着同样的增长,直到21世纪中期才趋于稳定。

我们也可以用曲线图来观察不同国家和地区之间的趋势,选取前5个国家和地区,比较它们的人口密度增长。由于现在处理的是一个图形上的多条线,应该为每个国家和地区设置不同的颜色,还应该定义一个颜色与国家和地区对应关系的图例。

x = dataset.columns[1:]colors = cm.rainbow(np.linspace(0, 1, 5))for index in range(5):    y = dataset.iloc[index][1:]    plt.plot(x,              y,              c = colors[index],             label = dataset.iloc[index][0],             linewidth = 4)    plt.title('Comparing population density of various conutries')    plt.xlabel('Years')    plt.xticks(rotation = '90')    plt.ylabel('Population Density')    plt.legend(prop = {'size': 24})

在这里,我们循环遍历数据集的前5行,并且把值绘制为曲线图。我们使用matplotlibcm包及其rainbow方法来设置。在plot方法中,必须要设置label参数的值,因为它可以确保在启用图例时,显示图例名称。使用legend()方法启用图例,我在方法中指定了一个属性,即大小为24

29e196e6c1c8d066913d19ef4680a273.png

我们可以看到,阿鲁巴、安道尔、阿富汗、安哥拉和阿尔巴尼亚这5个国家和地区的人口密度都有所上升。

现在我们已经把所有的线图都绘制在一个平面图上,很容易看出阿鲁巴的人口密度一直高于其他4个国家/地区。

ac53be118d0170d0fea89848370dca0d.png

柱形图

使用带有相关参数的bar()方法可以轻松创建柱形图。先绘制2015年所有国家和地区的人口密度图。

countries = dataset['Country Name']populationDensity2015 = dataset['2015']plt.xticks(rotation = '90')plt.bar(countries, populationDensity2015, color = cm.rainbow(np.linspace(0, 1, len(countries))))
3fb7bbce1ed8b1c540a646d9fce373ff.png

虽然这个平面图确实试图在同一时间提供很多信息,但同样的原因使它缺乏任何有用的信息。在x轴标签中有太多重叠,使得整个图示凌乱而无用。我们不仅需要可视化数据集,而且还需要巧妙地可视化数据集的重要部分。更好的做法是把人口密度最大的前10个国家和地区排序,然后再来看一看这些国家和地区。

top10 = dataset.sort_values('2015', ascending = False).head(10)plt.xticks(rotation = '45')plt.title('Population Density of 10 most densely populated countries for the year 2015')plt.xlabel('Countires')plt.ylabel('Population Density')plt.bar(top10['Country Name'],        top10['2015'],        color = cm.rainbow(np.linspace(0, 1, len(top10))))

根据2015年的人口密度,用sort_values方法对各国和地区进行降序排列,然后使用head(10)方法选择前10名,再使用这些新数据绘制柱形图。

31ec113852ab6267ff47da73daabd638.png

现在信息很清楚了,数据是等距的,用不同的颜色清楚地表示了出来。

在2015年的数据集中,澳门特区和摩纳哥是人口密度最高的。

散点图

散点图将数据显示为开放空间中的点。散点图非常有用,因为我们可以根据特定值来指定每个数据点的大小,并且数据本身可以表示这个数据点与其他点的区别。

现在分析一些国家和地区,这些国家和地区多年来的平均人口密度低于10人每平方公里陆地面积。

total_columns = dataset.shape[1]selected_data = dataset[dataset.sum(axis = 1).apply(lambda x: x/total_columns) <= 10]consolidated_data = selected_data.sum(axis = 1).apply(lambda x: x/total_columns)countries = selected_data['Country Name']plt.rcParams['figure.figsize'] = (20, 20)plt.rcParams['font.size'] = 14plt.title('Average Population density for various countries')plt.xlabel('Countries')plt.ylabel('Average Population Density')plt.xticks(rotation = '90')plt.scatter(countries,             consolidated_data,             s = consolidated_data*20,             c = cm.rainbow(np.linspace(0, 1, len(countries))))

首先,我使用dataset.sum(axis = 1)对每行中的所有数据求和,然后使用 lambda函数:所得到的和,除以列数,得到平均值。然后,我比较这个值是否小于或等于10,并将其用作数据集的索引。其结果是所有平均人口密度小于或等于10的国家和地区。现在再次计算这个值,这次将它赋给变量consolidated_data。绘制散点图的方法类似于其他图。我们现在可以使用参数s定义每个数据点的大小,我一直让数据点的大小保持着与人口密度相当。由于这些值很小,将每个值乘以20,这样差异就更明显了。

cf7aecd99930dcc7f6a61fcee82eccd3.png

你可以看到,每个数据点都按照自身的大小(基于其密度值)表示在图上。

在数据集中,格陵兰岛似乎是所有国家/地区中平均人口密度最低的。

c6da842ff47c1cec4c79e297c5f6524a.png

深入分析

现在,我们将深入研究数据集,看看能否发现更多的信息。

描述性分析

我们可以看到世界上人口最大和最小密度值是否有任何变化。因此,我们需要计算值的范围。

minimum = dataset.loc[:, dataset.columns != 'Country Name'].min()maximum = dataset.loc[:, dataset.columns != 'Country Name'].max()diff = maximum - minimumminOfMax = maximum.min()plt.title('Range of Population Density for years 1962-2015')plt.xticks(rotation = '90')plt.xlabel('Years')plt.ylabel('Population Density')plt.bar(dataset.columns[1:], diff.apply(lambda x: x-minOfMax), color = cm.rainbow(np.linspace(0, 1, dataset.shape[1])))

使用min()max()方法计算除了 Country Name以外的每一列的最小值和最大值。然后,变量diff应用两个最值的差。接着,计算最大值数据集中最小的值,赋给变量minOfMax,当我们绘制柱形图时,每个值都减去这个数。这样做将确保把所有的数值分别与最小值的年份进行比较,此目的以apply(lambda x: x-minOfMax)方法来实现。

462e45c0e3962c06cda89f856a8b8056.png

我们看到,在2001年间,人口最稠密的国家/地区和人口最稀少的国家/地区之间的差距最大,然后在2002年急剧缩小。

人口数量与人口密度

如果人口密度真的是一个很好的衡量标准,如果它也能反映出这个国家和地区的人口状况,那么研究人口密度将是一件令人赞叹的事情。由于这个数据集没有人口或区域,我们需要从其他来源获取。在这里,我们将使用BeautifulSoup从维基百科页面上爬取土地面积,并把它用于计算人口数量,然后我比较人口数量和人口密度。

from urllib.request import urlopenfrom bs4 import BeautifulSoupcontent = BeautifulSoup(urlopen('https://en.wikipedia.org/wiki/List_of_countries_and_dependencies_by_area'),                        'html.parser')dataset['Area'] = 0.0table = content.find_all('table')[0]rows = table.find_all('tr')for tr in rows:    td = tr.find_all('td')    a = tr.find('a')    try:        area = td[3].text.split('♠')[1].split('(')[0].replace(',', '')        dataset.loc[dataset['Country Name'] == a.text, 'Area'] = int(area)    except Exception:        continuedataset = dataset[dataset['Area'] != 0]dataset.shape# (170, 57)

得到了含有170个国家和地区的名单,我们有关于这些国家和地区的完整信息,包括土地面积(以公里为单位)。但是要同时理解和解释这么多的国家和地区是很困难的,来看看前20个。

population = dataset['Area'].multiply(dataset['2015'], axis = 0)countries = dataset['Country Name']plt.subplot(2,2,1)plt.title('Population of various countries for year 2015')plt.xlabel('Countries')plt.ylabel('Population')plt.xticks(rotation = '90')plt.bar(countries[:20], population[:20], color = 'b')plt.subplot(2,2,2)plt.title('Population Density of various countries for year 2015')plt.xlabel('Countries')plt.ylabel('Population Density')plt.xticks(rotation = '90')plt.bar(countries[:20], dataset['2015'][:20], color = 'r')

我们现在通过柱形图来比较20个国家/地区的人口数量和人口密度。用area (面积)乘以2015年的人口密度得到Population (人口数量)。把画布分成多个子图, subplot(rows, columns, index) 方法用于定义子图,在这里,创建一个被2行2列的4个子图的的画布,第三个参数表明了我们指的是这里的哪个子图。我们把20个国家/地区的数据用两张图表在索引1和索引2上绘制出来。

741773a930af804e1fce876f3803656a.png

我们可以看出:用“人口密度”来描述一个国家/地区的“人口”并不总是正确的,例如巴林。

尽管孟加拉国的人口密度很高,但孟加拉国的人口远远高于巴林。

3f425bc17389156852e4c9d7c6dd8ee4.png

结论

在这里,使用Matplotlib来设计和创建平面图,这有助于我们更好地理解数据集。

关注微信公众号:老齐教室。读深度文章,得精湛技艺,享绚丽人生。

987181ea9ad3b8119a91488c1e481bc4.png

点击“阅读原文”,优惠购买《跟老齐学Python:数据分析》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值