Контуры рассеянности в Matplotlib

У меня массивная диаграмма рассеяния (~ 100 000 точек), которую я генерирую в matplotlib. Каждая точка имеет место в этом пространстве x/y, и я хотел бы генерировать контуры, содержащие определенные процентили от общего количества точек.

Есть ли функция в matplotlib, которая будет делать это? Я просмотрел контур(), но мне пришлось бы написать свою собственную функцию для работы таким образом.

Спасибо!

Ответ 1

В принципе, вам нужна оценка плотности. Существует несколько способов сделать это:

  • Используйте двумерную гистограмму какого-либо типа (например, matplotlib.pyplot.hist2d или matplotlib.pyplot.hexbin) (вы также можете отображать результаты как контуры - просто используйте numpy.histogram2d, а затем контур результирующего массива.)

  • Сделайте оценку плотности ядра (KDE) и контур результатов. KDE - это, по сути, сглаженная гистограмма. Вместо точки, попадающей в конкретный бункер, он добавляет вес окружающим бункерам (обычно в форме гауссовой "колоколообразной кривой" ).

Использование 2D-гистограммы прост и понятен, но фундаментально дает "блочные" результаты.

Есть несколько морщин, чтобы сделать второй "правильно" (т.е. нет ни одного правильного пути). Я не буду вдаваться в подробности здесь, но если вы хотите интерпретировать результаты статистически, вам нужно прочитать об этом (в частности, выбор полосы пропускания).

Во всяком случае, вот пример различий. Я собираюсь построить каждый из них одинаково, поэтому я не буду использовать контуры, но вы можете так же легко построить 2D-гистограмму или гауссовский KDE, используя контурный график:

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

np.random.seed(1977)

# Generate 200 correlated x,y points
data = np.random.multivariate_normal([0, 0], [[1, 0.5], [0.5, 3]], 200)
x, y = data.T

nbins = 20

fig, axes = plt.subplots(ncols=2, nrows=2, sharex=True, sharey=True)

axes[0, 0].set_title('Scatterplot')
axes[0, 0].plot(x, y, 'ko')

axes[0, 1].set_title('Hexbin plot')
axes[0, 1].hexbin(x, y, gridsize=nbins)

axes[1, 0].set_title('2D Histogram')
axes[1, 0].hist2d(x, y, bins=nbins)

# Evaluate a gaussian kde on a regular grid of nbins x nbins over data extents
k = kde.gaussian_kde(data.T)
xi, yi = np.mgrid[x.min():x.max():nbins*1j, y.min():y.max():nbins*1j]
zi = k(np.vstack([xi.flatten(), yi.flatten()]))

axes[1, 1].set_title('Gaussian KDE')
axes[1, 1].pcolormesh(xi, yi, zi.reshape(xi.shape))

fig.tight_layout()
plt.show()

enter image description here

Одно предупреждение: с очень большим количеством точек scipy.stats.gaussian_kde станет очень медленным. Это довольно легко ускорить, сделав приближение - просто возьмите 2D-гистограмму и смажьте ее с помощью гуассианского фильтра с правильным радиусом и ковариацией. Я могу привести пример, если вы хотите.

Еще одно предостережение: если вы делаете это в некартовской системе координат, ни один из этих методов не применяется! Получение оценок плотности на сферической оболочке несколько сложнее.

Ответ 2

У меня такой же вопрос. Если вы хотите построить контуры, содержащие какую-то часть точек, вы можете использовать следующий алгоритм:

создать 2d гистограмму

h2, xedges, yedges = np.histogram2d(X, Y, bibs = [30, 30])

h2 теперь является 2d-матрицей, содержащей целые числа, которые являются числом точек в некотором прямоугольнике

hravel = np.sort(np.ravel(h2))[-1] #all possible cases for rectangles 
hcumsum = np.sumsum(hravel)

уродливый взлом,

пусть для каждой точки матрицы h2 2d указывается кумулятивное число точек для прямоугольника, которые содержат число точек, равное или большее, которое мы анализируем в настоящее время.

hunique = np.unique(hravel)

hsum = np.sum(h2)

for h in hunique:
    h2[h2 == h] = hcumsum[np.argwhere(hravel == h)[-1]]/hsum

теперь контур контура для h2, это будет контур, содержащий некоторое количество всех точек