接受自己的普通,然后全力以赴的出众,告诉自己要努力,但不要着急....
前言
- 在自己的论文实验中,遇到了一个气象站实际观测数据与预测数据之间的对比,因此考虑绘制一个散点图来进行展示,但是我的实测数据与预测数据统计下来有100多万个样本点(时间分辨率比较高、统计时间较长),起初绘制散点图是这样的。。。
简单散点图绘制代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Name : simple_scatter.py
# Author : zengsk in NanJing
# Created: 2019/12/14 17:16
"""
Details: 不带颜色渲染的散点图绘制 (包含多个子图)
"""
import numpy as np
import matplotlib.pyplot as plt
def draw_scatter(x, y, marker_size=1, savefig_name=""):
# 创建画图窗口
fig = plt.figure(1, figsize=(8, 8))
# 将画图窗口分成2x2, 选择第一块区域作子图
subplot1 = fig.add_subplot(2, 2, 1)
# 画散点图
subplot1.scatter(x, y, s=marker_size, c='k', marker='.')
# 画参考线
subplot1.plot((0, 300), (0, 300), linestyle="--", linewidth=0.8, color="b")
# 调整坐标轴范围
plt.xlim((0, 300))
plt.ylim((0, 300))
# 设置坐标轴刻度
xticks = np.arange(0, 301, 50)
yticks = np.arange(0, 301, 50)
plt.xticks(xticks)
plt.yticks(yticks)
# 设置标题
subplot1.set_title('Scatter Plot')
# 设置坐标轴名称
subplot1.set_xlabel('Observation')
subplot1.set_ylabel('Estimate')
# 添加网格线
plt.grid(linestyle='--', color='grey')
# 全局修改字体
plt.rc('font', family='Times New Roman')
# save figure
fig.tight_layout()
if "" != savefig_name.strip():
plt.savefig(savefig_name, dpi=600)
plt.show()
# 主模块
if __name__ == "__main__":
# 加载数据
url = r"E:\Scripts\Test\scatter\data\estimate3.csv"
data = np.loadtxt(url, delimiter=',', skiprows=1)
x = data[:, 1]
y = data[:, 2]
draw_scatter(x, y, savefig_name="E:\Scripts\Test\scatter\data\scatter.jpg")
当然, 这个结果并不是我真正想要的,Pass, 太丑了!
- 样本点太多,一片黑色, 无法看出散点的聚集程度,如果能考虑用一个颜色带(colorbar),对图中的散点按照密集程度进行着色,那就完美了呗!当然,这个想法来自于我的师兄,我只是一个实现者....
好吧,安排,我们先看下实现后的效果!
这个效果自然就比之前的好多了!
下面介绍一下实现的思路和细节(自己在写代码的时候还是发现了很多坑)
实现python散点图绘制需要用到matplotlib库,matplotlib库是专门用于可视化绘图的工具库;学习一个新的库当然看官方文档了:https://www.osgeo.cn/matplotlib/contents.html
实现思路:
- 要实现对每个散点进行颜色渲染,我们指定一个计算半径radius,然后遍历每个样本点,并计算每个样本点在该半径(radius)范围内存在的散点数目来表示该散点的密度大小。那么我们最后就可以根据每个散点的密度大小利用色带进行颜色渲染。
散点密度计算如下:
def density_calc(x, y, radius):
"""
散点密度计算(以便给散点图中的散点密度进行颜色渲染)
:param x:
:param y:
:param radius:
:return: 数据密度
"""
res = np.empty(len(x), dtype=np.float32)
for i in range(len(x)):
print(i)
res[i] = np.sum((x > (x[i] - radius)) & (x < (x[i] + radius))
& (y > (y[i] - radius)) & (y < (y[i] + radius)))
return res
matplotlib.pyplot.scatter()函数是专门绘制散点图的函数:https://www.osgeo.cn/matplotlib/api/_as_gen/matplotlib.pyplot.scatter.html?highlight=scatter#matplotlib.pyplot.scatter
matplotlib.pyplot.scatter
(x, y, s=None, c=None, marker=None, cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, verts=None, edgecolors=None, ***, data=None, **kwargs) **
当我们通过上面的density_calc()函数计算得到每个样本点的散点密度Z1时,就可以根据scatter函数进行彩色散点图的绘制,那么如何根据计算的散点密度与色带colormap的颜色进行一一映射呢,需要设置三个参数:c、cmap、norm,具体参数设置如下:
plt.scatter(observation, estimate, c=Z1, cmap=colormap, marker=".", s=marker_size, norm=colors.LogNorm(vmin=Z1.min(), vmax=0.5 * Z1.max()))
其中:
1、c参数为计算的散点密度;
2、cmap为色带(matplotlib里面自带了很多色带可供选择),参见:
https://www.osgeo.cn/matplotlib/gallery/color/colormap_reference.html
3、由于计算的散点密度数值大小分散,因此利用norm参数对散点密度Z1进行归一化处理(归一化方式很多,参见colors类),并给归一化方式设置色带刻度的最大最小值vmin和vmax(一般这两个参数就是指定散点密度的最小值和最大值),这样就建立起了密度与色带的映射关系。
https://matplotlib.org/tutorials/colors/colormapnorms.html
不知道我这个通俗的解释能否看懂,语言表达能力就这样了!!!
完整代码如下:(这里展示了同时绘制三个子图的写法)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Name : scatter_render_main2.py
# Author : zengsk in NanJing
# Created: 2019/12/11 12:46
"""
Details: 散点图绘制
"""
import time
import numpy as np
import pandas as pd
import matplotlib.colors as colors
import matplotlib.pyplot as plt
def density_calc(x, y, radius):
"""
散点密度计算(以便给散点图中的散点密度进行颜色渲染)
:param x:
:param y:
:param radius:
:return: 数据密度
"""
res = np.empty(len(x), dtype=np.float32)
for i in range(len(x)):
print(i)
res[i] = np.sum((x > (x[i] - radius)) & (x < (x[i] + radius))
& (y > (y[i] - radius)) & (y < (y[i] + radius)))
return res
# Script Start...
start = time.process_time()
url_i = r"E:\Scripts\Test\scatter\data\estimate1.csv"
url_ii = r"E:\Scripts\Test\scatter\data\estimate2.csv"
url_iii = r"E:\Scripts\Test\scatter\data\estimate3.csv"
savefig_name = r"E:\Scripts\Test\scatter\data\scatter_render3.jpg"
# ------------ read data -----------------
matrix_i = pd.read_csv(url_i).values
sevp_i = matrix_i[:, 1] # 观测数据
estimate_i = matrix_i[:, 2] # 预测数据
matrix_ii = pd.read_csv(url_ii).values
sevp_ii = matrix_ii[:, 1]
estimate_ii = matrix_ii[:, 2]
matrix_iii = pd.read_csv(url_iii).values
sevp_iii = matrix_iii[:, 1]
estimate_iii = matrix_iii[:, 2]
# ----------- Define Parameters ------------
radius = 3 # 半径
colormap = plt.get_cmap("jet") # 色带
marker_size = 1 # 散点大小
xrange = [0, 350]
yrange = [0, 350]
xticks = np.linspace(0, 350, 8)
yticks = np.linspace(0, 350, 8)
xlabel = "Observation"
ylabel_i = "Estimate-1"
ylabel_ii = "Estimate-2"
ylabel_iii = "Estimate-3"
cbar_ticks = [10**0, 10**1, 10**2, 10**3, 10**4, 10**5]
font = {'family': 'Times New Roman',
'weight': 'bold',
'size': 7}
# ----------------- Plot Start -----------------
fig = plt.figure(1, facecolor="grey")
# --------------- sub plot no.1 ----------------
plt.subplot(1, 3, 1, aspect="equal")
Z1 = density_calc(sevp_i, estimate_i, radius)
plt.scatter(sevp_i, estimate_i, c=Z1, cmap=colormap, marker=".", s=marker_size,
norm=colors.LogNorm(vmin=Z1.min(), vmax=0.5 * Z1.max()))
plt.xlim(xrange)
plt.ylim(yrange)
plt.xticks(xticks, fontproperties='Times New Roman', size=7)
plt.yticks(yticks, fontproperties='Times New Roman', size=7)
plt.xlabel(xlabel, fontdict=font)
plt.ylabel(ylabel_i, fontdict=font)
plt.grid(linestyle='--', color="grey")
plt.plot(xrange, yrange, color="k", linewidth=0.8, linestyle='--')
plt.rc('font', **font)
# color bar
cbar = plt.colorbar(orientation='horizontal', extend="both", pad=0.1) # 显示色带
cbar.set_label("Scatter Density", fontdict=font)
cbar.set_ticks(cbar_ticks)
cbar.ax.tick_params(which="major", direction="in", length=2, labelsize=6) # 主刻度
cbar.ax.tick_params(which="minor", direction="in", length=0) # 副刻度
# --------------- sub plot no.2 ----------------
plt.subplot(1, 3, 2, aspect="equal")
Z2 = density_calc(sevp_ii, estimate_ii, radius)
plt.scatter(sevp_ii, estimate_ii, c=Z2, cmap=colormap, marker=".", s=marker_size,
norm=colors.LogNorm(vmin=Z2.min(), vmax=Z2.max()))
plt.xlim(xrange)
plt.ylim(yrange)
plt.xticks(xticks)
plt.yticks(yticks)
plt.xlabel(xlabel, fontsize=8)
plt.ylabel(ylabel_ii, fontsize=8)
plt.grid(linestyle='--', color="grey")
plt.plot(xrange, yrange, color="k", linewidth=0.8, linestyle='--')
plt.rc('font', **font)
# color bar
cbar = plt.colorbar(orientation='horizontal', extend="both", pad=0.1) # 显示色带
cbar.set_label("Scatter Density", fontdict=font)
cbar.set_ticks(cbar_ticks)
cbar.ax.tick_params(which="major", direction="in", length=2, labelsize=6) # 主刻度
cbar.ax.tick_params(which="minor", direction="in", length=0) # 副刻度
# --------------- sub plot no.3 ----------------
plt.subplot(1, 3, 3, aspect="equal")
Z3 = density_calc(sevp_iii, estimate_iii, radius)
plt.scatter(sevp_iii, estimate_iii, c=Z3, cmap=colormap, marker=".", s=marker_size,
norm=colors.LogNorm(vmin=Z3.min(), vmax=Z3.max()))
plt.xlim(xrange)
plt.ylim(yrange)
plt.xticks(xticks)
plt.yticks(yticks)
plt.xlabel(xlabel, fontsize=8)
plt.ylabel(ylabel_iii, fontsize=8)
plt.grid(linestyle='--', color="grey")
plt.plot(xrange, yrange, color="k", linewidth=0.8, linestyle='--')
plt.rc('font', **font)
# color bar
cbar = plt.colorbar(orientation='horizontal', extend="both", pad=0.1) # 显示色带
cbar.set_label("Scatter Density", fontdict=font)
cbar.set_ticks(cbar_ticks)
cbar.ax.tick_params(which="major", direction="in", length=2, labelsize=6) # 主刻度
cbar.ax.tick_params(which="minor", direction="in", length=0) # 副刻度
# save figure
fig.tight_layout()
plt.savefig(savefig_name, dpi=600)
plt.show()
elapsed = time.process_time() - start
print("This program totally costs {0} seconds!".format(elapsed))
print(" .... All is OK!!")
结果展示
(这里的结果与前面展示的相比改变了计算散点密度的半径:radius = 3以及绘制散点图的散点大小marksize)
作者能力水平有限,欢迎各位批评指正!