Python绘制能流图实战:以中国2022年能流图为例
最近有个小组作业,老师让我们用origin来绘制中国2022年的能流图,本着偷懒的心态,想到以前绘制过疫情数据可视化的案例场景,我对python的能力有着极大的信心,于是找到了一段有关的代码,plotly库中有关绘制Sankey的代码,属于是改改就可以用的那种
一、关于plotly包中graph_objects模块中Sankey类的介绍
前言
这部分没什么好介绍的,首先如果你是用anaconda来管理你的python环境的,在 prompt里面,打开你想要安装包的环境里面用conda install plotly
当然在很多IDE里面也是有终端对话框的,要是没找到或者是下载过慢,那就用pip下载,注意:每个环境里面初始的时候是没有pip(不行可以看看自己的sitepackage文件)的这个和你单独安装python不一样,如果你直接使用pip可能给你按照到c盘里面的Roaming文件夹里面去了,所以记得每次创建环境之后就得安装好pip
当然如果你只是安装了python那就直接pip install packages
就行
操作
打开plotly官网,找到python语言中Sankey图制作的示例代码,如下所示:
import plotly.graph_objects as go
fig = go.Figure(data=[go.Sankey(
node = dict(
pad = 15,
thickness = 20,
line = dict(color = "black", width = 0.5),
label = ["A1", "A2", "B1", "B2", "C1", "C2"],
color = "blue"
),
link = dict(
source = [0, 1, 0, 2, 3, 3], # indices correspond to labels, eg A1, A2, A1, B1, ...
target = [2, 3, 3, 4, 4, 5],
value = [8, 4, 2, 8, 4, 2]
))])
fig.update_layout(title_text="Basic Sankey Diagram", font_size=10)
fig.show()
figure
很自然的可以看出来上面代码的意思,前面的figure类的使用可以套用到所有的图中,主要是Sankey里面参数node和link两个字典类型的数据,第一个node也就是节点的设置,包括节点的具体属性
可以看到具体的属性实在是太多了,感兴趣的读者可以自行查看Sankey类文件,值得注意的是里面的属性设置好像都是dict形式的。
接下来就是link参数,也就是对能流图的线来进行设置,包含source、target和value三个list,这三者之间是一一对应的,也就是source中的值是node中节点的索引,同理target也是如此,二者一个表示流出节点一个表示流入节点,比如当索引指针是2的时候三个list对应的值是0,3,2。也就是从node[0]中流入到node[3]中价值为2的能量。对应A1到B2这条线,
以上就是上面给出Sankey中属性的简单解释,但是这样的图并不好看。我们还可以进行下面的修改工作
利用color属性来进行节点颜色和线条颜色的修改。
# override gray link colors with 'source' colors
opacity = 0.4
# change 'magenta' to its 'rgba' value to add opacity
data['data'][0]['node']['color'] = ['rgba(255,0,255, 0.8)' if color == "magenta" else color for color in
data['data'][0]['node']['color']]
data['data'][0]['link']['color'] = [data['data'][0]['node']['color'][src].replace("0.8", str(opacity))
for src in data['data'][0]['link']['source']]
这是一段将link中的颜色设置0.4的透明度,同时将颜色为品红的node节点颜色设置为特定的rgb值if color == “magenta” else color for color in data[‘data’][0][‘node’][‘color’]]这段代码实际上是一个包含两个逻辑规则的语句,首先是if判断语句,然后是一个循环语句,今后的要是遇到语句都是先运行后面的在运行前面的,可以将他视为一个嵌套结构,最后面的是最里面的层次
同理第二段代码也就是在对link颜色的透明度在进行设置
也可以对图名进行设置
fig.update_layout(
title_text="China's 2022 Energy Flow Chart<br>Data Source: via <a href='https://www.stats.gov.cn/sj/ndsj/2022/indexch.htm'>National Bureau of Statistics of China</a> etc.<br>unified unit of measurement TWH<br>Note: Due to the lack of some energy data in 2022, the corresponding latest energy data will be used instead.",
font_size=10)
fig.show()
<br>
是 是 HTML 标签,用于在网页中插入换行符。它的全称是 “break”。使用 <br>
标签可以在文本中创建换行效果,而不需要开始新的段落(这是因为plotly绘图是在浏览器的网页中展示的,当然读者也可以用plotly的保存语法fig.write_image("plot.png")
将图片保存下来查看。
二、利用Sankey类来绘制2022年中国能流图,基于中国统计年鉴
准备工作,先制作好能量流动的初始情况保证中间节点的流入和流出的能量大小是一致的,保证节点的能量流入和流出与统计年鉴上的数据一致,考虑到有的小伙伴不能科学上网,但是又想看到数据原貌的,我就不参照示例代码上传到GitHub服务器上,改变为放到了blog附件里面,注意:在读取json文件的时候记得看自己的文件位置。
方便进行可视化修改,建议用jupyter来运行代码
这里统一单位就不用国际上的TWH,统一为中国特有的百万吨标准煤。参照统计年鉴规定1千瓦时电等于0.404千克标准煤
生物质:数据来源中国产业发展促进会生物质能产业分会
2022年生物质能发电量为1824亿千瓦时,为73.736百万吨标准煤
生活焚烧垃圾发电 | 1268 |
---|---|
农林生物质发电 | 556 |
沼气发电 | 39 |
2022年我国沼气总产量为98.68亿立方米,其中户用沼气产量为74.3亿立方米,占比74.3%。工程沼气产量为25.37亿立方米,占比25.7%。在中国产业化运作生物质液体燃料(主要是以动植物来源的燃料)主要包括生物柴油和燃料乙醇 ,二者分别是270万吨和214万吨。生物质清洁供热成型燃料年利用量超 2000万吨;供应热量约3亿吉焦(这里没有找到22年的数据,就用23年的吧)这四种能量:发电量,沼气,液体燃料,成型燃料(不考虑进口的情况下)大概的流出是:电力,生活用能,生活用能,交通(少量的交叉不进行考虑),数据的统一度量标准工作交给GPT,分别对应的百万吨标准煤的数量是:73.736,54.48,5.85,10.24(没有考虑进口数量,同时生物质的颗粒燃烧只考虑清洁利用的)
天然气:中国2022年天然气的消费量是3646亿立方米,其中有1503亿立方米是进口量。折算为百万吨标准煤是442.59,182.45,其中天然气的使用方向大概为:发电、工业、生活以及其他(主要是指化工领域,同时天然气用于交通的部分不做考虑)。考虑一立方米天然气的发电量是3.21kwh,三者在此度量单位下的值分别是106.18, 直接看人家做好的图,哈哈哈。有扇形的百分比,resource:前瞻经济学人所以天然气流入的能量大小分别是:70.81、168.2、163.76、39.82
结果:
石油:石油的能量流动计算起来比较简单,直接看统计年鉴中的石油平衡表,2022年石油的消费量是7.19亿吨,按照一吨原油折合1.4286吨标准煤计算,大概是1027.16百万吨标准煤,其中进口石油相当于726.13百万吨标准煤,石油存量使用301.03百万吨标准煤。缺少22年的数据,可以参照综合能源表的21年石油各个流向的系数比例可得工业、生活、交通、其他以及损失量分别为423.81、112.71、330.23、132.55、26.16百万吨煤。还有1.7单位的石油用于发电
核能、风能、水能:由于这部分的能量主要用于发电,所以计算简便,乘以0.404然后除以10即可,结果为168.79,308.13,526.45
太阳能发电与太阳能产热:中国太阳能22年除了发出了4273亿千瓦时电意外,还用做了太阳能光伏来进行产热,用太阳能供热,此处只考虑太阳能电热器,保有量5亿平方米,平均每三人拥有一平方米,取平均辐射强度和辐射时间,大概每年提供给居民用户的供暖能量是4562.5亿千瓦时(理想环境下),二者换算单位后的指分别为172.63、184.33
目前大概的图是这样的:
煤炭:中国是用煤大国,有关这部分的计算也是最难的。由于没有找到22年中国煤炭平衡表,所以以下数据来自于网络上的搜索以及参照21的比例来进行折算。首先22年消耗42.8亿吨标准煤(总能量是54.1亿吨标准煤),2.93亿吨进口量(由于我们这里是按照消耗量进行计算的,不是按照供应量,所以每年的库存增量可以不进行计算,同理石油和其他物资也是如此)
煤炭经过加工和洗选,其中用于发电的原煤是2350百万吨标准煤,民用煤及其他大概320百万吨标准煤(交通用的15.13百万吨标准煤),直接用于工业用煤(包含热力用煤量)是16.1亿吨,包含473.44的焦煤还有其他洗煤之类的工业用煤,这里由于没找到数据,就暂且将其归为两类,直接用煤和焦煤。 洗选损失的量是100.74百万吨。
电力:接下来是电力这个子节点的细分,先验证一下我们之前做的work是否有效,从能流图中可以看出我们的电力用煤量大概是3675.36百万吨煤,折算成电力大概是90000亿度电,和实际22年的86372亿千瓦时相差不大,因为太阳能、水能等其他能源都是以电能通过0.404这个转换标准来进行转化的,唯一可能出错的地方就是22电电力企业用了2350吨标准煤的统计数据中包含了损耗量。所以对前面的数据进行修改,利用86372这个数据计算出2350中包含的有效使用量是2164.07,损耗量是185.93。这个时候3489.43百万吨标准煤的电力就对准了
接下来就是以其为父节点来进行一个细分工作,首先22年工业用电量是56001亿千瓦时,大概是2262.4百万吨标准煤,城乡居民生活用电大概是13366亿千瓦时,也就是539.99百万吨标准煤。交通用电量是属于第三产业用电,2020.42,大概是20.41(按照21的比例进行折算1993/85200)
数据来源:[交通运输、全社会用电量(2022年1-12月)],余下的就是其他用电比如第一产业和其他第三产业等等大概是666.63百万吨标准煤(http://tjj.zhangzhou.gov.cn/cms/pages/830580972336610004/pdfattachments/%E4%BA%A4%E9%80%9A%E8%BF%90%E8%BE%93%E3%80%81%E5%85%A8%E7%A4%BE%E4%BC%9A%E7%94%A8%E7%94%B5%E9%87%8F%EF%BC%882022%E5%B9%B41-12%E6%9C%88%EF%BC%89.pdf)
结束
import plotly.graph_objects as go
fig = go.Figure(data=[go.Sankey(
valueformat=".2f",
valuesuffix="百万吨标准煤",
# Define nodes
node=dict(
pad=15,
thickness=15,
line=dict(color="black", width=0.5),
label=['生物质能','电力','成型燃料','沼气','液体燃料','生活','交通','天然气进口','天然气','天然气存量使用','工业','其他','石油进口量','石油',
'石油存量消耗','损失量','核能','风能','水能','太阳能光伏','太阳能','太阳能产热','煤炭进口量','煤炭','煤炭存量消耗','焦煤'],
color=[
"rgba(31, 119, 180, 0.8)",
"rgba(255, 127, 14, 0.8)",
"rgba(44, 160, 44, 0.8)",
"rgba(214, 39, 40, 0.8)",
"rgba(148, 103, 189, 0.8)",
"rgba(140, 86, 75, 0.8)",
"rgba(227, 119, 194, 0.8)",
"rgba(188, 189, 34, 0.8)",
"rgba(23, 190, 207, 0.8)",
"rgba(31, 119, 180, 0.8)",
"rgba(179, 127, 14, 0.8)",
"rgba(44, 160, 44, 0.8)",
"rgba(214, 39, 40, 0.8)",
"rgba(148, 50, 89, 0.8)",
"rgba(140, 86, 75, 0.8)",
"rgba(227, 119, 194, 0.8)",
"rgba(127, 127, 127, 0.8)",
"rgba(188, 189, 34, 0.8)",
"rgba(23, 190, 207, 0.8)",
"rgba(31, 119, 180, 0.8)",
"rgba(255, 127, 14, 0.8)",
"rgba(44, 160, 44, 0.8)",
"rgba(214, 39, 40, 0.8)",
"rgba(148, 103, 189, 0.8)",
"rgba(140, 86, 75, 0.8)",
"rgba(127, 127, 127, 0.8)"]
),
# Add links
link=dict(
source=[0,0,0,0,2,3,4,7,9,8,8,8,8,12,14,13,13,13,13,13,13,16,17,18,19,21,20,20,22,24,23,23,23,23,23,25,23,1,1,1,1],
target=[1,2,3,4,5,5,6,8,8,1,10,5,11,13,13,10,5,6,11,1,15,1,1,1,20,20,5,1,23,23,1,25,15,5,10,10,6,10,5,6,11],
value=[73.736,10.24,54.48,5.85,10.24,54.48,5.85,182.45,260.14,70.81,168.20,163.76,39.82,726.13,301.03,423.81,112.71,330.23,129.44,4.81,26.16,
168.79,308.13,526.45,172.63,184.33,184.33,172.63,293,3987,2164.07,473.44,286.67,304.87,1035.82,473.44,15.13,2262.4,539.99,20.41,666.63],
color=[
"rgba(0,0,96,0.2)",
"rgba(0,0,96,0.2)",
"rgba(0,0,96,0.2)",
"rgba(0,0,96,0.2)",
"rgba(0,0,96,0.2)",
"rgba(0,0,96,0.2)",
"rgba(0,0,96,0.2)",
"rgba(155,55,96,0.2)",
"rgba(155,55,96,0.2)",
"rgba(155,55,96,0.2)",
"rgba(155,55,96,0.2)",
"rgba(155,55,96,0.2)",
"rgba(155,55,96,0.2)",
"rgba(55,155,96,0.2)",
"rgba(55,155,96,0.2)",
"rgba(55,155,96,0.2)",
"rgba(55,155,96,0.2)",
"rgba(55,155,96,0.2)",
"rgba(55,155,96,0.2)",
"rgba(55,155,96,0.2)",
"rgba(255,255,96,0.8)",
"rgba(0,10,216,0.2)",
"rgba(0,10,216,0.2)",
"rgba(0,10,216,0.2)",
"rgba(250,90,0,0.2)",
"rgba(250,90,0,0.2)",
"rgba(250,90,0,0.2)",
"rgba(250,90,0,0.2)",
"rgba(220,0,0,0.2)",
"rgba(220,0,0,0.2)",
"rgba(220,0,0,0.2)",
"rgba(180,100,0,0.2)",
"rgba(255,255,96,0.2)",
"rgba(148, 103, 0, 0.2)",
"rgba(31, 119, 180, 0.2)",
"rgba(140, 86, 75, 0.2)",
"rgba(23, 190, 207, 0.2)",
"rgba(127, 127, 127, 0.2)",
"rgba(214, 39, 40, 0.2)",
"rgba(140, 86, 75, 0.2)",
"rgba(31, 119, 80, 0.2)"]
))])
fig.update_layout(
title_text="China's 2022 Energy Flow Chart<br>Data Source: via <a href='https://www.stats.gov.cn/sj/ndsj/2022/indexch.htm'>National Bureau of Statistics of China</a> etc. unified unit of measurement million tonnes of coal<br>Note: Due to the lack of some energy data in 2022, the corresponding latest energy data will be used instead.",
font_size=10)
fig.show()
成品
作者:晚风热吻于我