可视化展示
本文生成的所有图片均已上传到github仓库,地址为https://github.com/Watson-swx/identity/,有需要的同学可以自行下载。同时我使用github的Pages服务,可以在浏览器输入https://watson-swx.github.io/identity/ 直接查看图片链接。点击对应的连接即可跳转到对应的图片。
分析结果
本文的数据来源是1997-2015年的中国CO2排放量,其中具体包括30个省份的总数据和每个省份具体行业、具体燃料种类的数据。因为本次数据非常规整和齐全,所以可以进行非常丰富的可视化,这也是赵老师对我们的要求。所以这次我花时间实现了比较丰富的可视化。
1.1 Map-中国地图
通过pyecharts框架实现Map地图显示,由于在中国地图的空间上显示出每年的CO2排放量的静态分布,因此更加直观清晰。我们随机选择出6年来初步分析中国CO2在这20年时间的变化。
- 高CO2排放量的省份主要是河北省、山东省、江苏省等几个省份。在过去20年间一直保持全国的高排放量。
- 内蒙古自治区在过去20年间增长明显
- 东三省(黑龙江、吉林、辽宁)CO2排放量总体来说逐渐降低
- 除个别省份如青海省、甘肃省、海南省等省份CO2排放量基本不变,其他省份CO2排放量都有比较明显的上升。
- 河北、山东、江苏都是工业大省,能源结构中煤炭都占很大比例,在火力发电仍然是主要的发电方式的前提下,CO2排放量很难降低。但是2015年中央提出“双控”政策,近年来要求非常严格,这迫使这些能源大省不得不做出转型。
- 内蒙古是一个比较特殊的省份,因为内蒙古的地域优势和资源优势,内蒙古的火力发电、水力发电、风力发电、核能发电、新能源发电都有所涉及,并且因为要供应京津地区的电力,内蒙古成为电力输出的大省,所以CO2排放量这些年来上升明显。
- 黑龙江、吉林、辽宁这些省份在上世纪经历过一段时间的高增长,大庆油田、大型煤田促使东北产生一系列重工业,但是上世纪末开始东北老工业基地逐渐没落,使得东北地区的CO2排放量下降明显,这带来的也是严峻的产业结构调整问题。黑龙江、吉林尤其严重,事实上近年来东北的人口流出也很严重。
- 北京、上海等城市由于城市定位的原因,更多地作为经济、文化中心,服务业和新兴的互联网、金融等行业聚集,因此CO2排放量较少。
- 青海、甘肃、海南等省份因为发展较为落后,缺乏大型工业基地,所以CO2排放量也相对较少。
单独分析某一年的CO2排放量,在Map地图中的颜色变化会根据该图中的CO2区间来变化,但是不能和其他年份相对比。比如说1997年地图中显示为红色的区域并不一定要比2015年的黄色区域的CO2含量高。所以我应用pyecharts的时间线来绘制动态图形,并保持同一个颜色绘制区间,就可以在同一量度下观察20年来的动态变化。
同时因为给出的数据很齐全,我将所需数据的年份(1997-2015)、地域(Beijing、Shanghai等)和燃料类型(Total、Raw Coal、Coke等)作为参数传入函数,使得可以绘制任意年份中国任意燃料类型产生的CO2排放量随时间的变化。此处截取其中两幅地图作简单说明:
第一幅图是2015年Raw Coal(原煤)产生的CO2排放在中国的分布图,可以看出内蒙古遥遥领先,这使得我们可以猜测内蒙古近年来CO2总排放量激增有可能主要就是由于原煤CO2排放量的增长。
第二幅图是中国2014年Coke(焦炭)CO2排放分布图,可以看到这时全国只有河北遥遥领先,刚才的内蒙古排放量很少,所以说不同的燃料在某个省份的CO2排放量的占比中是差别很大的。此外还能得到很多有意思的结论,我们接下来再说。
1.2 Pie-时间线饼图
如之前所应用的方法,时间线绘图的方法同样应用到饼图的绘制中。虽然中国地图在地理空间上可以很好地表示不同省份排放的CO2差异,但是所占总体的比例并不能很好地衡量,这一点可以通过饼图更清晰地展示出来,动态的结果通过生成的.html文件展示。
下图一是2015年各省份CO2的总排放量占比,饼图清晰直观,山东、江苏、天津、内蒙古排放量最多。下图二是2015年全国Sum-CO2排放量不同燃料贡献占比。Raw Coal原煤占比超过一半,Coke焦炭次之,这很明显地体现出我国仍然是一个用煤大国,我国的制造业转型仍然任重道远。
1.3 River-河流图
Map地图和Pie饼图已经可以较好地展示数据的性质,不过为了多加探索我还是尝试了更多有趣的可视化。River河流图的优点在于通过纵轴的宽窄可以表示某种燃料对应排放量的绝对大小,同时可以看出同一时间不同燃料的相对大小,时间线又能反映随时间的变化情况,所以是很好的一种可视化方法。
下图一是1997-2015年中国CO2总排放量(Sum-CO2)各种燃料的比例和变化。Raw Coal原煤占始终占总排放量的一半左右,2011年之后增速才逐步放缓。同时总排放量大致上升到原来的4倍,这说明中国过去的确是以环境为代价大力发展生产。
图二是上海在1997-2015年CO2排放量的变化图,分析类似。可以看出原煤的占比没有那么大,同时20年来总排放量的增长还可以接受,大致是两倍。因为上海属于一线城市,服务业、金融业等比较发达,重工业较少。
图三是中国20年来Raw Coal原煤对CO2排放量的占比变化,单独对于原煤做分析。可以发现各省比例相对平稳,没有很明显的变化,这也体现出各省对原煤的需求都比较旺盛,同时总量大概增长到原来的3倍左右,增长明显。
1.4 Treemap-矩形树图
Treemap矩形树图的作用主要是可以在同一张图里面分析多层次的数据,比如这次的数据包括全国30个省市的数据,而每个省市的数据又可以分解成十多个燃料种类的具体数据,除此之外,每个燃料的数据还能分解成不同行业的数据,这种多层次的数据很适用于矩形树图来表示。
图一是2015年中国CO2总排放量,每种颜色代表某个省份的排放量,因此可以大致判断不同省份的占比,而点击每个省份会放大所点击的这一块区域,详细展现在该省份内部的不同燃料种类产生CO2的占比。理论上可以继续结构数据,下一层可以展示不同行业的占比,但是数据需要组织成json格式比较复杂,我分开进行完成。
图二与图一类似,分析的是甘肃省2008年CO2的总排放量,每种颜色代表一种燃料的占比,一种颜色内部的不同区域代表的是不同行业industry的占比,矩形树图交互性强,大小关系明确,可以多层次地组织数据。
图三与图二类似,分析了山西省2000年的CO2排放情况。
2.1 PCA主成分分析降维+Kmeans聚类
将每个省份看做一个数据点,每个数据点有18个分量,也可以说有18维,为方便画图将18维降至两维,然后通过碎石图来确定Kmeans选取的聚类个数。 由下图我们选择变化最明显的k = 3作为聚类的个数。
由于通过python处理相关数据进行聚类相对比较复杂,这里我使用SPSS对数据进行了简要分析。
首先我尝试进行系统聚类:观测值聚类,很明显将内蒙古、山东、河北、江苏这几个CO2排放大省聚在一起。系统聚类的好处在于一张图里面包含了所有的聚类个数,你可以按照自己的需要进行选择。之后我进行了变量聚类,Raw Coal遥遥领先,这也和原煤在各个省份都占据一半以上的份额的事实相符。如果要分为三类,Coke焦炭的占比相对大一些,剩余其他的燃料占比都不大。
之后我使用PCA主成分分析进行降维,当保留两个主成分时总方差解释45.2%,这说明降到二维并不能很好地解释总方差,但因为画图的原因我们还是保留到二维,实际上保留7个主成分才能使得总方差解释达到82%。 {‘Raw Coal’: ‘原煤’, ‘Cleaned Coal’: ‘精煤’, ‘Other Washed Coal’: ‘其他洗净煤’, ‘Briquettes’: ‘煤球’, ‘Coke’: ‘焦炭’, ‘Coke Oven Gas’: ‘焦炉煤气’, ‘Other Gas’: ‘其他气体’, ‘Other Coking Products’: ‘其他焦化产品’, ‘Crude Oil’: ‘原油’, ‘Gasoline’: ‘汽油’, ‘Kerosene’: ‘煤油’, ‘Diesel Oil’: ‘柴油’, ‘Fuel Oil’: ‘燃料油’, ‘LPG’: ‘液化石油气’, ‘Refinery Gas’: ‘炼厂气’, ‘Other Petroleum Products’: ‘其他石油产品’, ‘Natural Gas’: ‘天然气’, ‘Process’: ‘加工’}
降到二维之后分析两个维度,可以看到各个变量对两个维度的相关性都不是很高,除了个别的比如Diesel Oil柴油、Gasoline汽油和成分1高度相关,大多数变量都没有表现出明显的相关性,在这种情况下以两个主成分为x,y轴绘制散点图然后进行聚类标记得到的结果如图2,但是缺点在于我们无法说明两个主成分在实际中代表什么含义,所以我们继续进行处理。 在主成分分析的基础上我们进行因子分析,实际上就是把原坐标轴作旋转,对于总方差的解释不变,但不同主成分的解释性会发生改变。可以明显地看出Raw Coal原煤、Coke焦炭、Briquettes煤球、Cleaned Coal精煤等变量与成分1高度相关而与2高度不相关,Fuel Oil燃料油、LPG液化石油气、Refinery Gas炼厂气、Gasoline汽油等变量与成分2高度相关而与成分1高度不相关,我们可以将成分1看做是煤及煤的提取物产生的CO2量,而把成分2看作是石油及石油的提取物产生的CO2量,这可以在实际中做一定的解释。
之后重新进行散点图绘制聚类标签,第三类(四川、河北、山东、江苏、河南)在煤及相关燃料方面CO2产生量很大,第二类(北京、上海、浙江、辽宁、广东)在石油及相关燃料方面CO2产生量很大,第一类其他省份两方面表现都比较小。当然结果还是有一定的不合理性,因为对于总方差的解释只有42%,所以这里也只是从理论上做一些简要的分析。
2.2 线性相关性分析
图一是2015年中国不同燃料CO2排放量的相关系数热力图,图二是2015年中国不同城市CO2排放量的相关系数热力图。
全部代码
import math
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
from seaborn.matrix import heatmap
from pyecharts.charts import Map
from pyecharts import options as opts
from pyecharts.charts import ThemeRiver
from pyecharts.charts import Map, Pie, Timeline
from pyecharts.charts import TreeMap
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False
class Dataprocess:
def __init__(self,prov,classes,dic):
self.prov = prov
self.classes = classes
self.dic = dic
def time_change(self):
for i in range(1997,2016):
path =f'co2_demo\Province sectoral CO2 emissions {i}.xlsx'
df = pd.read_excel(path,index_col=0)
data = df.loc[self.prov,self.classes]
print('{}年{}地区{}类型CO2排放量是{}'.format(i,self.prov,self.classes,data))
def area_distribution(self,year):
path =f'co2_demo\Province sectoral CO2 emissions {year}.xlsx'
df = pd.read_excel(path)
print(df)
class Visualization:
def __init__(self,prov,classes,year,Cprovince,Eprovince,fuel,dic):
self.prov = prov
self.classes = classes
self.year = year
self.Cprovince = Cprovince
self.Eprovince = Eprovince
self.fuel = fuel
self.dic = dic
def pca(self,k):
path = f'co2_demo\Province sectoral CO2 emissions {self.year}.xlsx'
df = pd.read_excel(path,index_col=0,sheet_name=0)
X = df.iloc[0:30,1:20]
pca = PCA(n_components=2)
pca_matrix = pca.fit_transform(X)
clf = KMeans(n_clusters=k)
clf.fit(pca_matrix)
fig = plt.figure()
axis = fig.add_subplot(111)
axis.set_title("PCA 降维聚类分析图")
color = ['r', 'y', 'b']
x,y = [],[]
for i in range(k):
comment_if = clf.labels_==i
xs = pca_matrix[comment_if, 0]
ys = pca_matrix[comment_if, 1]
x.extend(xs.tolist())
y.extend(ys.tolist())
plt.scatter(xs, ys, c=color[i], marker='.')
for i in range(len(x)):
plt.annotate(self.Cprovince[i], xy = (x[i], y[i]), xytext = (x[i]+0.1, y[i]+0.1),fontsize=8)
plt.legend(["第1组", "第2组", "第3组"])
plt.savefig('PCA 降维聚类分析图.png',dpi=600)
print('PCA dimension-reduction and cluster have finished!')
def heatmap_fuel(self):
path = f'co2_demo\Province sectoral CO2 emissions {self.year}.xlsx'
df = pd.read_excel(path,index_col=0)
X = df.iloc[0:30,1:20]
length = len(X.columns.values)
plt.subplots(figsize = (length,length))
plt.yticks(fontsize=20)
heatmap(X.corr(),annot = True,fmt = ".2f",vmax = 1,square = True, xticklabels = False,cmap = "Reds",annot_kws={'size':20})
plt.title(f'{self.year}年中国CO2不同燃料排放量的相关系数热力图',fontsize=30)
plt.savefig(f'{self.year}年中国CO2不同燃料排放量的相关系数热力图.png',dpi=800)
print('heatmap_fuel.png have finished!')
def heatmap_china(self):
path = f'co2_demo\Province sectoral CO2 emissions {self.year}.xlsx'
df = pd.read_excel(path,index_col=0)
X = df.iloc[0:30,1:20]
X = X.T
length = len(X.columns.values)
print(length)
plt.subplots(figsize = (length,length))
plt.yticks(fontsize=20)
heatmap(X.corr(),annot = True,fmt = ".2f",vmax = 1,square = True, xticklabels = False,cmap = "Reds",annot_kws={'size':20})
plt.title(f'{self.year}年中国不同城市CO2排放量的相关系数热力图',fontsize=50)
plt.savefig(f'{self.year}年中国不同城市CO2排放量的相关系数热力图.png',dpi=800)
print('heatmap_china.png have finished!')
def get_map_classes(self):
tl = Timeline()
path ='co2_demo\Province sectoral CO2 emissions 2015.xlsx'
df = pd.read_excel(path)
data = df[self.classes]
data = data.iloc[0:30]
maximum = math.ceil(data.max())
for i in range(1997,2016):
path ='co2_demo\Province sectoral CO2 emissions {}.xlsx'.format(i)
df = pd.read_excel(path)
data = df[self.classes]
data = data.iloc[0:30]
num = [(self.Cprovince[i],data[i]) for i in range(len(self.Cprovince))]
chinamap = (
Map()
.add(self.classes, num, "china")
.set_global_opts(
title_opts=opts.TitleOpts(title="Map-中国{}年{}CO2排放量省份分布图".format(i,self.classes)),
visualmap_opts=opts.VisualMapOpts(max_= maximum),
)
)
tl.add(chinamap, "{}年".format(i))
tl.render(f"timeline_map_{self.classes}.html")
print(f"timeline_map_{self.classes}.html have finished!")
def get_pie_classes(self):
tl = Timeline()
for i in range(1997, 2016):
path ='co2_demo\Province sectoral CO2 emissions {}.xlsx'.format(i)
df = pd.read_excel(path)
data = df[self.classes]
data = data.iloc[0:30]
num = [(self.Cprovince[i],data[i]) for i in range(len(self.Cprovince))]
pie = (
Pie()
.add(
self.classes,
num,
rosetype="radius",
radius=["30%", "55%"],
)
.set_global_opts(title_opts=opts.TitleOpts("CO2{}年各省份{}消耗量".format(i,self.classes)))
)
tl.add(pie, "{}年".format(i))
tl.render(f"timeline_pie_{self.classes}.html")
print(f"timeline_pie_{self.classes}.html have finished!")
def get_pie_province(self):
tl = Timeline()
for i in range(1997,2016):
path ='co2_demo\Province sectoral CO2 emissions {}.xlsx'.format(i)
df = pd.read_excel(path)
data = df.iloc[self.dic[self.prov]]
num = [(i,data[i]) for i in df.columns.values[2:20]]
pie = (
Pie()
.add(self.prov, num, rosetype="radius",radius=["30%", "55%"],)
.set_global_opts(
title_opts=opts.TitleOpts(title=f"Pie-{self.prov}{i}年CO2排放燃料分布图"),
)
)
tl.add(pie, "{}年".format(i))
tl.render(f"timeline_pie_{self.prov}.html")
print(f"timeline_pie_{self.prov}.html have finished!")
def get_river_classes(self):
y_data = []
for i in range(1997,2016):
path ='co2_demo\Province sectoral CO2 emissions {}.xlsx'.format(i)
df = pd.read_excel(path)
data = df[self.classes]
for prov in self.Eprovince:
y_data.append(["{}/06/30".format(i),data[self.dic[prov]],prov])
(
ThemeRiver(init_opts=opts.InitOpts(width="1600px", height="800px"))
.add(
series_name=self.Eprovince,
data=y_data,
singleaxis_opts=opts.SingleAxisOpts(
pos_top="50", pos_bottom="50", type_="time"
),
)
.set_global_opts(
title_opts=opts.TitleOpts(title=f"River-中国1997-2015年CO2排放量{self.classes}分布图"),
legend_opts=opts.LegendOpts(is_show=True),
tooltip_opts=opts.TooltipOpts(trigger="axis", axis_pointer_type="line")
)
.render(f"theme_river_{self.classes}.html")
)
print(f"theme_river_{self.classes}.html have finished!")
def get_river_province(self):
y_data = []
for i in range(1997,2016):
path ='co2_demo\Province sectoral CO2 emissions {}.xlsx'.format(i)
df = pd.read_excel(path)
data = df.iloc[self.dic[self.prov]]
for classes in df.columns.values[2:20]:
y_data.append([f"{i}/06/30",data[classes],classes])
(
ThemeRiver(init_opts=opts.InitOpts(width="1600px", height="800px"))
.add(
series_name=self.fuel,
data=y_data,
singleaxis_opts=opts.SingleAxisOpts(
pos_top="50", pos_bottom="50", type_="time"
),
)
.set_global_opts(
title_opts=opts.TitleOpts(title=f"River-1997-2015年CO2排放量{self.prov}分布图"),
legend_opts=opts.LegendOpts(is_show=True),
tooltip_opts=opts.TooltipOpts(trigger="axis", axis_pointer_type="line")
)
.render(f"theme_river_{self.prov}.html")
)
print(f"theme_river_{self.prov}.html have finished!")
def get_treemap_china(self):
path = f'co2_demo\Province sectoral CO2 emissions {self.year}.xlsx'
data = []
df = pd.read_excel(path,index_col=0)
for prov in df.index.values[:-2]:
dic = {}
data1 = []
dic['value'] = df.loc[prov,'Total']
dic['name'] = prov
for classes in df.columns.values[1:]:
dic1 = {}
dic1['value'] = df.loc[prov,classes]
dic1['name'] = f'{prov}.{classes}'
data1.append(dic1)
dic['children'] = data1
data.append(dic)
c = (
TreeMap()
.add(f"中国{self.year}年CO2总排放量", data)
.set_global_opts(title_opts=opts.TitleOpts(title="TreeMap"))
.render(f"treemap_china_{self.year}.html")
)
print(f"treemap_china_{self.year}.html have finished!")
def get_treemap_prov(self):
path = f'co2_demo\Province sectoral CO2 emissions {self.year}.xlsx'
data = []
df = pd.read_excel(path,index_col=0,sheet_name=self.dic[self.prov]+1)
for classes in df.columns.values[:18]:
dic = {}
data1 = []
dic['value'] = df.loc['Total Consumption',classes]
dic['name'] = classes
for industry in df.index.values[3:]:
dic1 = {}
dic1['value'] = df.loc[industry,classes]
dic1['name'] = f'{classes}.{industry}'
data1.append(dic1)
dic['children'] = data1
data.append(dic)
c = (
TreeMap()
.add(f"{self.year}年{self.prov}CO2总排放量", data)
.set_global_opts(title_opts=opts.TitleOpts(title="TreeMap"))
.render(f"treemap_{self.prov}_{self.year}.html")
)
print(f"treemap_{self.prov}_{self.year}.html have finished!")
class NotNumError(ValueError):
def __init__(self,year,province,industry,type):
self.year = year
self.province = province
self.industry = industry
self.type = type
self.message = f"{self.year} year have NotNumError,the location is {self.industry} row and {self.type} column"
def datapro(prov,classes,year,dic):
print("Start processing the data!")
d = Dataprocess(prov,classes,dic)
d.time_change()
d.area_distribution(year)
def visual(prov,classes,Cprovince,Eprovince,fuel,dic,year,k=3):
print("Start drawing!")
v = Visualization(prov,classes,year,Cprovince,Eprovince,fuel,dic)
v.pca(k)
v.heatmap_china()
v.heatmap_fuel()
v.get_map_classes()
v.get_pie_province()
v.get_pie_classes()
v.get_river_classes()
v.get_river_province()
v.get_treemap_china()
v.get_treemap_prov()
def examine(Eprovince):
print("Start checking data!")
sheet = Eprovince[:]
sheet.insert(0,'Sum')
for num in range(1997,2016):
path = f'co2_demo\Province sectoral CO2 emissions {num}.xlsx'
for pro in sheet:
df = pd.read_excel(path,sheet_name=pro)
df = df.iloc[:,1:20]
if pro=='Sum':
df = df.drop(30,axis=0)
index = df[df['Total']==0].index
if len(index)>0:
print(index)
else:
df = df.drop(1,axis=0)
try:
a,b = np.where(df.isnull())
if len(a)>0:
year = num
province = pro
industry = a
type = b
df.fillna(method='pad',axis=0)
raise NotNumError(year,province,industry,type)
except NotNumError as nne:
print(nne.message)
print("No problem!")
def main():
prov = input('enter the province:')
classes = input('enter the classes:')
year = input('enter the year:')
Cprovince = ['北京','天津','河北','山西','内蒙古','辽宁','吉林','黑龙江','上海','江苏',
'浙江','安徽','福建','江西','山东','河南','湖北','湖南','广东','广西',
'海南','重庆','四川','贵州','云南','陕西','甘肃','青海','宁夏','新疆']
Eprovince = ['Beijing','Tianjin','Hebei','Shanxi','InnerMongolia','Liaoning','Jilin','Heilongjiang',
'Shanghai','Jiangsu','Zhejiang','Anhui','Fujian','Jiangxi','Shandong','Henan','Hubei','Hunan',
'Guangdong','Guangxi','Hainan','Chongqing','Sichuan','Guizhou','Yunnan','Shaanxi',
'Gansu','Qinghai','Ningxia','Xinjiang']
fuel = ['Raw Coal','Cleaned Coal','Other Washed Coal','Briquettes','Coke','Coke Oven Gas',
'Other Gas','Other Coking Products','Crude Oil','Gasoline','Kerosene','Diesel Oil',
'Fuel Oil','LPG','Refinery Gas','Other Petroleum Products','Natural Gas','Process']
number = [i for i in range(30)]
dic = dict(zip(Eprovince,number))
examine(Eprovince)
datapro(prov,classes,year,dic)
visual(prov,classes,Cprovince,Eprovince,fuel,dic,year)
if __name__=='__main__':
main()
|