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

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?
# ...
37
demandé sur user248237dfsf 2013-06-05 22:33:51

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
53
répondu Joe Kington 2013-06-06 17:14:45

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.

21
répondu bdiamante 2013-06-05 20:42:58

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.

13
répondu Perk 2014-05-16 02:26:51

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))
1
répondu bio 2017-10-30 10:46:25