使用Python Matplotlib进行可视化解释数据实例
该机器学习数据集(https://ibm.box.com/shared/static/lw190pt9zpy5bd1ptyg2aw15awomz9pu.xlsx)涉及1980年至2013年期间从各个国家移民到加拿大的情况。
加拿大移民数据.xlsx格式
由于机器学习数据集是.xlsx格式的,并且文件中有3个工作表,我们将该文件读入data-frame,Python代码如下:
import numpy as np import pandas as pd import matplotlib.pyplot as plt Canada_df = pd.read_excel('Canada.xlsx', skiprows=range(20), sheet_name='Canada by Citizenship',skipfooter=2) print (Canada_df.head(3)) print ("Shape of Data-Frame:", Canada_df.shape,) print ' ' print ("Check Null values:", Canada_df.isnull().values.any())
skiprows用于处理excel表中最初无用的行。最好将列(使用pandas.DataFrame.rename)“OdName”和“AreaName”分别重命名为“Country_Name”和“Continents”,以便更好地理解,inplace=True确保将更改保存在数据帧中。如果不希望在原始数据帧中进行此更改,则应该使用inplace=False。
#删除无用的列 Canada_df.drop(['Type', 'Coverage', 'AREA', 'REG', 'DEV', 'DevName'], axis=1, inplace=True) Canada_df.rename(columns={'OdName':'Country_Name', 'AreaName':'Continent', 'RegName':'Region'}, inplace=True) Canada_df.tail(3)
将所有列名转换为字符串,即将年份视为字符串
Canada_df.columns = list(map(str, Canada_df.columns))
我们希望country_name作为索引而不是行号
Canada_df_world_map = Canada_df.copy() Canada_df.set_index('Country_Name', inplace=True)
添加一个列,其中包含每个国家一年内所有移民的总和
Canada_df['Total_Immigrants'] = Canada_df.sum(axis=1) Canada_df.head(2)
我们的主要方法是使用年份,即range(1980, 2014),首先我们创建一个年份列表
all_years = list(map(str, range(1980, 2014))) Canada_df_haiti = Canada_df.loc[['Haiti'], all_years] #Canada_df_haiti.head(5) Canada_df_haiti = Canada_df_haiti.transpose()
Bar Plots:DataFrame.plot(kind ='bar')
我们在经过上述几次调整之后,让我们使用pandas DataFrame.plot绘图,首先我们将尝试一些条形图。如果我们绘制多年来来自海地的移民数量,那么我们可以看到移民增加的惊人上升趋势靠近2009年,2010年,2011年。Python代码如下:
Canada_df_haiti.plot(kind='bar', figsize=(11,8), legend=False, logy=False, fontsize=13) plt.xlabel('Years', fontsize=15, labelpad=12) plt.ylabel('No. of Immigrants', fontsize=14) label = ['Haitian Immigrants to Canada'] plt.legend(label, fontsize=15)
图1:2011年附近海地移民人数不断增加
有些人已经猜对了,由于2010年海地地震灾难,2011年移民数量急剧增加。让我们用清晰的注释做出清晰的表述,Python实现如下:
Canada_df_haiti.plot(kind='bar', figsize=(10,8), legend=False, logy=False, fontsize=13) plt.xlabel('Years', fontsize=15, labelpad=12) plt.ylabel('No. of Immigrants', fontsize=14) label = ['Haitian Immigrants to Canada'] plt.legend(label, fontsize=15) #plt.xlim(-1,34.2) #plt.ylim(0, 6500) bbox_props = dict(boxstyle="rarrow,pad=0.7", fc="lavender", ec="goldenrod", lw=1,alpha=0.3) t = plt.text(25, 5500, "2010 Haiti Earthquake", ha="center", va="center", rotation=40, bbox=bbox_props, fontsize=13, color='magenta') bb = t.get_bbox_patch()
图2:在图1中添加了一些注释
随着冰岛金融危机(2008-2011)导致严重的经济萧条,来自冰岛的移民数量也出现了类似的趋势。Python可视化代码如下:
Canada_df_Iceland = Canada_df.loc[['Iceland'], all_years] Canada_df_Iceland = Canada_df_Iceland.transpose() Canada_df_Iceland.plot(kind='bar', figsize=(10,8), legend=False, logy=False, fontsize=13, color='olive') plt.xlabel('Years', fontsize=15, labelpad=12) plt.ylabel('No. of Immigrants', fontsize=14) label = ['Icelandic Immigrants to Canada'] plt.legend(label, fontsize=15) bbox_props = dict(boxstyle="rarrow,pad=0.7", fc="Dodgerblue", ec="Turquoise", lw=1,alpha=0.3) t = plt.text(28, 52, "2008-2011 Icelandic Financial Crisis", ha="center", va="center", rotation=50, bbox=bbox_props, fontsize=13, color='purple') bb = t.get_bbox_patch()
图3:冰岛的经济危机导致加拿大移民人数急剧增加
Pie Plots:DataFrame.plot(kind ='pie')
饼形图是一种圆形图形,此圆形图中的切片表示数字比例。在这里,我们可以看到来自不同大陆的移民的数字比例如何使用饼形图在20年(1985年和2005年)中变化。但是,有效的代表性是个问题。让我们看下面的Python代码和相应的图
Canada_df_Continents = Canada_df.groupby('Continent', axis=0).sum() Canada_df_Continents_2005 = Canada_df_Continents.loc[:, ['2005']] Canada_df_Continents_2005.sort_values(['2005'], inplace=True) Canada_df_Continents_1985 = Canada_df_Continents.loc[:, ['1985']] Canada_df_Continents_1985.sort_values(['1985'], inplace=True) fig = plt.figure(figsize=(15,8)) plt.subplot(1,2,1) Canada_df_Continents_1985['1985'].plot(kind='pie') plt.subplot(1,2,2) Canada_df_Continents_2005['2005'].plot(kind='pie')
图4:1985年和2005年来自不同大陆的移民的饼图分别显示在左图和右图中
正如你所看到的,这些饼状图在视觉上并不令人满意,即使我们大致了解了在20年的时间里来自不同大陆的移民比例是如何变化的,它仍然不是很清楚。
fig = plt.figure(figsize=(18,7)) plt.subplot(1,2,1) explode_list_1985 = [0.6,0.1,0.1,0.1,0.2,0.2] colors_list_1985 = ['aqua', 'green', 'yellow', 'orange', 'Pink', 'Red'] Canada_df_Continents_1985['1985'].plot(kind='pie', explode = explode_list_1985, fontsize=16, autopct='%1.2f%%', pctdistance=0.7, labels=None, colors=colors_list_1985) labels_1985 = Canada_df_Continents_1985.index plt.legend(labels_1985, loc='upper left', fontsize=12, bbox_to_anchor=(-0.4,1.1)) plt.subplot(1,2,2) explode_list_2005 = [0.3,0.1,0.1,0.1,0.1,0.0] colors_list_2005 = ['dodgerblue', 'limegreen', 'gold', 'darkorange', 'Hotpink', 'crimson'] Canada_df_Continents_2005['2005'].plot(kind='pie', explode = explode_list_2005, fontsize=16, autopct='%1.2f%%', pctdistance=1.2, labels=None, colors=colors_list_2005, shadow=True) labels_2005 = Canada_df_Continents_2005.index plt.legend(labels_2005, loc='upper left', fontsize=12, bbox_to_anchor=(-0.6,1.1)) fig.subplots_adjust(wspace=1) plt.suptitle("Pie Charts of Immigrants to Canda from Different Continetnts in 1985 and 2005", fontsize = 18, y=0.04) #plt.savefig("Pie_Imm_to_Canda_Cont_1985_2005.png", dpi=700) plt.show()
图5:与图4相同,但与前一个相比,这个视觉上更好一些
从上面的图表你可以看到,在1985年,很大一部分移民来自欧洲,相比之下,20年后的2005年,欧洲完全被亚洲所主导。
气泡图:
气泡图基本上是美化的散点图,其中3维数据可以在2D图中显示。除了通常的X和Y之外,气泡(或任何其他标记)的大小代表另一个维度(读取特征)。
首先我们只选择中国和印度两个国家,然后绘制1980年到2013年的移民趋势
Canada_df_years = Canada_df.drop(['Continent', 'Region', 'Total_Immigrants'], axis=1).transpose() Canada_df_years= Canada_df_years.reset_index() Canada_df_years.rename(columns={'index':'Years'}, inplace=True) #print (Canada_df_years.tail(3)) #Canada_df_Chi_Ind = Canada_df.loc[['China', 'India'], all_years].transpose() #Canada_df_Chi_Ind.tail(3) Canada_df_Chi_Ind = Canada_df_years.loc[:,['Years', 'China', 'India']] #print (Canada_df_Chi_Ind.tail(3)) #print (Canada_df_Chi_Ind.dtypes) # change the variable types in Years column Canada_df_Chi_Ind = Canada_df_Chi_Ind.astype({"Years":int}) #print (Canada_df_Chi_Ind.dtypes)
在创建一个小型dataframe并重新设置索引之后,就可以绘图了
China_norm = (Canada_df_Chi_Ind['China'] - Canada_df_Chi_Ind['China'].min())/(Canada_df_Chi_Ind['China'] + Canada_df_Chi_Ind['China'].max()) India_norm = (Canada_df_Chi_Ind['India'] - Canada_df_Chi_Ind['India'].min())/(Canada_df_Chi_Ind['India'] + Canada_df_Chi_Ind['India'].max()) ax1 = Canada_df_Chi_Ind.plot(kind='scatter', figsize=(14,8), x='Years', y='China', s = China_norm*1500, marker = 'o', color='maroon', alpha=0.3, xlim=(1978, 2015), label='China') ax2 = Canada_df_Chi_Ind.plot(kind='scatter', x = 'Years', y='India', s=India_norm*1500, marker='*', color='navy', alpha=0.4, ax=ax1, xlim=(1978, 2015), label='India') print ("Minimum immigrants from India: ", Canada_df_Chi_Ind['India'].min()) print ("Minimum immigrants from China ", Canada_df_Chi_Ind['China'].min()) ax1.set_ylabel("No. of Immigrants", fontsize=13) ax1.set_xlabel("Years", fontsize=14) ax2.legend(loc=4, fontsize=13) ax1.legend(loc=4, fontsize=13) ax1.set_title("Immigrnats from China and India to Canada over the years 1980-2013", fontsize=15)
我们看到1997-1998左右的数字有所增加,这可能归因于亚洲金融危机。让我们看一下下面的Python代码及生成的图形
India_list = Canada_df_Chi_Ind.loc[:,'India'].tolist() fig = plt.figure(figsize=(14,8)) plt.scatter(x=Canada_df_Chi_Ind['Years'], y=Canada_df_Chi_Ind['China'], s = China_norm*1600, marker = 'o', color='maroon', alpha=0.5, label='China') plt.scatter(x = Canada_df_Chi_Ind['Years'], y=Canada_df_Chi_Ind['India'], s=India_norm*1600, marker='*', c = India_list, cmap='PuBu', alpha=0.6, label='India') print ("Minimum immigrants from India: ", Canada_df_Chi_Ind['India'].min()) print ("Minimum immigrants from China ", Canada_df_Chi_Ind['China'].min()) plt.ylabel("No. of Immigrants", fontsize=13) plt.xlabel("Years", fontsize=14) plt.legend(loc=4, fontsize=13) plt.legend(loc=4, fontsize=13) plt.title("Immigrnats from China and India to Canada over the years 1980-2013", fontsize=15) bbox_props = dict(boxstyle="circle,pad=0.5", fc="azure", ec="lightsalmon", lw=1,alpha=0.3) t = plt.text(2000, 9500, "1997 Asian Financial Crisis", ha="center", va="center", rotation=30, bbox=bbox_props, fontsize=13, color='magenta') bb = t.get_bbox_patch() plt.savefig("Chi_In_to _Can_1980_2013.png", dpi=400)
图6:1980年至2013年间从中国和印度到加拿大的移民的气泡图
如果您注意到星形标记(代表来自印度的移民),它们会变得更大,并且这些年来颜色从紫色变为蓝色。在plt.scatter中,s和c表示标记的大小和颜色。特别是印度移民图,使用了这两个参数。这里的s是多年来移民的标准化值(乘以2000,这样标记的大小就足够大了),而c, cmap只是移民的原始数量。
我们可以根据多年来加拿大的移民总数创建一个前6名国家的data-frame
#print (Canada_df.head(3)) Canada_df_top6=Canada_df.sort_values(['Total_Immigrants'], axis=0, ascending=False).head(6) #print (Canada_df_top6.head(3)) Canada_df_top6.drop(['Continent', 'Region'], axis=1, inplace=True) #print (Canada_df_top6.head(3)) #print (Canada_df_top6.columns) decade_1980 = list(map(str, range(1980, 1990))) #print (decade_1980) decade_1990 = list(map(str, range(1990, 2000))) decade_2000 = list(map(str, range(2000, 2010))) #Create dataframe based on these decades Canada_df_top6_1980s = Canada_df_top6.loc[:, decade_1980].sum(axis=1) #print (Canada_df_top6_1980s.head(3)) Canada_df_top6_1990s = Canada_df_top6.loc[:, decade_1990].sum(axis=1) Canada_df_top6_2000s = Canada_df_top6.loc[:, decade_2000].sum(axis=1) # create a new dataframe include the info from the decades new_decades_df_top6 = pd.DataFrame({'1980s':Canada_df_top6_1980s, '1990s': Canada_df_top6_1990s, '2000s': Canada_df_top6_2000s}) print (new_decades_df_top6.head(3))
让我们画出箱形图
ax1 = new_decades_df_top6.plot(kind='box', figsize=(14,6), color='navy', fontsize=13) ax1.set_ylabel("No. of Immigrants", fontsize=15) ax1.set_title("Box plot of Top 6 Immigrating Nations Over 3 Different Decades", fontsize=15)
#print (Canada_df.head(3)) Canada_df_top10=Canada_df.sort_values(['Total_Immigrants'], axis=0, ascending=False).head(10) #print (Canada_df_top6.head(3)) Canada_df_top10.drop(['Continent', 'Region'], axis=1, inplace=True) #print (Canada_df_top6.head(3)) #print (Canada_df_top6.columns) #Create dataframe based on these decades Canada_df_top10_1980s = Canada_df_top10.loc[:, decade_1980].sum(axis=1) #print (Canada_df_top6_1980s.head(3)) Canada_df_top10_1990s = Canada_df_top10.loc[:, decade_1990].sum(axis=1) Canada_df_top10_2000s = Canada_df_top10.loc[:, decade_2000].sum(axis=1) # create a new dataframe include the info from the decades new_decades_df_top10 = pd.DataFrame({'1980s':Canada_df_top10_1980s, '1990s': Canada_df_top10_1990s, '2000s': Canada_df_top10_2000s}) print (new_decades_df_top10.head(3)) ax2 = new_decades_df_top10.plot(kind='box', figsize=(14,6), color='lime', fontsize=13) ax2.set_ylabel("No. of Immigrants", fontsize=15) ax2.set_title("Box plot of Top 10 Immigrating Nations Over 3 Different Decades", fontsize=15)
#print (Canada_df.head(3)) Canada_df_top15=Canada_df.sort_values(['Total_Immigrants'], axis=0, ascending=False).head(15) #print (Canada_df_top6.head(3)) Canada_df_top15.drop(['Continent', 'Region'], axis=1, inplace=True) #print (Canada_df_top6.head(3)) #print (Canada_df_top6.columns) #Create dataframe based on these decades Canada_df_top15_1980s = Canada_df_top15.loc[:, decade_1980].sum(axis=1) #print (Canada_df_top6_1980s.head(3)) Canada_df_top15_1990s = Canada_df_top15.loc[:, decade_1990].sum(axis=1) Canada_df_top15_2000s = Canada_df_top15.loc[:, decade_2000].sum(axis=1) # create a new dataframe include the info from the decades new_decades_df_top15 = pd.DataFrame({'1980s':Canada_df_top15_1980s, '1990s': Canada_df_top15_1990s, '2000s': Canada_df_top15_2000s}) print (new_decades_df_top15.head(3)) ax3=new_decades_df_top15.plot(kind='box', figsize=(14,6), fontsize=13, color='red') ax3.set_ylabel("No. of Immigrants", fontsize=15) ax3.set_title("Box plot of Top 15 Immigrating Nations Over 3 Different Decades", fontsize=15)
new_decades_df_top6['1980s'].plot(kind='pie', figsize=(14,6), autopct='%1.2f%%', fontsize=13)
new_decades_df_top6['1990s'].plot(kind='pie', figsize=(14,6), fontsize=13)
new_decades_df_top6['2000s'].plot(kind='pie', figsize=(14,6), autopct='%1.2f%%', fontsize=13)
世界地图:folium.Map()
首先,我们开始安装Folium。
pip install Folium print folium.__version__
0.7.0
我们感兴趣的那种特殊的地图叫做Choropleth。这是一种专题地图,其中地图的一部分是阴影/图案根据所使用的统计变量的比例。在这里,我将标出1985年和2005年世界各地移民的数量是如何变化的。
现在要创建一个Choropleth地图,我们需要一个.json文件,其中包含所有国家的边界坐标,该文件由IBM提供,下载地址(http://www.kankanyun.com/data/world-countries.json)。我用下面的Python代码片段绘制了1985年世界各地移民的分布图。这个地图是互动的,所以你可以放大或缩小
import folium world_geo = r'world-countries.json' # first create a world map with folium world_map1 = folium.Map(location=[0,0], zoom_start=2, tiles = 'Mapbox Bright') world_geo = r'world-countries.json' world_map1.choropleth(geo_data=world_geo, data = Canada_df_world_map, columns=['Country_Name', '1985'], key_on='feature.properties.name', fill_color='YlGn', fill_opacity=0.6, line_opacity=0.3, fontsize=12, legend_name="Immigration to Canada in 1985") #world_map1 world_map1.save('Immigration_to_Canada_W_MAP1985.html') world_map1
在上面的Python代码中,key_on涉及.json文件中的国家/地区名称以及data我们感兴趣的data-frame。
图7:1985年从世界各地移民到加拿大
按照相同的程序,我们在2005年创建了另一个代表来自世界各地的移民到加拿大的Choropleth地图,你可以清楚地看到差异。
world_map2 = folium.Map(location=[0,0], zoom_start=2, tiles = 'Mapbox Bright') #world_map2 world_map2.choropleth(geo_data=world_geo, data = Canada_df_world_map, columns=['Country_Name', '2005'], key_on='feature.properties.name', fill_color='YlGn', fill_opacity=0.6, line_opacity=0.3, fontsize=12, legend_name="Immigration to Canada in 2005") #world_map1 world_map2.save('Immigration_to_Canada_W_MAP2005.html') world_map2
图8:与图7相同,但现在使用2005年的数据来绘制移民到加拿大的图形
你可以在上面的图中看到的一个很大的缺点是,即使我们从data-frame 中知道20世纪80年代的移民数量相当高,英国的颜色也没有从1985年到2005年发生变化。问题出现在data-frame 中,国家名称是“United Kingdom of Great Britain and Northern Ireland”,而在.json文件中,它只是United Kingdom。所以可以在'Country_Name'列中使用replace选项 -
Canada_df_world_map.Country_Name = Canada_df_world_map.Country_Name.replace({"United Kingdom of Great Britain and Northern Ireland": "United Kingdom"}) Canada_df_world_map.tail(20)
这是一个粗略的替代品,但我们可以试图验证我们的理解
图9:与图7相同,但现在英国部分已得到纠正