Расчет статистики каждого контура kdeplot

У меня есть следующее DataFrame:

import pandas as pd 
ex = pd.DataFrame({'x': {51963: 60.0, 52020: 110.0, 52054: 90.0, 52071: 86.0, 52072: 86.0, 52073: 86.0, 52131: 96.0, 52132: 96.0, 52140: 115.0, 52209: 92.0, 52346: 112.0, 52347: 114.0, 52429: 103.0, 52497: 104.0, 52561: 88.0, 52626: 110.0, 52627: 110.0, 52630: 109.0, 52631: 111.0, 52685: 105.0, 52725: 95.0, 52726: 95.0, 52727: 95.0, 52915: 100.0, 52916: 100.0, 52918: 101.0, 52936: 64.0, 52940: 66.0, 52987: 96.0, 52988: 96.0, 53088: 67.0, 53122: 76.0, 53123: 76.0, 53172: 105.0, 53420: 5.0, 53561: 105.0, 53585: 114.0, 53586: 114.0, 53587: 118.0, 53681: 105.0, 53927: 115.0, 53968: 117.0, 53993: 107.0, 54062: 114.0, 54063: 113.0, 54140: 103.0, 54141: 103.0, 54143: 107.0, 54145: 107.0, 54146: 107.0, 54200: 84.0, 54624: 88.0, 54625: 88.0, 54661: 116.0, 54664: 114.0, 54679: 119.0, 54685: 67.0, 54695: 59.0, 54706: 64.0, 54711: 69.0, 54722: 70.0, 54751: 100.0, 54753: 104.0, 54934: 81.0, 54960: 67.0, 55028: 107.0, 55082: 99.0, 55083: 99.0, 55084: 99.0, 55198: 102.0, 55199: 102.0, 55200: 102.0, 55279: 55.0, 55280: 55.0, 55388: 99.0, 55391: 97.0, 55392: 96.0, 55459: 97.0, 55460: 97.0, 55464: 99.0, 55465: 99.0, 55467: 97.0, 55499: 113.0, 55500: 113.0, 55501: 114.0, 55504: 107.0, 111812: 61.0, 111862: 69.0, 111863: 69.0, 111864: 68.0, 111868: 68.0, 111872: 63.0, 111971: 82.0, 111972: 82.0, 111974: 83.0, 111995: 101.0, 111996: 101.0, 111997: 102.0, 112041: 95.0, 112042: 95.0}, 'y': {51963: 41.0, 52020: 45.0, 52054: 57.0, 52071: 12.0, 52072: 12.0, 52073: 13.0, 52131: 26.0, 52132: 26.0, 52140: 34.0, 52209: 19.0, 52346: 47.0, 52347: 45.0, 52429: 39.0, 52497: 18.0, 52561: 12.0, 52626: 54.0, 52627: 54.0, 52630: 53.0, 52631: 51.0, 52685: 35.0, 52725: 37.0, 52726: 37.0, 52727: 37.0, 52915: 58.0, 52916: 58.0, 52918: 58.0, 52936: 34.0, 52940: 41.0, 52987: 52.0, 52988: 52.0, 53088: 28.0, 53122: 52.0, 53123: 52.0, 53172: 32.0, 53420: 37.0, 53561: 13.0, 53585: 28.0, 53586: 28.0, 53587: 21.0, 53681: 26.0, 53927: 38.0, 53968: 38.0, 53993: 35.0, 54062: 32.0, 54063: 31.0, 54140: 41.0, 54141: 41.0, 54143: 33.0, 54145: 36.0, 54146: 36.0, 54200: 24.0, 54624: 14.0, 54625: 14.0, 54661: 40.0, 54664: 41.0, 54679: 39.0, 54685: 43.0, 54695: 59.0, 54706: 44.0, 54711: 28.0, 54722: 18.0, 54751: 22.0, 54753: 22.0, 54934: 57.0, 54960: 51.0, 55028: 22.0, 55082: 19.0, 55083: 19.0, 55084: 19.0, 55198: 27.0, 55199: 27.0, 55200: 27.0, 55279: 44.0, 55280: 44.0, 55388: 29.0, 55391: 30.0, 55392: 33.0, 55459: 14.0, 55460: 14.0, 55464: 9.0, 55465: 9.0, 55467: 10.0, 55499: 11.0, 55500: 11.0, 55501: 8.0, 55504: 14.0, 111812: 40.0, 111862: 24.0, 111863: 24.0, 111864: 21.0, 111868: 23.0, 111872: 5.0, 111971: 18.0, 111972: 18.0, 111974: 16.0, 111995: 14.0, 111996: 14.0, 111997: 12.0, 112041: 15.0, 112042: 15.0}})

Для этого DataFrame я могу построить график плотности с помощью sns.kdeplot() следующим образом:

import seaborn as sns
ax = sns.kdeplot(df.x, df.y, cmap = 'Reds')
ax.set_xlabel('')
ax.set_ylabel('')

введите здесь описание изображения

Из того, что я заметил, по умолчанию на sns.kdeplot() есть 10 контуров, что означает, что данные разделены на 11 бинов, причем контуры делят бины с разной плотностью. Допустим, я хочу взять 3-й контур, считая от самого внешнего, и рассчитать для него различные статистические данные, например. область, заключенная в этот контур, горизонтальный или вертикальный диапазон и т. д. Как я могу это сделать? Другими словами, как мне рассчитать 2-мерный kde, а затем вычислить площадь, для которой kde больше, например, 0,2?


person jakes    schedule 21.08.2019    source источник
comment
В принципе, вы можете получить данные контура от ax.collections[0].collections[2] и применить формулу шнурка, чтобы получить площадь.   -  person ImportanceOfBeingErnest    schedule 21.08.2019
comment
обратите внимание, что KDE не группирует данные, как это делают гистограммы, это означает оценку плотности ядра и является хорошим непараметрическим способом сглаживания данных. kdeplot Seaborn использует scipy gaussian_kde для выполнения тяжелая работа, а затем использует contour от matplotlib для фактического расчета/рисования контуров   -  person Sam Mason    schedule 22.08.2019
comment
Да, но контурные графики показывают данные так, что каждые два соседних контура равноудалены по отношению к 3-му (скрытому) измерению, верно? Поэтому я подумал, что могу использовать n-й контур, чтобы сказать, что внутри этого контура плотность больше x, поэтому площадь плотности больше x равна площади этого контура. И все, что находится за пределами этого контура, можно рассматривать как своего рода выброс, поскольку маловероятно, что оно произойдет вне контура.   -  person jakes    schedule 22.08.2019


Ответы (1)


если вы не возражаете против использования конечного приближения к этому (это, вероятно, лучше, чем получение данных контура из matplotlib), вы можете сделать что-то вроде:

import numpy as np
import scipy.stats as sps

# estimate kernel density of data
kde = sps.gaussian_kde(ex.values.T)

# get a regular grid of points over our region of interest
xx, yy = np.meshgrid(
    np.linspace(0, 130, 500),
    np.linspace(0, 70, 500))

# calculate probability density on these points
z = kde.pdf([xx.ravel(), yy.ravel()]).reshape(xx.shape)

# note, the above calls are identical to how seaborn does things

# proportion of points above the 30%, i.e. approx the third contour line
zi = z > np.max(z) * 0.3

# print some summaries
print('x = (%.1f, %.1f)' % (min(xx[zi]), max(xx[zi])))
print('y = (%.1f, %.1f)' % (min(yy[zi]), max(yy[zi])))
print('area = %.1f' % (130 * 70 * np.mean(zi)))

что дает мне:

x = (57.1, 124.3)
y = (3.4, 59.2)
area = 2154.8

Вы можете увидеть, что в настоящее время делает seaborn, выполнив поиск «bivariate» в дистрибутивах . py (например, функции _bivariate_kdeplot и _scipy_bivariate_kde). причина, по которой я говорю, что это «лучше», чем получение данных о контурах из matplotlib, заключается в том, что я в основном делаю то же самое, только с сеткой с более высоким разрешением (приведенные выше выборки в 25 раз больше).

резюме должно быть в основном очевидным, за исключением, возможно, «области», которая похожа на оценка Монте-Карло (эта страница анимирует демо, вычисляющее число пи), за исключением того, что мы делаем выборку на обычной сетке, поэтому ошибка будет намного меньше.

приведенные выше оценки будут приблизительными, x будет точным с точностью до 130 / 500 = 0.26, а y равно 0.14. площадь имеет точность почти до 4 значащих цифр; Я получаю стандартное отклонение 0,30 и 95% ДИ (2154,3, 2155,4). если вы выясните, как рассчитать это, используя контуры из matplotlib, было бы здорово, если бы вы опубликовали его, чтобы я мог сравнить и увидеть, что на самом деле «лучше».

person Sam Mason    schedule 21.08.2019
comment
Спасибо, но я действительно не понимаю, что вы здесь сделали. Не могли бы вы уточнить свой ответ? И, прежде всего, почему вы думаете, что это лучше, чем получение точного контура из matplotlib? - person jakes; 22.08.2019
comment
@jakes matplotlib не получает точных контуров, они рассчитываются из конечного набора выборок плотности, поэтому будут иметь связанные ошибки ... алгоритм, который он использует, должен интерполировать между выборками, поэтому он потенциально более точен, но это было бы интересно на самом деле увидеть - person Sam Mason; 22.08.2019