簡體   English   中英

如何在 matplotlib 中制作按密度着色的散點圖?

[英]How can I make a scatter plot colored by density in matplotlib?

我想制作一個散點圖,其中每個點都由附近點的空間密度着色。

我遇到了一個非常相似的問題,它顯示了一個使用 R 的例子:

R 散點圖:符號顏色代表重疊點的數量

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

除了@askewchan 建議的hist2dhexbin之外,您還可以使用與鏈接到的問題中接受的答案相同的方法。

如果你想這樣做:

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)
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)
plt.show()

在此處輸入圖片說明

你可以做一個直方圖:

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

此外,如果點的數量使 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] )

繪制 > 100k 數據點?

接受的答案,使用gaussian_kde()會花費很多時間。 在我的機器上,10 萬行大約需要11 分鍾 在這里,我將添加兩種替代方法( mpl-scatter-densitydatashader )並將給定的答案與相同的數據集進行比較。

在下面,我使用了一個 10 萬行的測試數據集:

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 散射密度

放大看起來很不錯: 放大 mpl 散射密度

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 秒:

在此處輸入圖片說明

放大的圖像看起來很棒!

在此處輸入圖片說明

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)

  • 繪制這個 bins=(50,50) 花了 0.021 秒: using_hist2d_50
  • 繪制這個 bins=(1000,1000) 花了 0.173 秒: using_hist2d_1000
  • 缺點:放大后的數據看起來不如 mpl-scatter-density 或 datashader 中的好。 您還必須自己確定垃圾箱的數量。

放大 hist2d 1000bins

5: density_scatter

  • 代碼與Guillaume回答相同
  • 用 bins=(50,50) 繪制這個花了 0.073 秒: density_scatter_50bins
  • 用 bins=(1000,1000) 繪制這個花了 0.368 秒: density_scatter_1000bins

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM