Як я можу зробити графік розсіювання, пофарбований щільністю в matplotlib?


83

Я хотів би зробити графік розсіювання, де кожна точка забарвлена ​​просторовою щільністю сусідніх точок.

Я натрапив на дуже подібне запитання, яке показує приклад цього з використанням R:

R Графік розсіяння: колір символу представляє кількість точок, що перекриваються

Який найкращий спосіб зробити щось подібне в python за допомогою matplotlib?


4
Привіт! Люди проголосували проти вас, мабуть, тому, що ви не переписали запитання, не дали жодного контексту, і не показали жодної спроби зробити це самостійно. Вважайте редагування запитання самодостатнім (а не лише посиланням), а для майбутніх питань, будь-ласка, зробіть спробу перед публікацією.
askewchan

Відповіді:


158

На додаток до 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 - Ефірний дзвінок plt.colorbar(), або якщо ви хочете бути більш чітким, зробіть cax = ax.scatter(...)і потім fig.colorbar(cax). Майте на увазі, що одиниці вимірювання різні. Цей метод оцінює функцію розподілу ймовірностей для точок, тому значення будуть між 0 і 1 (і, як правило, не наближаються до 1). Ви можете перетворити назад на щось ближче до числа гістограм, але для цього потрібно трохи попрацювати (вам потрібно знати параметри, gaussian_kdeоцінені з даних).
Джо Кінгтон,

1
Дуже мило! Перевірка інших KDE в Python також може бути корисною: jakevdp.github.io/blog/2013/12/01/kernel-density-estimation та scikit-learn.org/stable/modules/density.html У моєму випадку scipy.stats 'KDE зайняв занадто багато часу
Rems

1
Чому ядро ​​Гауса називається двічі за допомогою (xy)?
Arjan Groen

@ArjanGroen Перший виклик створює новий об'єкт gaussian_kde, а другий виклик обчислює передбачуваний pdf для безлічі точок (ярлик для виклику методу оцінки).
qRTPCR

35

Ви можете скласти гістограму:

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

2-гіст


Щоб краще відповідати масштабу рішення Джо Кінгтона, можливо, ви захочете побудувати графік у масштабі журналу  : plt.hist2d(…, norm = LogNorm())(with from matplotlib.colors import LogNorm).
Skippy le Grand Gourou

29

Крім того, якщо кількість точок робить обчислення 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] )


1
Це чудова порада, дякую. Я планував 100 тис. Балів, і gaussian_kde був надмірно повільним.
Емануель

2
Попередження, я помітив, що в деяких випадках це генерує NaN, і оскільки "bounds_error = False", він мовчить. Точки, для яких c встановлено значення NaN, не будуються. Це не проблема з gaussian_kde.
Емануель

Велике спасибі за таку відповідь. Зазвичай нам потрібна така теплова карта, коли ми маємо велику кількість точок даних, і в цьому випадку KDE працює дуже повільно. Однак питання все ще залишається відкритим. Я хочу включити кольорову смужку із зазначенням частоти! Це видає помилку: об'єкт "AxesSubplot" не має атрибута "autoscale_None". Я зробив "plt.colorbar (scat, ax = ax)"
Вінод Кумар,

@VinodKumar ти дізнався, як побудувати кольорову смугу?
Даніель

1
@Daniel так, це можливо, див. Відредаговану відповідь. Потім вам потрібно встановити "щільність = Істинно" при побудові гістограми, інакше кольорова панель залежить від розміру кошика. @ Емануель, справді! Я замінив NaN на нуль, щоб переконатись побудувати всі точки (NaN повинні відбуватися, коли даних недостатньо, тому 0.0 має бути в порядку)
Гійом

6

Ділянка> 100 тис. Точок даних?

Загальноприйнятий відповідь , використовуючи gaussian_kde () займе багато часу. На моїй машині 100 тис. Рядків зайняли близько 11 хвилин . Тут я додам два альтернативні методи ( mpl-scatter-density і datashader ) і порівняю подані відповіді з тим самим набором даних.

Далі я використовував тестовий набір даних із 100 тис. Рядків:

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 хвилин: розкидання_з_гаусовим_кде

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 с: using_hist2d_50
  • Для виведення цих бункерів = (1000,1000) знадобилося 0,173 с: using_hist2d_1000
  • Мінуси: збільшені дані виглядають не так добре, як у випадку з mpl-щільністю розсіювання або шейдером даних. Також вам доведеться самостійно визначати кількість смітників.

збільшено в hist2d 1000bins

5: density_scatter

  • Код , як і в відповідь з боку Гійома .
  • Для того, щоб намалювати це за допомогою bins = (50,50), знадобилося 0,073 с: щільність_розсіювання_50бінів
  • Щоб намалювати це за допомогою bins = (1000,1000), знадобилося 0,368 с: щільність_розсіювання_1000бінів
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.