matplotlib에서 밀도로 색상이 지정된 산점도를 어떻게 만들 수 있습니까?


83

각 지점이 주변 지점의 공간 밀도에 따라 색상이 지정되는 산점도를 만들고 싶습니다.

R을 사용하는 예를 보여주는 매우 유사한 질문을 보았습니다.

R 산점도 : 기호 색상은 겹치는 점의 수를 나타냅니다.

matplotlib를 사용하여 Python에서 비슷한 작업을 수행하는 가장 좋은 방법은 무엇입니까?


4
안녕하세요! 사람들은 아마도 당신이 질문을 다시 작성하지 않았거나 맥락을 제공하지 않았기 때문에 당신을 비추천했습니다. 링크가 아닌 자급 자족하도록 질문을 편집하고 향후 질문에 대해서는 게시하기 전에 몇 가지 시도해보십시오.
askewchan 2013

답변:


157

@askewchan이 제안한 것 외에 hist2d또는 hexbin링크 한 질문에서 수락 한 답변이 사용하는 것과 동일한 방법을 사용할 수 있습니다.

원하는 경우 :

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합니다 (데이터에서 추정 한 매개 변수를 알아야 함 ).
Joe Kington

1
아주 좋아요! 파이썬에서 다른 KDE를 확인하는 것도 유용 할 수 있습니다 : jakevdp.github.io/blog/2013/12/01/kernel-density-estimationscikit-learn.org/stable/modules/density.html 제 경우에는 scipy.stats 'KDE가 너무 오래
걸렸

1
가우스 커널이 (xy)로 두 번 호출되는 이유는 무엇입니까?
Arjan Groen

@ArjanGroen 첫 번째 호출은 새로운 gaussian_kde 객체를 생성하고 두 번째 호출은 포인트 집합에서 추정 된 pdf를 평가합니다 (evaluate 메서드를 호출하기위한 바로 가기).
qRTPCR

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에서 색상을 보간 할 수 있습니다. [주석에 대한 응답 업데이트 : 컬러 바를 표시하려면 ax.scatter () 대신 plt.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는 엄청나게 느 렸습니다.
Emanuel

2
경고, 어떤 경우에는 이것이 NaN을 생성하고 "bounds_error = False"이기 때문에 조용하다는 것을 알았습니다. c가 NaN으로 설정된 점은 플로팅되지 않습니다. 이것은 gaussian_kde의 문제가 아닙니다.
Emanuel

이 답변에 감사드립니다. 일반적으로 데이터 포인트가 많을 때 이와 같은 히트 맵을 원하며이 경우 KDE가 매우 느립니다. 그러나 아직 미해결 문제가 있습니다. 주파수를 나타내는 컬러 바를 포함하고 싶습니다! 이 경우 오류가 발생합니다. 'AxesSubplot'개체에 'autoscale_None'속성이 없습니다. "plt.colorbar (scat, ax = ax)"를했습니다
Vinod Kumar

@VinodKumar 컬러 바를 그리는 방법을 알아 냈습니까?
Daniel

1
@Daniel 예 가능합니다. 편집 된 답변을 참조하십시오. 그런 다음 히스토그램을 작성할 때 "density = True"를 설정해야합니다. 그렇지 않으면 컬러 바가 bin 크기에 따라 달라집니다. @ Emanuel, Indeed! 모든 포인트를 표시하기 위해 NaN을 0으로 대체했습니다 (데이터가 많지 않을 때 NaN이 발생해야하므로 0.0이 충분해야합니다)
Guillaume

4

10 만 개 이상의 데이터 포인트를 플로팅하십니까?

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-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 초가 걸렸습니다.

여기에 이미지 설명 입력

확대 된 이미지가 멋져 보입니다!

여기에 이미지 설명 입력

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

  • 이 코드는 같이이다 대답 하여 기욤 .
  • bins = (50,50)으로 이것을 그리는 데 0.073 초가 걸렸습니다. density_scatter_50bins
  • bins = (1000,1000)로 이것을 그리는 데 0.368 초가 걸렸습니다. density_scatter_1000bins
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.