2013-03-13 16 views
18

Ho un dataframe panda con una colonna denominata my_labels che contiene stringhe: 'A', 'B', 'C', 'D', 'E'. Vorrei contare il numero di occorrenze di ciascuna di queste stringhe quindi dividere il numero di conteggi per la somma di tutti i conteggi. Sto cercando di fare questo in Pandas in questo modo:Applicare la funzione al gruppo panda da

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

Questo codice genera un errore, 'oggetto dataframe non ha alcun attributo 'size'. Come posso applicare una funzione per calcolare questo in Panda?

risposta

21

apply accetta una funzione da applicare a ogni valore, non alla serie e accetta kwargs. Quindi, i valori non hanno il metodo .size().

Forse funzionerebbe:

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()) 

Il .agg() metodo qui prende una funzione che viene applicata a tutti valori della groupby object.

6

Prova:

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 

uscita:

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

È inoltre possibile utilizzare 'isto = gg.size()' per semplicità – Reservedegotist

1

ho visto una tecnica funzione annidata per calcolare una media ponderata su S.O. una volta, modificando questa tecnica puoi risolvere il tuo problema.

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 

Ecco come fare una media ponderata all'interno dei gruppi

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, esiste anche un'alternativa alla apply: pipe, che può essere notevolmente più veloce rispetto all'utilizzo di apply (è possibile anche controllare this question per maggiori differenze tra le due funzionalità).

Per esempio:

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 

La versione 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 

e la versione pipe

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

cede

my_label 
A 0.285714 
B 0.142857 
C 0.142857 
D 0.285714 
E 0.142857 

Così i valori sono identici, tuttavia, i tempi differiscono parecchio (almeno per questo piccolo dataframe):

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

e

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

Anche il suo inserimento in una funzione è semplice:

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

Ora è possibile chiamare

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

cedendo

my_label 
A 0.285714 
B 0.142857 
C 0.142857 
D 0.285714 
E 0.142857 

Tuttavia, per questo caso particolare, non avrete nemmeno bisogno di un groupby, ma si può semplicemente utilizzare value_counts come questo:

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

resa

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

Per questo piccolo dataframe si è abbastanza veloce

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