binning a dataframe in pandas in Python

Podano następujący dataframe w Pandzie:

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

Gdzie id jest identyfikatorem dla każdego punktu składającego się z wartości a i b, W Jaki Sposób mogę binować a i b do określonego zbioru pojemników (tak, że mogę następnie przyjąć medianę/średnią wartość a i b w każdym pojemniku)? df może mieć NaN wartości dla a lub b (lub obu) dla dowolnego wiersza w df. dzięki.

Oto lepszy przykład użycia rozwiązania Joe Kingtona z bardziej realistycznym df. The thing I ' m unsure o tym, jak uzyskać dostęp do df.elementy b dla każdego df.Grupa poniżej:

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?
# ...
Author: user248237dfsf, 2013-06-05

4 answers

Może być bardziej efektywny sposób( mam przeczucie pandas.crosstab przydałby się tutaj), ale oto jak bym to zrobił:

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: jako, że OP prosił specjalnie o środki b binowane przez wartości w a, po prostu zrób

groups.mean().b

Jeśli chcesz, aby indeks wyglądał ładniej (np. wyświetlał interwały jako indeks), tak jak w przykładzie @bdiamante, użyj pandas.cut zamiast numpy.digitize. (Wyrazy uznania dla bidamante. Nie wiedziałem, że istnieje.)

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

To wyniki w:

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
Author: Joe Kington,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-06 17:14:45

Nie jestem w 100% pewien, czy to jest to, czego szukasz, ale oto, co myślę, że dochodzisz do:

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

Oczywiście, Nie wiem, jakie kosze miałeś na myśli, więc będziesz musiał wymienić Moje na swoje okoliczności.

 21
Author: bdiamante,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-05 20:42:58

Odpowiedź Joe Kingtona była bardzo pomocna, jednak zauważyłem, że nie binuje wszystkich danych. W rzeczywistości zostawia wiersz z = a.min (). Sumując groups.size() dał 99 zamiast 100.

Aby zagwarantować, że wszystkie dane są binowane, po prostu podaj liczbę pojemników do cut (), A Ta funkcja automatycznie ustawi pierwszy [ostatni] pojemnik o 0,1%, aby upewnić się, że wszystkie dane są dołączone.

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)

W tym przypadku, sumowanie grup.rozmiar () dał 100.

Wiem, że to wybredny punkt dla ten konkretny problem, ale dla podobnego problemu, który próbowałem rozwiązać, było kluczowe, aby uzyskać poprawną odpowiedź.

 13
Author: Perk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-16 02:26:51

Jeśli nie musisz trzymać się pandas grupowania, możesz użyć 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
Author: bio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-30 10:46:25