用Python绘制北京近一年来空气质量热力图,看看北京的沙尘暴真的多吗?

3月15日北京迎来了近6年来的首次沙尘暴,是被迫吃土的一天!!上次沙尘暴出现还是在2015年4月15日

记得早上起床,打开手机看到好多盆友发来的询问关怀“听说北京沙尘暴了,注意安全哦”,比心 ! 随后拉开窗帘,果然是漫天黄沙还伴随着大风,打开朋友圈满屏的银翼杀手末日的关键字。

其实,我们看看北京最近15个月空气质量热力图发现,大部分的时间空气质量还是不错的在良好与优秀之间

注意:绿色(50以下) 表示优秀,黄色(50-100)表示良好,橙色开始属于污染。

公众号回复 0316 可以获取全部演示代码ipynb文件!

北京最近15个月空气质量热力图

接下来,我们 来一起试着利用python绘制上面这张热力图吧!

目录:

  • 0. 图赏

  • 1. 空气质量数据获取

  • 2. 数据预处理

  • 3. 热力图绘制

    • 3.1. seaborn的heatmap热力图

    • 3.2. plotly的热力图绘制

  • 4. 附

0. 图赏

鸟巢:

鸟巢

故宫角楼:

故宫角楼

大裤衩:

大裤衩

银翼杀手:

银翼杀手

我上班路上:

2021年03月15日早上10点上班路上

1. 空气质量数据获取

这里我们从akshare库的接口直接获取北京市历史空气质量数据

import akshare as ak

air_quality_hist_df = ak.air_quality_hist(city="北京", period="day", start_date="2020-01-01", end_date="2021-03-15")
air_quality_hist_df.head()
请求的数据预览

2. 数据预处理

由于绘制热力图x轴是日期(1-31),y轴是年月。因此我们需要对原数据进行宽表转化和一些简单的预处理。

注意:处理过程详情代码注释说明。

import pandas as pd

# 拷贝并进行索引重置
df = air_quality_hist_df[['aqi']].copy()
df.reset_index(inplace=True)
# 将time字段改为时间格式
df.time = pd.to_datetime(df.time)
# 新增年月字段,内容为 x年x月,如2021年3月,为字符串格式
df['年月'] = df.time.apply(lambda x:x.strftime('%Y{y}%m{m}').format(y="年",m="月"))
# 新增日期字段,内容为 1-31
df['日期'] = df['time'].dt.day

# 做透视处理,将长表转化为宽表
data = pd.pivot(df,
                values='aqi',
                index='年月',
                columns='日期')
# 转化后部分月份不存在部分日期默认为nan值,需要转化为数字格式 float(无法转化为int)
data = data.astype('float')
# 按照 索引年月倒序排序
data.sort_index(ascending=False,inplace=True)

data.head()
处理后数据预览

3. 热力图绘制

这里我们介绍两种热力图绘制手法,其一是sns.heatmap(),其二是plotlyff.create_annotated_heatmap()

3.1. seaborn的heatmap热力图

import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import seaborn as sns

#设置全局默认字体 为 雅黑
plt.rcParams['font.family'] = ['Microsoft YaHei'] 
# 设置全局轴标签字典大小
plt.rcParams["axes.labelsize"] = 14  
# 设置背景
sns.set_style("darkgrid",{"font.family":['Microsoft YaHei', 'SimHei']})  
# 设置画布长宽 和 dpi
plt.figure(figsize=(18,8),dpi=100)
# 自定义色卡
cmap = mcolors.LinearSegmentedColormap.from_list("n",['#95B359','#D3CF63','#E0991D','#D96161','#A257D0','#7B1216']) 
# 绘制热力图
ax = sns.heatmap( data, cmap=cmap, vmax=300, 
                 annot=True, # 热力图上显示数值
                 fmt='0.f', # 数值格式
                 linewidths=0.5,) 
ax.set_title( label='北京最近15个月空气质量AQI',fontdict = {'fontsize': 16})
# 将x轴刻度放在最上面
ax.xaxis.set_ticks_position('top') 
效果

3.2. plotly的热力图绘制

对于plotly来说,plotly.express可以直接将满足条件格式的dataframe数据用px.imshow()绘制,不过试了很久暂时没学会怎么方便的将数值显示在热力图上。找了半天发现plotly提供另外一种方式create_annotated_heatmap,专门用来显示数值。

px.imshow方法:

import plotly.express as px
# import plotly.graph_objs as go

fig = px.imshow(data,
               color_continuous_scale='Temps', 
               color_continuous_midpoint= 75 ,
               range_color = [0,300],
               title = '北京最近15个月空气质量AQI',
               width=1200,
               height=711,
               )
fig.update_layout(xaxis=dict(tickmode='linear'), # x轴全部显示
                 )
fig.show()
px.imshow方法

create_annotated_heatmap方法:

以下重点介绍该方法

  • 由于绘图的时候 顺序和之前是反的,所以需要先进行顺序逆序调整;

  • 为了更好的根据aqi数值进行颜色分配,我们可以对数据进行分箱操作;

  • 为了让颜色和空气质量指数级别色卡一致,我们可以自定义颜色色卡;

  • 为了更好的在热力图上显示数值,需要将原来的float转化为int,nan转化为空字符。

# 数据分箱操作
data.sort_index(ascending=True,inplace=True)
bins = [0,50,100,150,200,300,999]
groups = [.1,.2,.3,.4,.5,.6]
data1 = data.apply(lambda x: pd.cut(x,bins,labels=groups))
data1.head()
数据预览
import plotly.figure_factory as ff
import numpy as np

x = list(data.columns)
y = list(data.index)
z = data1.values.tolist()
z_text = data3.fillna('').values.tolist() # data3 为初始未进行格式转化的透视宽表
# 自定义色卡
colorscale=[[0.0, 'rgb(0,153,102)'], [.1, 'rgb(211, 207, 99)'],
            [.3, 'rgb(255, 153, 51)'], [.4, 'rgb(204, 97, 51)'],
            [.5, 'rgb(102, 0, 153)'],[1.0, 'rgb(126, 0, 35)']]

fig = ff.create_annotated_heatmap(z, x=x, y=y, 
                                  annotation_text=z_text, 
                                  colorscale=colorscale,
                                 )
fig.update_layout(title ='北京最近15个月空气质量AQI',
                   width=1200,
                   height=711,
                 )
# 将x轴刻度放在最上面
fig.update_xaxes(side="top")
fig.show()
北京最近15个月空气质量AQI

4. 附

空气质量指数色卡对照表:

空气质量评级

matplotlib 热力图绘制参考

https://matplotlib.org/stable/gallery/images_contours_and_fields/image_annotated_heatmap.html

plotly元素周期表绘制案例:

元素周期表
# Add Periodic Table Data
symbol = [['H', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'He'],
         ['Li', 'Be', '', '', '', '', '', '', '', '', '', '', 'B', 'C', 'N', 'O', 'F', 'Ne'],
         ['Na', 'Mg', '', '', '', '', '', '', '', '', '', '', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar'],
         ['K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr'],
         ['Rb ', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe' ],
         ['Cs', 'Ba', '', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn' ],
         ['Fr', 'Ra', '', 'Rf', 'Db', 'Sg', 'Bh', 'Hs', 'Mt', 'Ds', 'Rg', 'Cn', 'Uut', 'Fl', 'Uup', 'Lv', 'Uus', 'Uuo'],
         ['', '', 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu', ''],
         ['', '', 'Ac', 'Th', 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', 'No', 'Lr', '' ],
         ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
         ['', 'Alkali Metal', '', '', 'Transition Metal', '', '', 'Actinide', '', '', 'Semimetal', '', '', 'Halogen', '', '', '', ''],
         ['', 'Alkaline Metal', '', '', 'Lanthanide', '', '', 'Basic Metal', '', '', 'Nonmetal', '', '', 'Noble Gas', '', '', '', '']]

element = [['Hydrogen', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'Helium'],
           ['Lithium', 'Beryllium', '', '', '', '', '', '', '', '', '', '', 'Boron', 'Carbon', 'Nitrogen', 'Oxygen', 'Fluorine', 'Neon'],
           ['Sodium', 'Magnesium', '', '', '', '', '', '', '', '', '', '', 'Aluminium', 'Silicon', 'Phosphorus', 'Sulfur', 'Chlorine', ' Argon'],
           ['Potassium', ' Calcium', ' Scandium', ' Titanium', ' Vanadium', ' Chromium',  'Manganese', 'Iron', 'Cobalt', 'Nickel', 'Copper', 'Zinc', 'Gallium', 'Germanium', 'Arsenic', 'Selenium', 'Bromine', 'Krypton'],
           ['Rubidium', 'Strontium', 'Yttrium', 'Zirconium', 'Niobium', 'Molybdenum', 'Technetium', 'Ruthenium', 'Rhodium', 'Palladium', 'Silver', 'Cadmium', 'Indium', 'Tin', 'Antimony', 'Tellurium', 'Iodine', 'Xenon'],
           [' Cesium', ' Barium', '',  'Hafnium', 'Tantalum', 'Tungsten', 'Rhenium', 'Osmium', 'Iridium', 'Platinum', 'Gold', 'Mercury', 'Thallium', 'Lead', 'Bismuth', 'Polonium', 'Astatine', 'Radon'],
           [' Francium', ' Radium', '', 'Rutherfordium','Dubnium','Seaborgium','Bohrium','Hassium','Meitnerium','Darmstadtium','Roentgenium','Copernicium','Ununtrium','Ununquadium','Ununpentium','Ununhexium','Ununseptium','Ununoctium'],
           ['', '',  'Lanthanum', 'Cerium', 'Praseodymium', 'Neodymium', 'Promethium', 'Samarium', 'Europium', 'Gadolinium', 'Terbium', 'Dysprosium', 'Holmium', 'Erbium', 'Thulium', 'Ytterbium', 'Lutetium', ''],
           ['', '', 'Actinium', 'Thorium', 'Protactinium', 'Uranium', 'Neptunium', 'Plutonium', 'Americium', 'Curium', 'Berkelium', 'Californium', 'Einsteinium','Fermium' ,'Mendelevium', 'Nobelium', 'Lawrencium', '' ],
           ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
           ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
           ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']]

atomic_mass = [[ 1.00794, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0,  4.002602],
     [ 6.941, 9.012182, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0,  10.811, 12.0107, 14.0067, 15.9994, 18.9984032, 20.1797],
     [ 22.98976928, 24.3050, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0,  26.9815386, 28.0855, 30.973762, 32.065, 35.453, 39.948],
     [ 39.0983, 40.078, 44.955912, 47.867, 50.9415, 51.9961, 54.938045, 55.845, 58.933195, 58.6934, 63.546, 65.38, 69.723, 72.64, 74.92160, 78.96, 79.904, 83.798],
     [ 85.4678, 87.62, 88.90585, 91.224, 92.90638, 95.96, 98, 101.07, 102.90550, 106.42, 107.8682, 112.411, 114.818, 118.710, 121.760, 127.60, 126.90447, 131.293],
     [ 132.9054519, 137.327, .0, 178.49, 180.94788, 183.84, 186.207, 190.23, 192.217, 195.084, 196.966569, 200.59, 204.3833, 207.2, 208.98040, 209, 210, 222],
     [223, 226, .0, 267, 268, 271, 272, 270, 276, 281, 280, 285, 284, 289, 288, 293, 'unknown', 294],
     [.0, .0, 138.90547, 140.116, 140.90765, 144.242, 145, 150.36, 151.964, 157.25, 158.92535, 162.500, 164.93032, 167.259, 168.93421, 173.054, 174.9668, .0],
     [.0, .0, 227, 232.03806, 231.03588, 238.02891, 237, 244, 243, 247, 247, 251, 252, 257, 258, 259, 262, .0],
     [.0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0],
     [.0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0],
     [.0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0]]

z = [[.8, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, 1.],
     [.1, .2, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .7, .8, .8, .8, .9, 1.],
     [.1, .2, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .6, .7, .8, .8, .9, 1],
     [.1, .2, .3, .3, .3, .3, .3, .3, .3, .3, .3, .3, .6, .7, .8, .8, .9, 1.],
     [.1, .2, .3, .3, .3, .3, .3, .3, .3, .3, .3, .3, .6, .6, .7, .7, .9, 1.],
     [.1, .2, .4, .3, .3, .3, .3, .3, .3, .3, .3, .3, .6, .6, .6, .7, .9, 1.],
     [.1, .2, .5, .3, .3, .3, .3, .3, .3, .3, .3, .3, .6, .6, .6, .6, .9, 1.],
     [.0, .0, .4, .4, .4, .4, .4, .4, .4, .4, .4, .4, .4, .4, .4, .4, .4, .0],
     [.0, .0, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, .5, .0],
     [.0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0, .0],
     [.1, .1, .1, .3, .3, .3, .5, .5, .5, .7, .7, .7, .9, .9, .9, .0, .0, .0],
     [.2, .2, .2, .4, .4, .4, .6, .6, .6, .8, .8, .8, 1., 1., 1., .0, .0, .0]]

# Display element name and atomic mass on hover
hover=[]
for x in range(len(symbol)):
    hover.append([i + '<br>' + 'Atomic Mass: ' + str(j)
                      for i, j in zip(element[x], atomic_mass[x])])

# Invert Matrices
symbol = symbol[::-1]
hover = hover[::-1]
z = z[::-1]

# Set Colorscale
colorscale=[[0.0, 'rgb(255,255,255)'], [.2, 'rgb(255, 255, 153)'],
            [.4, 'rgb(153, 255, 204)'], [.6, 'rgb(179, 217, 255)'],
            [.8, 'rgb(240, 179, 255)'],[1.0, 'rgb(255, 77, 148)']]

# Make Annotated Heatmap
fig = ff.create_annotated_heatmap(z, annotation_text=symbol, text=hover,
                                 colorscale=colorscale, font_colors=['black'], hoverinfo='text')
fig.update_layout(title_text='Periodic Table')
fig.show()

以上就是本次全部内容,欢迎大家给个三连哦!

推荐阅读:
入门: 最全的零基础学Python的问题  | 零基础学了8个月的Python  | 实战项目 |学Python就是这条捷径
干货:爬取豆瓣短评,电影《后来的我们》 | 38年NBA最佳球员分析 |   从万众期待到口碑扑街!唐探3令人失望  | 笑看新倚天屠龙记 | 灯谜答题王 |用Python做个海量小姐姐素描图 |
趣味:弹球游戏  | 九宫格  | 漂亮的花 | 两百行Python《天天酷跑》游戏!
AI: 会做诗的机器人 | 给图片上色 | 预测收入 | 碟中谍这么火,我用机器学习做个迷你推荐系统电影

年度爆款文案

点阅读原文,领廖雪峰大数据视频资料!
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值