46 votes

classement d'un cadre de données 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)})

donde id est un identifiant pour chaque point constitué d'un a y b comment puis-je bin a y b dans un ensemble spécifié de cases (afin que je puisse ensuite prendre la valeur médiane/moyenne de a y b dans chaque bac) ? df pourrait avoir NaN valeurs pour a o b (ou les deux) pour une ligne donnée dans df .

Voici un meilleur exemple qui utilise la solution de Joe Kington avec un modèle plus réaliste. df . Ce que je ne sais pas, c'est comment accéder à la base de données de l'entreprise. df.b éléments pour chaque df.a 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?
# ...

63voto

Joe Kington Points 68089

Il y a peut-être un moyen plus efficace (j'ai le sentiment que pandas.crosstab serait utile ici), mais voici comment je m'y prendrais :

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 le moyen de b binées par les valeurs dans a Fais-le, c'est tout.

groups.mean().b

De même, si vous voulez que l'index soit plus joli (par exemple, afficher les intervalles comme l'index), comme dans l'exemple de @bdiamante, utilisez pandas.cut au lieu de numpy.digitize . (Bravo à Bidamante. Je n'avais pas réalisé pandas.cut existé.)

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

Cela se traduit par :

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

27voto

bdiamante Points 6542

Je ne suis pas sûr à 100% que ce soit ce que vous recherchez, mais voici ce que je pense que vous cherchez :

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 sont les bacs que vous aviez à l'esprit, vous devrez donc adapter les miens à votre situation.

16voto

Perk Points 161

La réponse de Joe Kington a été très utile, mais j'ai remarqué qu'elle ne mettait pas toutes les données en mémoire. En fait, elle laisse de côté la ligne avec a = a.min(). En résumé groups.size() a donné 99 au lieu de 100.

Pour garantir que toutes les données sont binées, il suffit de passer le nombre de bins à cut() et cette fonction remplira automatiquement le premier [dernier] bin 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() donne 100.

Je sais que c'est un point délicat 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.

2voto

bio Points 139

Si vous ne devez pas vous en tenir à 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))

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X