web-dev-qa-db-fra.com

regrouper une trame de données dans pandas dans Python

étant donné le cadre de données suivant dans les pandas:

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

id est un identifiant pour chaque point composé d'une valeur a et b, comment puis-je regrouper a et b dans un ensemble de bacs spécifié (pour que je puisse ensuite prendre la valeur médiane/moyenne de a et b dans chaque bac)? df peut avoir des valeurs NaN pour a ou b (ou les deux) pour une ligne donnée dans df. Merci.

Voici un meilleur exemple en utilisant la solution de Joe Kington avec un df plus réaliste. La chose dont je ne suis pas sûr est de savoir comment accéder aux éléments df.b pour chaque groupe df.a ci-dessous:

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

Il peut y avoir un moyen plus efficace (j'ai le sentiment pandas.crosstab serait utile ici), mais voici comment je procéderais:

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: comme l'OP demandait spécifiquement juste les moyens de b regroupés par les valeurs dans a, faites simplement

groups.mean().b

De plus, si vous souhaitez que l'index soit plus joli (par exemple, afficher les intervalles comme index), comme dans l'exemple de @ bdiamante, utilisez pandas.cut au lieu de numpy.digitize. (Bravo à bidamante. Je ne savais pas pandas.cut existait.)

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

Il en résulte:

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
58
Joe Kington

Je ne suis pas sûr à 100% si c'est ce que vous recherchez, mais voici ce que je pense que vous voulez dire:

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

Bien sûr, je ne sais pas quels bacs vous aviez en tête, vous devrez donc échanger le mien pour votre situation.

24
bdiamante

La réponse de Joe Kington a été très utile, cependant, j'ai remarqué qu'elle ne regroupe pas toutes les données. Il supprime en fait la ligne avec a = a.min (). La somme de groups.size() a donné 99 au lieu de 100.

Pour garantir que toutes les données sont regroupées, entrez simplement le nombre de cases à couper () et cette fonction remplira automatiquement la première [dernière] case de 0,1% pour garantir que toutes les données sont incluses.

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)

Dans ce cas, la somme de groups.size () a donné 100.

Je sais que c'est un point difficile pour ce problème particulier, mais pour un problème similaire que j'essayais de résoudre, il était crucial d'obtenir la bonne réponse.

14
Perk

Si vous n'avez pas à vous en tenir au regroupement pandas, vous pouvez utiliser 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))
2
bio