binning une dataframe dans pandas en Python
étant donné le cadre de données suivant dans 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 id pour chaque point consistant en une valeur a
et b
, Comment puis-je bin a
et b
dans un ensemble spécifié de bacs (de sorte que je puisse alors prendre la valeur médiane/moyenne de a
et b
dans chaque bacs)? df
peut avoir NaN
pour a
ou b
(ou les deux) pour une rangée donnée dans df
. grâce.
voici un meilleur exemple d'utilisation de la solution de Joe Kington avec un df plus réaliste. Ce que je ne sais pas, c'est comment accéder au df.B éléments pour chaque FD.un groupe 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?
# ...
4 réponses
il y a peut-être un moyen plus efficace (j'ai le sentiment que pandas.crosstab
serait utile ici), Mais voici comment je le ferais:
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 pour juste le moyen de b
binned par les valeurs dans a
, just do
groups.mean().b
aussi si vous voulez que l'index paraisse plus joli (par exemple des intervalles d'affichage comme l'index), comme dans l'exemple de @bdiamante, utilisez pandas.cut
au lieu de numpy.digitize
. (Bravo à bidamante. Je ne savais pas que 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
pas sûr à 100% Si c'est ce que vous cherchez, mais voici ce que je pense que vous obtenez:
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 ce que vous aviez en tête, donc vous devrez échanger le mien contre votre situation.
la réponse de Joe Kington a été très utile, cependant, j'ai remarqué qu'elle ne bin pas toutes les données. Il laisse la ligne avec un = un.min(). En résumé, groups.size()
donne 99 au lieu de 100.
pour garantir que toutes les données sont stockées, il suffit de passer le nombre de bacs à couper() et cette fonction pad automatiquement le premier[dernier] bac de 0,1% pour s'assurer 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, en résumant les groupes.taille() 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 ne devez pas vous en tenir au groupe 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))