Интегрируйте двумерную оценку плотности ядра

У меня есть x,y распределение баллов, за которое я получаю KDE через scipy.stats.gaussian_kde. Это мой код и то, как выглядит вывод (данные x,y можно получить из здесь):

import numpy as np
from scipy import stats

# Obtain data from file.
data = np.loadtxt('data.dat', unpack=True)
m1, m2 = data[0], data[1]
xmin, xmax = min(m1), max(m1)
ymin, ymax = min(m2), max(m2)

# Perform a kernel density estimate (KDE) on the data
x, y = np.mgrid[xmin:xmax:100j, ymin:ymax:100j]
positions = np.vstack([x.ravel(), y.ravel()])
values = np.vstack([m1, m2])
kernel = stats.gaussian_kde(values)
f = np.reshape(kernel(positions).T, x.shape)

# Define the number that will determine the integration limits
x1, y1 = 2.5, 1.5

# Perform integration?

# Plot the results:
import matplotlib.pyplot as plt
# Set limits
plt.xlim(xmin,xmax)
plt.ylim(ymin,ymax)
# KDE density plot
plt.imshow(np.rot90(f), cmap=plt.cm.gist_earth_r, extent=[xmin, xmax, ymin, ymax])
# Draw contour lines
cset = plt.contour(x,y,f)
plt.clabel(cset, inline=1, fontsize=10)
plt.colorbar()
# Plot point
plt.scatter(x1, y1, c='r', s=35)
plt.show()

результат

Красная точка с координатами (x1, y1) имеет (как и любая точка на 2D-графике) связанное значение, заданное f (ядро или KDE) от 0 до 0,42. Допустим, что f(x1, y1) = 0.08.

Мне нужно интегрировать f с пределами интегрирования в x и y, заданными теми регионами, где f оценивается как меньше, чем f(x1, y1), то есть: f(x, y)<0.08.

Из того, что я видел, python может выполнять интеграцию функций и одномерных массивов посредством численного интегрирования, но я не видел ничего, что позволило бы мне выполнять численное интегрирование для 2D-массива (ядро f ) Кроме того, я не уверен, как бы я вообще распознал регионы, заданные этим конкретным условием (то есть: f(x, y)меньше, чем заданное значение)

Это вообще можно сделать?


person Gabriel    schedule 23.07.2013    source источник


Ответы (3)


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

#Compute the point below which to integrate
iso = kernel((x1,y1))

#Sample from your KDE distribution
sample = kernel.resample(size=10000)

#Filter the sample
insample = kernel(sample) < iso

#The integral you want is equivalent to the probability of drawing a point 
#that gets through the filter
integral = insample.sum() / float(insample.shape[0])
print integral

Я получаю примерно 0,2 в качестве ответа для вашего набора данных.

person jcrudy    schedule 24.07.2013
comment
Удивительно просто, мне явно нужно прочитать еще немного статистики. Большое спасибо! - person Gabriel; 25.07.2013
comment
Остерегайтесь, эта реализация Монте-Карло, вероятно, неверна. См. здесь: stackoverflow.com/a/35903712/1391441 - person Gabriel; 10.03.2016
comment
@Gabriel Я думаю, что это решение действительно правильно для этого вопроса. Я посмотрел на другой вопрос, на который вы ссылались. Вот мои мысли. Здесь смешиваются две разные границы интегрирования. В этом вопросе вы довольно четко заявляете, что хотите интегрировать набор x, y, где f (x, y) ‹ f (x1, y1) (верно?). Это решение делает это. В другом вашем вопросе мне не ясно, хотите ли вы интегрировать по тому же набору, что и в этом вопросе, или по набору x, y, где x ‹ x1 и y ‹ y1. Если последнее, ответ dfb правильный. - person jcrudy; 10.03.2016
comment
Вы абсолютно правы, jcrudy, я не заметил, что пределы интеграции отличаются. И ваш ответ, и тот, что в другом вопросе, верны. Что на самом деле неверно, так это ответ cqcn1991 (ниже). Спасибо за комментарий! - person Gabriel; 10.03.2016

В настоящее время он доступен

kernel.integrate_box([-np.inf,-np.inf], [2.5,1.5])

person Pavel Prochazka    schedule 01.11.2019

Прямой путь к integrate

import matplotlib.pyplot as plt
import sklearn
from scipy import integrate
import numpy as np

mean = [0, 0]
cov = [[5, 0], [0, 10]]
x, y = np.random.multivariate_normal(mean, cov, 5000).T
plt.plot(x, y, 'o')
plt.show()

sample = np.array(zip(x, y))
kde = sklearn.neighbors.KernelDensity().fit(sample)
def f_kde(x,y):
    return np.exp((kde.score_samples([[x,y]])))

point = x1, y1
integrate.nquad(f_kde, [[-np.inf, x1],[-np.inf, y1]])

Проблема в том, что это очень медленно, если вы делаете это в больших масштабах. Например, если вы хотите построить линию x,y в точке x (0,100), расчет займет много времени.

Примечание: я использовал kde из sklearn, но я считаю, что вы также можете изменить его на другую форму.


Используя kernel, как определено в исходном вопросе:

import numpy as np
from scipy import stats
from scipy import integrate

def integ_func(kde, x1, y1):

    def f_kde(x, y):
        return kde((x, y))

    integ = integrate.nquad(f_kde, [[-np.inf, x1], [-np.inf, y1]])

    return integ

# Obtain data from file.
data = np.loadtxt('data.dat', unpack=True)
# Perform a kernel density estimate (KDE) on the data
kernel = stats.gaussian_kde(data)

# Define the number that will determine the integration limits
x1, y1 = 2.5, 1.5
print integ_func(kernel, x1, y1)
person cqcn1991    schedule 23.02.2016
comment
cqcn1991 Я не могу заставить этот пример работать. Не могли бы вы расширить свой код, чтобы он мог работать? - person Gabriel; 23.02.2016
comment
@Gabriel Я меняю его полным примером, но пропускаю некоторые import. import на питоне для меня просто катастрофа. - person cqcn1991; 24.02.2016
comment
KernelDensity не импортируется правильно, вы должны использовать: from sklearn.neighbors import KernelDensity. Как вы определяете inf? Я получаю NameError: name 'inf' is not defined с вашим кодом. Кроме того, где точка, которую следует использовать в качестве предела интегрирования (x1,y1)? - person Gabriel; 24.02.2016
comment
Только что обновил импорт inf перед вашим комментарием, а также добавил x1, y1 - person cqcn1991; 24.02.2016
comment
Да, вы можете просто использовать kernel, как определено в моем вопросе, вместо использования sklearn.neighbors.KernelDensity(). - person Gabriel; 24.02.2016
comment
@Gabriel Кроме того, я не думаю, что вам следует использовать его как nquad вместо integrate.nquad. Это делает код менее выразительным. nquad используется, чтобы сказать, что это интеграция n-d вместо 1d интеграции. - person cqcn1991; 24.02.2016
comment
cqcn1991 см. раздел комментариев к ответу jcrudy (выше). Я не заметил, что в вашем ответе используются разные пределы интеграции. Исходный вопрос просит интегрировать в домен f(x,y)<(f(x1,y1) не (-inf,x1) & (-inf,x1). - person Gabriel; 10.03.2016
comment
@ Габриэль, моя ошибка, я совершенно неправильно понял вопрос. Ответ, который я дал здесь, вообще не имеет отношения к вашему вопросу. Мне жаль. - person cqcn1991; 11.03.2016
comment
Все в порядке, это познакомило меня с nquad, с которым я не был знаком. Это также вызвало этот новый вопрос, который превратился в интересное обсуждение. - person Gabriel; 11.03.2016