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

Применить функцию к pandas groupby

У меня есть фреймворк pandas с столбцом с именем my_labels, который содержит строки: 'A', 'B', 'C', 'D', 'E'. Я хотел бы подсчитать количество событий каждой из этих строк, затем разделить количество отсчетов на сумму всех счетчиков. Я пытаюсь сделать это в pandas следующим образом:

func = lambda x: x.size() / x.sum()
data = frame.groupby('my_labels').apply(func)

Этот код выдает ошибку: "Объект DataFrame не имеет атрибута" размер ". Как я могу применить функцию для вычисления этого значения в Pandas?

4b9b3361

Ответ 1

apply принимает функцию, применяемую к каждому значению, а не к серии, и принимает kwargs. Таким образом, значения не имеют метода .size().

Возможно, это сработает:

from pandas import *

d = {"my_label": Series(['A','B','A','C','D','D','E'])}
df = DataFrame(d)


def as_perc(value, total):
    return value/float(total)

def get_count(values):
    return len(values)

grouped_count = df.groupby("my_label").my_label.agg(get_count)
data = grouped_count.apply(as_perc, total=df.my_label.count())

Здесь метод .agg() принимает функцию, которая применяется ко всем значениям объекта groupby.

Ответ 2

Try:

g = pd.DataFrame(['A','B','A','C','D','D','E'])

# Group by the contents of column 0 
gg = g.groupby(0)  

# Create a DataFrame with the counts of each letter
histo = gg.apply(lambda x: x.count())

# Add a new column that is the count / total number of elements    
histo[1] = histo.astype(np.float)/len(g) 

print histo

Вывод:

   0         1
0             
A  2  0.285714
B  1  0.142857
C  1  0.142857
D  2  0.285714
E  1  0.142857

Ответ 3

Я увидел метод вложенных функций для вычисления средневзвешенного значения на S.O. один раз, изменяя, что техника может решить вашу проблему.

def group_weight(overall_size):
    def inner(group):
        return len(group)/float(overall_size)
    inner.__name__ = 'weight'
    return inner

d = {"my_label": pd.Series(['A','B','A','C','D','D','E'])}
df = pd.DataFrame(d)
print df.groupby('my_label').apply(group_weight(len(df)))



my_label
A    0.285714
B    0.142857
C    0.142857
D    0.285714
E    0.142857
dtype: float64

Вот как сделать средневзвешенное значение внутри групп

def wavg(val_col_name,wt_col_name):
    def inner(group):
        return (group[val_col_name] * group[wt_col_name]).sum() / group[wt_col_name].sum()
    inner.__name__ = 'wgt_avg'
    return inner



d = {"P": pd.Series(['A','B','A','C','D','D','E'])
     ,"Q": pd.Series([1,2,3,4,5,6,7])
    ,"R": pd.Series([0.1,0.2,0.3,0.4,0.5,0.6,0.7])
     }

df = pd.DataFrame(d)
print df.groupby('P').apply(wavg('Q','R'))

P
A    2.500000
B    2.000000
C    4.000000
D    5.545455
E    7.000000
dtype: float64

Ответ 4

Начиная с Pandas версии 0.22, существует также альтернатива apply: pipe, что может быть значительно быстрее, чем при использовании apply (вы также можете проверить этот вопрос для большего различия между две функциональности).

В вашем примере:

df = pd.DataFrame({"my_label": ['A','B','A','C','D','D','E']})

  my_label
0        A
1        B
2        A
3        C
4        D
5        D
6        E

Версия apply

df.groupby('my_label').apply(lambda grp: grp.count() / df.shape[0])

дает

          my_label
my_label          
A         0.285714
B         0.142857
C         0.142857
D         0.285714
E         0.142857

и pipe версия

df.groupby('my_label').pipe(lambda grp: grp.size() / grp.size().sum())

дает

my_label
A    0.285714
B    0.142857
C    0.142857
D    0.285714
E    0.142857

Значения идентичны, однако тайминги сильно различаются (по крайней мере, для этого небольшого блока данных):

%timeit df.groupby('my_label').apply(lambda grp: grp.count() / df.shape[0])
100 loops, best of 3: 5.52 ms per loop

и

%timeit df.groupby('my_label').pipe(lambda grp: grp.size() / grp.size().sum())
1000 loops, best of 3: 843 µs per loop

Включение этой функции в функцию также является простым:

def get_perc(grp_obj):
    gr_size = grp_obj.size()
    return gr_size / gr_size.sum()

Теперь вы можете позвонить

df.groupby('my_label').pipe(get_perc)

получая

my_label
A    0.285714
B    0.142857
C    0.142857
D    0.285714
E    0.142857

Однако для этого конкретного случая вам даже не нужен groupby, но вы можете просто использовать value_counts следующим образом:

df['my_label'].value_counts(sort=False) / df.shape[0]

получая

A    0.285714
C    0.142857
B    0.142857
E    0.142857
D    0.285714
Name: my_label, dtype: float64

Для этого небольшого блока данных он довольно быстро

%timeit df['my_label'].value_counts(sort=False) / df.shape[0]
1000 loops, best of 3: 770 µs per loop