Matplotlib:数据可视化利器
来源: https://towardsdatascience.com/matplotlib-making-data-visualization-interesting-8bac1eb3d25c
作者: Karan Bhanot
翻译:老齐
数据可视化是理解数据并从中得出推论的关键步骤,虽然我们可以逐行地仔细检查数据,但这是一项冗长乏味的工作,而且不会凸显全局特征。另一方面,视觉效果很重要,以一目了然的形式定义数据,让浏览者有直观体验。
★Matplotlib是Python的图库,可以生成各种格式的高质量的出版物图形和跨平台的交互环境(matplotlib.org)。《跟老齐学Python:数据分析》一书中有专门的章节对其使用方法进行了详细阐述。
”
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
首先需要导入所有必需的库。导入Numpy
和Pandas
库来处理数据,然后导入matplotlib
并使用其模块pyplot
来绘制图形,把cm
用作调色板。语句%matplotlib inline
确保所有绘图都以嵌入方式显示在浏览器页面上。
导入数据集
文件dataset.csv
的前四行不是必需的,因此我们可以在跳过前四行的同时将数据导入,然后使用head(5)
函数查看部分数据。
我们立即看到了一些无关的列。保留Country Name
,删除Country Code
列。正如我们所知道的,我们正在处理人口密度问题,所以可以删除 Indicator Name
列和Indicator Code
列。接下来,1960
和2016
的列具有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。
已经做好了准备,数据可视化,只欠东风。
可视化
现在,我们将使用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)
恭喜!!第一个平面图完成了!!
这张图有一定作用,但很难理解。坐标轴上没有标签,并且横轴的值重叠了。这就是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。
现在可以看到,只要稍加修改,图形就更具描述性。
从上图可以看出,到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行,并且把值绘制为曲线图。我们使用matplotlib
库cm
包及其rainbow
方法来设置。在plot
方法中,必须要设置label
参数的值,因为它可以确保在启用图例时,显示图例名称。使用legend()
方法启用图例,我在方法中指定了一个属性,即大小为24
。
我们可以看到,阿鲁巴、安道尔、阿富汗、安哥拉和阿尔巴尼亚这5个国家和地区的人口密度都有所上升。
现在我们已经把所有的线图都绘制在一个平面图上,很容易看出阿鲁巴的人口密度一直高于其他4个国家/地区。
柱形图
使用带有相关参数的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))))
虽然这个平面图确实试图在同一时间提供很多信息,但同样的原因使它缺乏任何有用的信息。在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名,再使用这些新数据绘制柱形图。
现在信息很清楚了,数据是等距的,用不同的颜色清楚地表示了出来。
在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,这样差异就更明显了。
你可以看到,每个数据点都按照自身的大小(基于其密度值)表示在图上。
在数据集中,格陵兰岛似乎是所有国家/地区中平均人口密度最低的。
深入分析
现在,我们将深入研究数据集,看看能否发现更多的信息。
描述性分析
我们可以看到世界上人口最大和最小密度值是否有任何变化。因此,我们需要计算值的范围。
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)
方法来实现。
我们看到,在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上绘制出来。
我们可以看出:用“人口密度”来描述一个国家/地区的“人口”并不总是正确的,例如巴林。
尽管孟加拉国的人口密度很高,但孟加拉国的人口远远高于巴林。
结论
在这里,使用Matplotlib来设计和创建平面图,这有助于我们更好地理解数据集。
★关注微信公众号:老齐教室。读深度文章,得精湛技艺,享绚丽人生。
”
★点击“阅读原文”,优惠购买《跟老齐学Python:数据分析》
”