Как вы можете создать KDE только из значений гистограммы?

У меня есть набор значений, для которых я хотел бы построить оценку плотности ядра по Гауссу, однако у меня есть две проблемы:

  1. У меня есть только значения столбцов, а не сами значения
  2. Я рисую на категориальной оси

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

Я хотел бы добавить гауссовское наложение kde для каждого цвета, но до сих пор мне не удалось использовать seaborn или scipy для этого.

Вот код для вышеупомянутой сгруппированной гистограммы с использованием python и matplotlib:

enterN = len(color1_plotting_values)
fig, ax = plt.subplots(figsize=(20,30))
ind = np.arange(N)    # the x locations for the groups
width = .5         # the width of the bars
p1 = ax.barh(Species_Ordering.Species.values, color1_plotting_values, width, label='Color1', log=True)
p2 = ax.barh(Species_Ordering.Species.values, color2_plotting_values, width, label='Color2', log=True)
for b in p2:
    b.xy = (b.xy[0], b.xy[1]+width)

Спасибо!


person Joe B    schedule 17.12.2018    source источник
comment
Похоже, вы используете фрейм данных, пробовали ли вы встроенный функции построения графиков kde?   -  person G. Anderson    schedule 18.12.2018
comment
Да, я пробовал, но не знаю, как заставить его правильно интерпретировать категориальную ось. результирующий kde - это kde гистограммы данных. Однако данные уже представляют высоту столбцов гистограммы. Думайте о каждом виде бактерий как о корзине, а каждое число - как о количестве значений в этой корзине. Надеюсь, это поможет показать, как форматируются данные!   -  person Joe B    schedule 18.12.2018
comment
KDE обычно включает интеграцию по соседним точкам данных. Для категориальных данных, таких как ваш другой вид, нет критерия объективного расстояния (тем более того, который соблюдает неравенство треугольника). Следовательно, использование KDE здесь нежелательно и невозможно.   -  person Paul Brodersen    schedule 18.12.2018
comment
@PaulBrodersen, извините за вторжение, допустим, мы забываем, что данные являются категориальными, и смотрим на них как на гистограмму с равными ячейками, или, может быть, просто как функцию в однородно выбранном домене. Можно ли запустить KDE в таких условиях? Я имею в виду без доступа к самим образцам, только к бинированной гистограмме   -  person filippo    schedule 18.12.2018
comment
@PaulBrodersen Вы правы, конечно, в том, что KDE не идеален, но здесь он весьма полезен, поскольку мы ищем общие тенденции в столбцах «гистограммы». ось Y здесь построена из филогенетического дерева. Филогенетические деревья действительно имеют объективный критерий расстояния, который я не включил выше. Мы только сохранили порядок видов здесь, потому что расстояние может добавить информацию, которую нам не нужно представлять.   -  person Joe B    schedule 18.12.2018
comment
@filippo Да! это в основном то, что я хочу знать. Можем ли мы оценить гауссовский KDE (или другой KDE) без примеров?   -  person Joe B    schedule 18.12.2018
comment
Филогенетическое расстояние не является истинным расстоянием в математическом смысле, поскольку оно не удовлетворяет неравенство треугольника (пример здесь). Поэтому вы не можете применить здесь KDE (конечно, не на рисунке, который, как я полагаю, предназначен для академической публикации).   -  person Paul Brodersen    schedule 19.12.2018
comment
@filippo Вроде. В некотором смысле определение KDE по гистограмме аналогично KDE с использованием взвешенных выборок (что для большинства методов KDE является простым расширением). Проблема в том, что вы не знаете истинного положения точки внутри краев бункера. Поэтому, если ширина ядра равна или меньше ширины бункера, вы столкнетесь с проблемами (легко увидеть, если вы имитируете кучу точек на однородном интервале, примените алгоритм KDE по вашему выбору, а затем сравните результат с тем, когда вы округлите координаты точки до 1 значащей цифры). Тем не менее, широкие ядра подойдут.   -  person Paul Brodersen    schedule 19.12.2018
comment
@JoeB Существуют методы сглаживания, которые не зависят от ядра и, следовательно, не обязательно нуждаются в критерии расстояния, удовлетворяющем неравенству треугольника. На ум приходит оценка плотности k-ближайших соседей, взвешенная по обратному расстоянию. У меня есть реализация для общего случая здесь. Если сегодня у меня будет время, я могу придумать что-нибудь, что решит вашу проблему. Между тем, было бы полезно, если бы вы могли опубликовать данные, то есть значения бинов и матрицу филогенетических расстояний.   -  person Paul Brodersen    schedule 19.12.2018
comment
@PaulBrodersen Расслабьтесь. Взгляните на мой ответ. Не всегда есть интеграция.   -  person agcala    schedule 01.08.2019


Ответы (3)


Как построить KDE, начиная с гистограммы

Протокол оценки плотности ядра требует лежащих в основе данных. Вы могли бы придумать новый метод, который вместо этого использует эмпирический pdf (то есть гистограмму), но тогда это не будет дистрибутив KDE.

Однако не вся надежда потеряна. Вы можете получить хорошее приближение распределения KDE, сначала взяв образцы из гистограммы, а затем используя KDE для этих образцов. Вот полный рабочий пример:

import matplotlib.pyplot as plt
import numpy as np
import scipy.stats as sts

n = 100000

# generate some random multimodal histogram data
samples = np.concatenate([np.random.normal(np.random.randint(-8, 8), size=n)*np.random.uniform(.4, 2) for i in range(4)])
h,e = np.histogram(samples, bins=100, density=True)
x = np.linspace(e.min(), e.max())

# plot the histogram
plt.figure(figsize=(8,6))
plt.bar(e[:-1], h, width=np.diff(e), ec='k', align='edge', label='histogram')

# plot the real KDE
kde = sts.gaussian_kde(samples)
plt.plot(x, kde.pdf(x), c='C1', lw=8, label='KDE')

# resample the histogram and find the KDE.
resamples = np.random.choice((e[:-1] + e[1:])/2, size=n*5, p=h/h.sum())
rkde = sts.gaussian_kde(resamples)

# plot the KDE
plt.plot(x, rkde.pdf(x), '--', c='C3', lw=4, label='resampled KDE')
plt.title('n = %d' % n)
plt.legend()
plt.show()

Выход:

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

Красная пунктирная линия и оранжевая линия почти полностью перекрываются на графике, показывая, что реальные KDE и KDE, рассчитанные путем повторной выборки гистограммы, находятся в отличном согласии.

Если ваши гистограммы действительно зашумлены (например, то, что вы получаете, если вы устанавливаете n = 10 в приведенном выше коде), вам следует быть немного осторожнее при использовании передискретизированного KDE для чего-либо, кроме целей построения графика:

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

В целом соответствие между реальным и передискретизированным KDE по-прежнему хорошее, но отклонения заметны.

Внесите свои категориальные данные в соответствующую форму

Поскольку вы не опубликовали свои фактические данные, я не могу дать вам подробный совет. Я думаю, что лучше всего будет просто пронумеровать категории по порядку, а затем использовать это число в качестве значения «x» для каждого столбца гистограммы.

person tel    schedule 19.12.2018

Я высказал свои оговорки относительно применения KDE к категориальным данным OP в моих комментариях выше. По сути, поскольку филогенетическое расстояние между видами не подчиняется неравенству треугольника, не может быть действительного ядра, которое можно было бы использовать для оценки плотности ядра. Однако есть другие методы оценки плотности, которые не требуют построения ядра. Одним из таких методов является обратное взвешивание расстояния k-ближайшего соседа, который требует только неотрицательных расстояний, которые не обязательно должно удовлетворять неравенству треугольника (и даже не должно быть симметричным, я думаю). Ниже описывается этот подход:

import numpy as np

#--------------------------------------------------------------------------------
# simulate data

total_classes = 10
sample_values = np.random.rand(total_classes)
distance_matrix = 100 * np.random.rand(total_classes, total_classes)

# Distances to the values itself are zero; hence remove diagonal.
distance_matrix -= np.diag(np.diag(distance_matrix))

# --------------------------------------------------------------------------------
# For each sample, compute an average based on the values of the k-nearest neighbors.
# Weigh each sample value by the inverse of the corresponding distance.

# Apply a regularizer to the distance matrix.
# This limits the influence of values with very small distances.
# In particular, this affects how the value of the sample itself (which has distance 0)
# is weighted w.r.t. other values.
regularizer = 1.
distance_matrix += regularizer

# Set number of neighbours to "interpolate" over.
k = 3

# Compute average based on sample value itself and k neighbouring values weighted by the inverse distance.
# The following assumes that the value of distance_matrix[ii, jj] corresponds to the distance from ii to jj.
for ii in range(total_classes):

    # determine neighbours
    indices = np.argsort(distance_matrix[ii, :])[:k+1] # +1 to include the value of the sample itself

    # compute weights
    distances = distance_matrix[ii, indices]
    weights = 1. / distances
    weights /= np.sum(weights) # weights need to sum to 1

    # compute weighted average
    values = sample_values[indices]
    new_sample_values[ii] = np.sum(values * weights)

print(new_sample_values)
person Paul Brodersen    schedule 19.12.2018

ЛЕГКИЙ СПОСОБ

На данный момент я пропускаю какие-либо философские аргументы о правомерности использования плотности ядра в таких настройках. Об этом позже.

Простой способ сделать это - использовать scikit-learn KernelDensity:

import numpy as np
import pandas as pd
from sklearn.neighbors import KernelDensity
from sklearn import preprocessing

ds=pd.read_csv('data-by-State.csv')

Y=ds.loc[:,'State'].values # State is AL, AK, AZ, etc...

# With categorical data we need some label encoding here...
le = preprocessing.LabelEncoder()
le.fit(Y)                            # le.classes_ would be ['AL', 'AK', 'AZ',...
y=le.transform(Y)                    # y would be [0, 2, 3, ..., 6, 7, 9]
y=y[:, np.newaxis]                   # preparing for kde

kde = KernelDensity(kernel='gaussian', bandwidth=0.75).fit(y)

# You can control the bandwidth so the KDE function performs better
# To find the optimum bandwidth for your data you can try Crossvalidation

x=np.linspace(0,5,100)[:, np.newaxis] # let's get some x values to plot on
log_dens=kde.score_samples(x)
dens=np.exp(log_dens)            # these are the density function values

array([0.06625658, 0.06661817, 0.06676005, 0.06669403, 0.06643584,
       0.06600488, 0.0654239 , 0.06471854, 0.06391682, 0.06304861,
       0.06214499, 0.06123764, 0.06035818, 0.05953754, 0.05880534,
       0.05818931, 0.05771472, 0.05740393, 0.057276  , 0.05734634,
       0.05762648, 0.05812393, 0.05884214, 0.05978051, 0.06093455,
       ..............
       0.11885574, 0.11883695, 0.11881434, 0.11878766, 0.11875657,
       0.11872066, 0.11867943, 0.11863229, 0.11857859, 0.1185176 ,
       0.11844852, 0.11837051, 0.11828267, 0.11818407, 0.11807377])

И эти значения - все, что вам нужно для построения графика плотности ядра на гистограмме. Капитон?

Теперь, с теоретической точки зрения, если X - категориальная (*) неупорядоченная переменная с c возможными значениями, то для 0 ≤ h ‹1

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

- действующее ядро. Для упорядоченного X,

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

где |x1-x2| следует понимать как расстояние между x1 и x2 уровнями. Поскольку h стремится к нулю, оба они становятся индикаторами и возвращают подсчет относительной частоты. h часто называют пропускной способностью.


(*) Расстояние в пространстве переменных не требуется. Не обязательно должно быть метрическим пространством.

Devroye, Luc and Gábor Lugosi (2001). Combinatorial Methods in Density Estimation. Berlin: Springer-Verlag.

person agcala    schedule 29.07.2019