2013-06-05 28 views
34

proposta la seguente dataframe in panda:binning un dataframe in panda in Python

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

dove id è un ID per ciascun punto costituito da un valore a e b, come posso bin a e b in un determinato set di contenitori (in modo che io possa quindi prendere il valore medio/medio di a e b in ciascun cestino)? df potrebbe avere valori NaN per a o b (o entrambi) per qualsiasi riga specificata in df. Grazie.

Ecco un esempio migliore utilizzando la soluzione di Joe Kington con un df più realistico. La cosa di cui sono sicuro è su come accedere agli elementi df.b per ogni gruppo df.a di seguito:

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? 
# ... 

risposta

47

Ci può essere un modo più efficiente (ho la sensazione pandas.crosstab sarebbe utile qui), ma ecco come lo farei:

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

Edit: come il PO è stato chiesto specificamente solo per i mezzi di b cestinate dai valori in a, basta fare

groups.mean().b 

Inoltre, se si desidera che l'indice abbia un aspetto più gradevole (ad es. visualizzare gli intervalli come l'indice), come fanno nell'esempio di @ bdiamante, utilizzare pandas.cut anziché numpy.digitize. (Complimenti a bidamante Non mi rendevo conto pandas.cut esisteva..)

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 

Questo si traduce in:

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

eccellente ed elegante! esattamente quello che stavo cercando. non richiede affatto l'ordinamento del dataframe. – user248237dfsf

+0

E se volessi accedere ai valori di 'b' in base ai gruppi? 'groups.mean()' ti dà i mezzi per solo 'a', credo. – user248237dfsf

+0

@ user248237dfsf - No, fornisce la media per 'a' e' b' (o meglio, fornisce la media di 'b' decodificata dai valori in' a', che è quello che pensavo stavi chiedendo) . –

20

non al 100% sicuro se questo è quello che stai cercando, ma ecco quello che pensate che stai ricevendo in:

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 

Naturalmente, io non so cosa bidoni che aveva in mente, quindi dovrete scambiare il mio per il vostro circostanza.

+0

Bello! Supponevo che l'OP volesse bin "b" di "a", ma in retrospettiva, la tua risposta è probabilmente ciò che stavano cercando. Lascerò il mio, poiché le nostre risposte fanno cose leggermente diverse. –

+0

Forse vale la pena menzionare che si tratta di 'pandas.Dataframe ({..})' e 'a_bins.agg ([numpy.mean, numpy.median])' – Guido

13

La risposta di Joe Kington è stata molto utile, tuttavia, ho notato che non raccoglie tutti i dati. In realtà lascia fuori la riga con a = a.min(). Riassumendo groups.size() ha dato 99 anziché 100.

Per garantire che tutti i dati siano bloccati, basta inserire il numero di contenitori da tagliare() e tale funzione riempirà automaticamente il primo [ultimo] cestino dello 0,1% per garantire che tutto i dati sono inclusi

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) 

In questo caso, riassumendo groups.size() ha dato 100.

So che questo è un punto molto esigente per questo particolare problema, ma per un problema simile stavo cercando di risolvere, era fondamentale per ottenere la risposta corretta.

0

Se non si dispone di attenersi a pandas raggruppamento, è possibile utilizzare 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))