如何在matplotlib中绘制以密度着色的散点图?


82

我想作一个散点图,其中每个点都由附近点的空间密度着色。

我遇到了一个非常类似的问题,其中显示了使用R的示例:

R散点图:符号颜色代表重叠点的数量

使用matplotlib在python中完成类似操作的最佳方法是什么?


4
嗨!人们一直对您不满意,可能是因为您没有重写问题或提供任何上下文信息,也没有表现出自己做任何事情的尝试。考虑将问题编辑为自给自足(不只是链接),对于以后的问题,请在发布之前进行一些尝试。
askewchan

Answers:


157

除了@askewchan所建议的之外,hist2d或者hexbin您可以使用@askewchan建议的方法,也可以使用与链接到的问题中的已接受答案相同的方法。

如果要这样做:

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import gaussian_kde

# Generate fake data
x = np.random.normal(size=1000)
y = x * 3 + np.random.normal(size=1000)

# Calculate the point density
xy = np.vstack([x,y])
z = gaussian_kde(xy)(xy)

fig, ax = plt.subplots()
ax.scatter(x, y, c=z, s=100, edgecolor='')
plt.show()

在此处输入图片说明

如果希望按密度顺序绘制点,以使最密集的点始终位于顶部(类似于链接的示例),只需按z值对它们进行排序。我还将在此处使用较小的标记大小,因为它看起来更好一些:

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import gaussian_kde

# Generate fake data
x = np.random.normal(size=1000)
y = x * 3 + np.random.normal(size=1000)

# Calculate the point density
xy = np.vstack([x,y])
z = gaussian_kde(xy)(xy)

# Sort the points by density, so that the densest points are plotted last
idx = z.argsort()
x, y, z = x[idx], y[idx], z[idx]

fig, ax = plt.subplots()
ax.scatter(x, y, c=z, s=50, edgecolor='')
plt.show()

在此处输入图片说明


4
聪明,特别是让“最密集”的东西居上:)
askewchan

5
@Leszek-Ether调用plt.colorbar(),或者如果您想更明确一些,请执行cax = ax.scatter(...)然后再执行fig.colorbar(cax)。请注意,单位是不同的。此方法估计点的概率分布函数,因此值将在0到1之间(通常不会非常接近1)。您可以转换回更接近直方图计数的内容,但这需要一些工作(您需要了解gaussian_kde根据数据估算的参数)。
乔·肯顿2014年

1
非常好!在Python中检查其他KDE也会很有用:jakevdp.github.io/blog/2013/12/01/kernel-density-estimationscikit-learn.org/stable/modules/density.html在我的情况下scipy.stats 'KDE花费的时间太长
Rems

1
为什么高斯内核用(xy)调用两次?
Arjan Groen

@ArjanGroen第一次调用将创建一个新的gaussian_kde对象,第二次调用将根据该组点对估计的pdf进行评估(调用评估方法的快捷方式)。
qRTPCR '17

34

您可以制作一个直方图:

import numpy as np
import matplotlib.pyplot as plt

# fake data:
a = np.random.normal(size=1000)
b = a*3 + np.random.normal(size=1000)

plt.hist2d(a, b, (50, 50), cmap=plt.cm.jet)
plt.colorbar()

2dhist


26

另外,如果点数使KDE计算太慢,则可以在np.histogram2d中插入颜色[更新以响应注释:如果要显示颜色条,请使用plt.scatter()代替ax.scatter()通过plt.colorbar()]:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.colors import Normalize 
from scipy.interpolate import interpn

def density_scatter( x , y, ax = None, sort = True, bins = 20, **kwargs )   :
    """
    Scatter plot colored by 2d histogram
    """
    if ax is None :
        fig , ax = plt.subplots()
    data , x_e, y_e = np.histogram2d( x, y, bins = bins, density = True )
    z = interpn( ( 0.5*(x_e[1:] + x_e[:-1]) , 0.5*(y_e[1:]+y_e[:-1]) ) , data , np.vstack([x,y]).T , method = "splinef2d", bounds_error = False)

    #To be sure to plot all data
    z[np.where(np.isnan(z))] = 0.0

    # Sort the points by density, so that the densest points are plotted last
    if sort :
        idx = z.argsort()
        x, y, z = x[idx], y[idx], z[idx]

    ax.scatter( x, y, c=z, **kwargs )

    norm = Normalize(vmin = np.min(z), vmax = np.max(z))
    cbar = fig.colorbar(cm.ScalarMappable(norm = norm), ax=ax)
    cbar.ax.set_ylabel('Density')

    return ax


if "__main__" == __name__ :

    x = np.random.normal(size=100000)
    y = x * 3 + np.random.normal(size=100000)
    density_scatter( x, y, bins = [30,30] )


这是一个很好的提示,谢谢。我正在绘制10万个点,而gaussian_kde太慢了。
伊曼纽尔

2
警告,我注意到在某些情况下会生成NaN,并且因为“ bounds_error = False”,所以它是无声的。c设置为NaNs的点不绘制。gaussian_kde没问题。
伊曼纽尔

非常感谢您的回复。通常,当我们有大量数据点时,我们希望这样的热图,而在这种情况下,KDE的速度非常慢。但是,仍然存在一个未解决的问题。我要包括一个指示频率的彩条!这将引发错误:'AxesSubplot'对象没有属性'autoscale_None'。我做了“ plt.colorbar(scat,ax = ax)”
Vinod Kumar,

@VinodKumar您是否知道如何绘制色标?
丹尼尔

1
@Daniel是的,这是可能的,请参阅编辑后的答案。然后,您必须在构建直方图时设置“ density = True”,否则,颜色条取决于纸箱大小。@ Emanuel,的确!我已将NaN替换为零以确保绘制所有点(NaN应该在没有太多数据的情况下发生,所以0.0应该足够好)
Guillaume

4

绘制> 100k数据点?

接受的答案,使用gaussian_kde()将花费大量的时间。在我的机器上,10万行花了大约11分钟。在这里,我将添加两种替代方法(mpl-scatter-densitydatashader),并将给定的答案与相同的数据集进行比较。

在下面,我使用了一个100k行的测试数据集:

import matplotlib.pyplot as plt
import numpy as np

# Fake data for testing
x = np.random.normal(size=100000)
y = x * 3 + np.random.normal(size=100000)

输出和计算时间比较

以下是不同方法的比较。

1: mpl-scatter-density

安装

pip install mpl-scatter-density

范例程式码

import mpl_scatter_density # adds projection='scatter_density'
from matplotlib.colors import LinearSegmentedColormap

# "Viridis-like" colormap with white background
white_viridis = LinearSegmentedColormap.from_list('white_viridis', [
    (0, '#ffffff'),
    (1e-20, '#440053'),
    (0.2, '#404388'),
    (0.4, '#2a788e'),
    (0.6, '#21a784'),
    (0.8, '#78d151'),
    (1, '#fde624'),
], N=256)

def using_mpl_scatter_density(fig, x, y):
    ax = fig.add_subplot(1, 1, 1, projection='scatter_density')
    density = ax.scatter_density(x, y, cmap=white_viridis)
    fig.colorbar(density, label='Number of points per pixel')

fig = plt.figure()
using_mpl_scatter_density(fig, x, y)
plt.show()

绘制时间为0.05秒: 使用mpl-scatter-density

放大看起来非常不错: 放大mpl-scatter-density

2: datashader

pip install "git+https://github.com/nvictus/datashader.git@mpl"

代码(此处dsshow的源):

from functools import partial

import datashader as ds
from datashader.mpl_ext import dsshow
import pandas as pd

dyn = partial(ds.tf.dynspread, max_px=40, threshold=0.5)

def using_datashader(ax, x, y):

    df = pd.DataFrame(dict(x=x, y=y))
    da1 = dsshow(df, ds.Point('x', 'y'), spread_fn=dyn, aspect='auto', ax=ax)
    plt.colorbar(da1)

fig, ax = plt.subplots()
using_datashader(ax, x, y)
plt.show()
  • 花费了0.83 s来绘制:

在此处输入图片说明

缩放后的图像看起来很棒!

在此处输入图片说明

3: scatter_with_gaussian_kde

def scatter_with_gaussian_kde(ax, x, y):
    # https://stackoverflow.com/a/20107592/3015186
    # Answer by Joel Kington

    xy = np.vstack([x, y])
    z = gaussian_kde(xy)(xy)

    ax.scatter(x, y, c=z, s=100, edgecolor='')
  • 花了11分钟画出了这个: scatter_with_gaussian_kde

4: using_hist2d

import matplotlib.pyplot as plt
def using_hist2d(ax, x, y, bins=(50, 50)):
    # https://stackoverflow.com/a/20105673/3015186
    # Answer by askewchan
    ax.hist2d(x, y, bins, cmap=plt.cm.jet)

  • 绘制此容器=(50,50)用了0.021 s: using_hist2d_50
  • 花了0.173 s绘制了这些容器=(1000,1000): using_hist2d_1000
  • 缺点:放大后的数据看起来不如mpl-scatter-density或datashader那样好。另外,您还必须自己确定垃圾箱的数量。

放大hist2d 1000bins

5: density_scatter

  • 该代码是在回答纪尧姆
  • 用bins =(50,50)画了0.073 s: density_scatter_50bins
  • 用bins =(1000,1000)画了0.368 s: density_scatter_1000bins
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.