Matplotlib'de yoğunluğa göre renklendirilmiş bir dağılım grafiği nasıl yapabilirim?


83

Her noktanın yakın noktaların uzamsal yoğunluğu ile renklendirildiği bir dağılım grafiği yapmak istiyorum.

R kullanarak bunun bir örneğini gösteren çok benzer bir soruyla karşılaştım:

R Dağılım Grafiği: sembol rengi örtüşen noktaların sayısını temsil eder

Python'da matplotlib kullanarak benzer bir şeyi başarmanın en iyi yolu nedir?


4
Selam! İnsanlar muhtemelen soruyu yeniden yazmadığın veya herhangi bir bağlam vermediğin ya da bu şeyi kendi başına yapmaya çalışmadığın için seni olumsuz oyladılar. Soruyu kendi kendine yeterli olacak şekilde (yalnızca bir bağlantı değil) düzenlemeyi düşünün ve gelecekteki sorular için lütfen göndermeden önce biraz deneyin.
askewchan

Yanıtlar:


157

@Askewchan'a ek olarak hist2dveya hexbinönerdiği gibi, bağlantı kurduğunuz sorudaki kabul edilen cevabın kullandığı aynı yöntemi kullanabilirsiniz.

Bunu yapmak istiyorsanı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)

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

görüntü açıklamasını buraya girin

En yoğun noktaların her zaman en üstte olması için noktaların yoğunluk sırasına göre çizilmesini istiyorsanız (bağlantılı örneğe benzer şekilde), bunları z değerlerine göre sıralayın. Biraz daha iyi göründüğü için burada daha küçük bir işaretçi boyutu da kullanacağım:

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

görüntü açıklamasını buraya girin


4
Zekice, özellikle 'en yoğun' olanları zirveye
çıkarmak

5
@Leszek - Ether çağrısı plt.colorbar()veya daha açık konuşmayı tercih ederseniz, yapın cax = ax.scatter(...)ve sonra fig.colorbar(cax). Birimlerin farklı olduğunu unutmayın. Bu yöntem, noktalar için olasılık dağılım fonksiyonunu tahmin eder, bu nedenle değerler 0 ile 1 arasında olacaktır (ve genellikle 1'e çok yaklaşmayacaktır). Histogram sayılarına daha yakın bir şeye geri dönüş yapabilirsiniz, ancak biraz çalışma gerektirir ( gaussian_kdeverilerden tahmin edilen parametreleri bilmeniz gerekir ).
Joe Kington

1
Çok hoş! Python'daki diğer KDE'leri kontrol etmek de yararlı olabilir: jakevdp.github.io/blog/2013/12/01/kernel-density-estimation ve scikit-learn.org/stable/modules/density.html Benim durumumda scipy.stats KDE çok uzun sürdü
Rems

1
Neden Gauss çekirdeği (xy) ile iki kez çağrılır?
Arjan Groen

@ArjanGroen İlk çağrı yeni bir gaussian_kde nesnesi oluşturur ve ikinci çağrı, nokta kümesindeki tahmini pdf'yi değerlendirir (değerlendirme yöntemini çağırmak için kısayol).
qRTPCR

35

Bir histogram oluşturabilirsiniz:

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


Joe Kington'ın çözümünün ölçeğini daha iyi eşleştirmek için , logscale  : plt.hist2d(…, norm = LogNorm())(with from matplotlib.colors import LogNorm) ' de grafiğini çizmek isteyebilirsiniz .
Skippy le Grand Gourou

29

Ayrıca, nokta sayısı KDE hesaplamasını çok yavaş yapıyorsa, renk np.histogram2d [Yorumlara yanıt olarak güncelleme: Renk çubuğunu göstermek istiyorsanız, ax.scatter () yerine plt.scatter () kullanın ve ardından plt.colorbar ()] tarafından:

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


1
Bu harika bir ipucu, teşekkürler. 100k puan çiziyordum ve gaussian_kde engelleyici bir şekilde yavaştı.
Emanuel

2
Uyarı, bazı durumlarda bunun NaN'ler oluşturduğunu ve "bounds_error = False" sessiz olduğundan fark ettim. NaNs'ye ayarlanmış c olan noktalar çizilmez. Bu gaussian_kde ile ilgili bir sorun değil.
Emanuel

Bu yanıt için çok teşekkürler. Genellikle çok sayıda veri noktamız olduğunda böyle bir ısı haritası isteriz ve bu durumda KDE çok yavaştır. Ancak, hala açık bir sorun var. Frekansı gösteren bir renk çubuğu eklemek istiyorum! Bu bir hata verir: 'AxesSubplot' nesnesinin 'autoscale_None' özniteliği yoktur. "Plt.colorbar (scat, ax = ax)" yaptım
Vinod Kumar

@VinodKumar, renk çubuğunu nasıl çizeceğinizi buldunuz mu?
Daniel

1
@Daniel evet bu mümkün, düzenlenmiş cevaba bakınız. Daha sonra histogramı oluştururken "yoğunluk = Doğru" ayarlamanız gerekir, aksi takdirde renk çubuğu bölme boyutuna bağlıdır. @ Emanuel, Gerçekten! Tüm noktaların grafiğini çizdiğinizden emin olmak için NaN'leri sıfır ile değiştirdim (NaN'ler çok fazla veri olmadığında olmalıdır, böylece 0.0 yeterli olmalıdır)
Guillaume

6

> 100.000 veri noktası mı çiziyorsunuz?

Kabul edilen cevap , gaussian_kde () kullanmak çok zaman alacaktır. Makinemde 100.000 sıra yaklaşık 11 dakika sürdü . Burada iki alternatif yöntem ekleyeceğim ( mpl-scatter -ensity ve datashader ) ve verilen cevapları aynı veri karşılaştıracağım.

Aşağıda, 100.000 satırlık bir test veri seti kullandım:

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)

Çıktı ve hesaplama süresi karşılaştırması

Aşağıda farklı yöntemlerin bir karşılaştırması bulunmaktadır.

1: mpl-scatter-density

Kurulum

pip install mpl-scatter-density

Örnek kod

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

Bunun çizilmesi 0.05 saniye sürdü: mpl dağılım yoğunluğu kullanarak

Ve yakınlaştırma oldukça hoş görünüyor: mpl dağılım yoğunluğunu yakınlaştır

2: datashader

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

Kod (dsshow'un kaynağı burada ):

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()
  • Bunu çizmek için 0.83 saniye sürdü:

görüntü açıklamasını buraya girin

ve yakınlaştırılmış görüntü harika görünüyor!

görüntü açıklamasını buraya girin

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='')
  • Bunu çizmek 11 dakika sürdü: 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)

  • Bu kutuları çizmek 0.021 saniye sürdü = (50,50): using_hist2d_50
  • Bu kutuları çizmek için 0,173 saniye sürdü = (1000,1000): using_hist2d_1000
  • Eksileri: Yakınlaştırılmış veriler, mpl-scatter -ensity veya datashader ile olduğu kadar iyi görünmüyor. Ayrıca kutu sayısını kendiniz belirlemelisiniz.

hist2d 1000bins'de yakınlaştırılmış

5: density_scatter

  • Kod içinde gibidir cevap tarafından Guillaume .
  • Bunu bin = (50,50) ile çizmek 0.073 s sürdü: yoğunluk_ dağılım_50bins
  • Bunu binlerle çizmek için 0,368 saniye sürdü = (1000,1000): yoğunluk_ dağılım_1000bins
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.