Python实现彩色散点图绘制(利用色带对散点图进行颜色渲染)

image

接受自己的普通,然后全力以赴的出众,告诉自己要努力,但不要着急....

image

前言

  • 在自己的论文实验中,遇到了一个气象站实际观测数据与预测数据之间的对比,因此考虑绘制一个散点图来进行展示,但是我的实测数据与预测数据统计下来有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")
image

当然, 这个结果并不是我真正想要的,Pass, 太丑了!

  • 样本点太多,一片黑色, 无法看出散点的聚集程度,如果能考虑用一个颜色带(colorbar),对图中的散点按照密集程度进行着色,那就完美了呗!当然,这个想法来自于我的师兄,我只是一个实现者....
image

好吧,安排,我们先看下实现后的效果!

image

这个效果自然就比之前的好多了!

下面介绍一下实现的思路和细节(自己在写代码的时候还是发现了很多坑)

实现python散点图绘制需要用到matplotlib库,matplotlib库是专门用于可视化绘图的工具库;学习一个新的库当然看官方文档了:https://www.osgeo.cn/matplotlib/contents.html

实现思路:

  • 要实现对每个散点进行颜色渲染,我们指定一个计算半径radius,然后遍历每个样本点,并计算每个样本点在该半径(radius)范围内存在的散点数目来表示该散点的密度大小。那么我们最后就可以根据每个散点的密度大小利用色带进行颜色渲染。

image

散点密度计算如下:

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) **

image
image

当我们通过上面的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)

image

作者能力水平有限,欢迎各位批评指正!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容