2013-03-13 4 views
18

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

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

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

ответ

21

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 object.

6

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 
+1

Вы также можете использовать 'гисто = gg.size()' для простоты – Reservedegotist

1

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

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 
0

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

Для примера:

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 

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

%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 

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

%timeit df['my_label'].value_counts(sort=False)/df.shape[0] 
1000 loops, best of 3: 770 µs per loop 
Смежные вопросы