Подтвердить что ты не робот

Бинация фрейма данных в pandas в Python

задан следующий фрейм данных в pandas:

import numpy as np
df = pandas.DataFrame({"a": np.random.random(100), "b": np.random.random(100), "id": np.arange(100)})

где id - это идентификатор для каждой точки, состоящей из значений a и b, как я могу использовать bin a и b в заданный набор ящиков (чтобы затем я мог взять среднее/среднее значение a и b в каждом ящике)? df может иметь NaN значения для a или b (или обоих) для любой заданной строки в df. спасибо.

Вот лучший пример с использованием решения Джо Кингтона с более реалистичным df. Я не знаю, как получить доступ к элементам df.b для каждой группы df.a ниже:

a = np.random.random(20)
df = pandas.DataFrame({"a": a, "b": a + 10})
# bins for df.a
bins = np.linspace(0, 1, 10)
# bin df according to a
groups = df.groupby(np.digitize(df.a,bins))
# Get the mean of a in each group
print groups.mean()
## But how to get the mean of b for each group of a?
# ...
4b9b3361

Ответ 1

Может быть более эффективный способ (у меня есть ощущение pandas.crosstab было бы полезно здесь), но вот как я это сделаю:

import numpy as np
import pandas

df = pandas.DataFrame({"a": np.random.random(100),
                       "b": np.random.random(100),
                       "id": np.arange(100)})

# Bin the data frame by "a" with 10 bins...
bins = np.linspace(df.a.min(), df.a.max(), 10)
groups = df.groupby(np.digitize(df.a, bins))

# Get the mean of each bin:
print groups.mean() # Also could do "groups.aggregate(np.mean)"

# Similarly, the median:
print groups.median()

# Apply some arbitrary function to aggregate binned data
print groups.aggregate(lambda x: np.mean(x[x > 0.5]))

Изменить: поскольку ОП запрашивал конкретно только средства b, закодированные значениями в a, просто выполните

groups.mean().b

Также, если вы хотите, чтобы индекс выглядел лучше (например, интервалы отображения как индекс), как в примере @bdiamante, используйте pandas.cut вместо numpy.digitize. (Kudos to bidamante. Я не понимал, что существует pandas.cut.)

import numpy as np
import pandas

df = pandas.DataFrame({"a": np.random.random(100), 
                       "b": np.random.random(100) + 10})

# Bin the data frame by "a" with 10 bins...
bins = np.linspace(df.a.min(), df.a.max(), 10)
groups = df.groupby(pandas.cut(df.a, bins))

# Get the mean of b, binned by the values in a
print groups.mean().b

Это приводит к:

a
(0.00186, 0.111]    10.421839
(0.111, 0.22]       10.427540
(0.22, 0.33]        10.538932
(0.33, 0.439]       10.445085
(0.439, 0.548]      10.313612
(0.548, 0.658]      10.319387
(0.658, 0.767]      10.367444
(0.767, 0.876]      10.469655
(0.876, 0.986]      10.571008
Name: b

Ответ 2

Не на 100% уверен, что это то, что вы ищете, но вот что я думаю, что вы получаете:

In [144]: df = DataFrame({"a": np.random.random(100), "b": np.random.random(100), "id":   np.arange(100)})

In [145]: bins = [0, .25, .5, .75, 1]

In [146]: a_bins = df.a.groupby(cut(df.a,bins))

In [147]: b_bins = df.b.groupby(cut(df.b,bins))

In [148]: a_bins.agg([mean,median])
Out[148]:
                 mean    median
a
(0, 0.25]    0.124173  0.114613
(0.25, 0.5]  0.367703  0.358866
(0.5, 0.75]  0.624251  0.626730
(0.75, 1]    0.875395  0.869843

In [149]: b_bins.agg([mean,median])
Out[149]:
                 mean    median
b
(0, 0.25]    0.147936  0.166900
(0.25, 0.5]  0.394918  0.386729
(0.5, 0.75]  0.636111  0.655247
(0.75, 1]    0.851227  0.838805

Конечно, я не знаю, какие бункеры вы имели в виду, поэтому вам придется поменять местами свои обстоятельства.

Ответ 3

Ответ Джо Кингтона был очень полезен, однако я заметил, что он не заполняет все данные. Он фактически оставляет строку с a = a.min(). Подведение итогов groups.size() дало 99 вместо 100.

Чтобы гарантировать, что все данные закодированы, просто передайте количество бункеров для cut(), и эта функция автоматически поместит первый [последний] бит на 0,1%, чтобы обеспечить включение всех данных.

df = pandas.DataFrame({"a": np.random.random(100), 
                    "b": np.random.random(100) + 10})

# Bin the data frame by "a" with 10 bins...
groups = df.groupby(pandas.cut(df.a, 10))

# Get the mean of b, binned by the values in a
print(groups.mean().b)

В этом случае суммирование групп .size() дало 100.

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

Ответ 4

Если вам не нужно придерживаться группировки pandas, вы можете использовать scipy.stats.binned_statistic:

from scipy.stats import binned_statistic

means = binned_statistic(df.a, df.b, bins=np.linspace(min(df.a), max(df.a), 10))