binning un dataframe en pandas en Python

Dado el siguiente dataframe en pandas:

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

donde id es un id para cada punto que consiste en un valor a y b , ¿cómo puedo agrupar a y b en un conjunto específico de contenedores (de modo que pueda tomar la mediana / valor promedio de a y b en cada contenedor)? df puede tener valores de NaN para a o b (o ambos) para cualquier fila dada en df . Gracias.

Aquí hay un ejemplo mejor utilizando la solución de Joe Kington con un df más realista. De lo que no estoy seguro es de cómo acceder a los elementos df.b para cada grupo df.a a continuación:

 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? # ... 

Puede haber una forma más eficiente (tengo la sensación de que es útil usar pandas.crosstab aquí), pero así es como lo haría:

 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: Como el OP pedía específicamente los medios de b bin por los valores en a , simplemente haga

 groups.mean().b 

Además, si desea que el índice se vea mejor (por ejemplo, los intervalos de visualización como índice), como lo hacen en el ejemplo de @bdiamante, use pandas.cut lugar de numpy.digitize . (Felicitaciones a bidamante. No me di cuenta de que existían pandas.cut ).

 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 

Esto resulta en:

 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 

No estoy 100% seguro de si esto es lo que estás buscando, pero esto es a lo que creo que te refieres:

 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 

Por supuesto, no sé qué bandejas tenía en mente, por lo que tendrá que cambiar las mías por su circunstancia.

La respuesta de Joe Kington fue muy útil, sin embargo, noté que no almacena todos los datos. De hecho, deja la fila con a = a.min (). En resumen, groups.size() dio 99 en lugar de 100.

Para garantizar que todos los datos estén agrupados, simplemente ingrese la cantidad de contenedores para cortar () y esa función rellenará automáticamente el primer [último] contenedor en un 0,1% para garantizar que se incluya toda la información.

 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) 

En este caso, la sum de groups.size () dio 100.

Sé que este es un punto delicado para este problema en particular, pero para un problema similar que estaba tratando de resolver, fue crucial para obtener la respuesta correcta.

Si no tiene que atenerse a la agrupación de pandas , puede usar 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))