Как выполнить векторизацию биннинга, используя только панд

1

Я пытаюсь найти правильный синтаксис, чтобы выбрать срез строк в Pandas DataFrame, обусловленный многомерным срезом.

Я хочу выполнить бинонирование гистограмм, предоставив буферы в многомерном массиве numpy и сравнивая в любом случае, подходит ли запись в одном бункере или другом. Результат должен быть 1-мерным массивом numpy с количеством элементов в каждом бункере.

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

import numpy as np
import pandas as pd

## Generate Random Data
X = np.random.normal(0.5,0.1,100)

## Populate a Pandas DataFrame
DF = pd.DataFrame({'x':X})

## Some example, hardcoded 1D bins. 
bins = np.array([
                [[0.0,0.2]],
                [[0.2,0.4]],
                [[0.4,0.6]],
                [[0.6,0.8]],
                [[0.8,1.0]]
                ])

hist = np.zeros(shape=(4,))
hist[:] = np.sum(
                 DF.loc[   (DF >= bins[:,:,0]) &
                           (DF > bins[:,:,1])
                        ].dropna(how='all')
                 )

В общем случае данные являются n-мерными, а бункеры следуют приведенному выше шаблону, причем:

[[x_min, x_max], [y_min, ymax], [z_min, z_max]] 

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

DF['x'] >= x_min and DF['x'] < x_max and 
DF['y'] >= y_min and DF['y'] < y_max

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

Если нет, в моем ответе можно было бы подходить к пониманию списка, но у меня были проблемы с многомерностью.

  • 0
    Ваш код не запускается, возможно потому, что bins[:][0] и bins[:][1] все еще являются массивами. Тем не менее, убедитесь, что вы выполняете код или добавляете код с ошибками, только прямо объяснив, в чем ваша проблема.
  • 0
    Спасибо; Я знаю, что код не запускается - это своего рода проблема! Я не могу понять, как написать эту строку (начинающуюся с hist[:] = ) так, чтобы я правильно заполнил этот объект. Буду признателен за предложения о том, как правильно написать этот фрагмент, или, если это невозможно, объяснение, почему или как сделать это по-другому.
Теги:
pandas
vectorization
histogram
slice

2 ответа

0

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

Я представляю приведенный ниже код, пример выше расширенный до 2D.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.patches as patches

## Generate Random Data
X = np.random.normal(0.5,0.1,150)
Y = np.random.normal(0.5,0.2,150)

## Populate a Pandas DataFrame
DF = pd.DataFrame({'x':X,'y':Y})

## Some example, hardcoded 2D bins. 
bins = np.array([
            [[0.0,0.2],[0.0,1.5]],
            [[0.2,0.4],[0.0,1.5]],
            [[0.4,0.6],[0.0,1.5]],
            [[0.6,0.8],[0.0,1.5]],
            [[0.8,1.0],[0.0,1.5]]
            ])


hist = np.array([  
                np.product(  
                          np.sum(     (DF.iloc[:,:] >= bins[:,:,0][i][:]) & 
                                      (DF.iloc[:,:] <  bins[:,:,1][i][:])
                          ))/len(DF) 
                 for i in range(len(bins)) ], dtype=np.int32)[:,0]


print(hist)    
print(sum(hist))

## 2D Plot
plt.style.use('seaborn')
fig, axes = plt.subplots(figsize=(4, 3.5))
plt.scatter(DF['x'],DF['y'], 5, 'k')
axes.set_xlabel('x')
axes.set_xlabel('y')
axes.set_xlim(-0.5,1.5)
axes.set_ylim(-0.5,2)

# Create a Rectangle patch for each bin and plot
for i,bin in enumerate(bins):

    rect = patches.Rectangle(   (bin[0][0],bin[1][0]),
                                bin[0][1]-bin[0][0],
                                bin[1][1]-bin[1][0],
                                linewidth=1,
                                edgecolor='r',facecolor='none')
    # Add the patch to the Axes
    axes.add_patch(rect)

plt.show()

Это часть личного проекта для повторного создания N-мерных гистограмм в Python со ссылкой на обсуждение в этом вопросе SciComp.

0

Я не уверен, действительно ли вам нужны панды, но numpy имеет многомерную функцию histogramdd называемую histogramdd.

Вот тестовый цикл, который генерирует три массива с увеличением количества столбцов, всего 100 строк и соответствующих массивов бункеров, все с вашими границами образцов сверху.

Просто посмотрите, если это то, что вы искали:

for i in range(1, 4):
    data = np.random.random([100, i])
    bins = np.linspace(0, 1, 6)
    bins = [bins for _ in range(i)]
    print('shape of data: ', np.shape(data))
    print('bin borders: ',bins)
    print('\nresult: ', np.histogramdd(data, bins), '\n\n')

результат:

shape of data:  (100, 1)
bin borders:  [array([ 0. ,  0.2,  0.4,  0.6,  0.8,  1. ])]

result:  (array([ 14.,  26.,  21.,  24.,  15.]), [array([ 0. ,  0.2,  0.4,  0.6,  0.8,  1. ])]) 


shape of data:  (100, 2)
bin borders:  [array([ 0. ,  0.2,  0.4,  0.6,  0.8,  1. ]), array([ 0. ,  0.2,  0.4,  0.6,  0.8,  1. ])]

result:  (array([[  5.,   7.,   5.,   2.,   3.],
       [  5.,   4.,   5.,   3.,   1.],
       [  5.,   3.,   7.,   1.,   3.],
       [  2.,   6.,   4.,   3.,   7.],
       [  1.,  11.,   3.,   2.,   2.]]), [array([ 0. ,  0.2,  0.4,  0.6,  0.8,  1. ]), array([ 0. ,  0.2,  0.4,  0.6,  0.8,  1. ])]) 


shape of data:  (100, 3)
bin borders:  [array([ 0. ,  0.2,  0.4,  0.6,  0.8,  1. ]), array([ 0. ,  0.2,  0.4,  0.6,  0.8,  1. ]), array([ 0. ,  0.2,  0.4,  0.6,  0.8,  1. ])]

result:  (array([[[ 1.,  0.,  0.,  0.,  2.],
        [ 0.,  1.,  1.,  1.,  0.],
        [ 0.,  1.,  1.,  2.,  1.],
        [ 2.,  2.,  0.,  2.,  0.],
        [ 1.,  1.,  1.,  2.,  1.]],

       [[ 2.,  0.,  1.,  1.,  1.],
        [ 0.,  0.,  0.,  1.,  0.],
        [ 1.,  2.,  2.,  0.,  1.],
        [ 0.,  1.,  1.,  2.,  0.],
        [ 0.,  0.,  1.,  1.,  0.]],

       [[ 1.,  0.,  0.,  0.,  1.],
        [ 1.,  0.,  2.,  0.,  4.],
        [ 0.,  1.,  0.,  1.,  1.],
        [ 2.,  0.,  0.,  0.,  0.],
        [ 1.,  1.,  0.,  1.,  0.]],

       [[ 1.,  2.,  1.,  1.,  0.],
        [ 0.,  1.,  1.,  0.,  2.],
        [ 2.,  1.,  1.,  0.,  1.],
        [ 2.,  0.,  1.,  1.,  0.],
        [ 0.,  2.,  0.,  2.,  1.]],

       [[ 1.,  3.,  0.,  1.,  0.],
        [ 1.,  1.,  0.,  0.,  0.],
        [ 1.,  1.,  0.,  0.,  0.],
        [ 1.,  1.,  2.,  1.,  1.],
        [ 1.,  1.,  1.,  0.,  1.]]]), [array([ 0. ,  0.2,  0.4,  0.6,  0.8,  1. ]), array([ 0. ,  0.2,  0.4,  0.6,  0.8,  1. ]), array([ 0. ,  0.2,  0.4,  0.6,  0.8,  1. ])])
  • 0
    Спасибо - я знаком с собственными положениями гистограммы numpy и их ограничениями и на самом деле пытаюсь «заменить» их более общей формулировкой. Теперь у меня есть частичное решение проблемы, как указано, поэтому я опубликую это как ответ; но он не векторизован и кажется неоптимальным, поэтому я буду ждать превосходных подходов - надеюсь, используя мой ответ для большего вдохновения.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню