é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)})
où 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?
# ...
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
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.
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.
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))