Calcule el promedio de cada x filas en una tabla y cree una nueva tabla

Tengo una larga tabla de datos (~ 200 filas por 50 columnas) y necesito crear un código que pueda calcular los valores medios de cada dos filas y para cada columna de la tabla con la salida final como una nueva tabla de la media valores. ¡Esto es obviamente una locura que hacer en Excel! Uso python3 y soy consciente de algunas preguntas similares: aquí , aquí y aquí . Pero nada de esto me ayuda, ya que necesito un código elegante para trabajar con varias columnas y produce una tabla de datos organizada. Por la forma en que mi dato original se ha importado utilizando pandas y se define como un dataframe, pero no se pudo encontrar una manera fácil de hacer esto en pandas. La ayuda es muy apreciada.

Un ejemplo de la tabla (versión corta) es:

abcd 2 50 25 26 4 11 38 44 6 33 16 25 8 37 27 25 10 28 48 32 12 47 35 45 14 8 16 7 16 12 16 30 18 22 39 29 20 9 15 47 

Tabla de medias esperada:

 abcd 3 30.5 31.5 35 7 35 21.5 25 11 37.5 41.5 38.5 15 10 16 18.5 19 15.5 27 38 

Puede crear un grupo artificial usando df.index//2 (o como lo señaló @DSM, usando np.arange(len(df))//2 – para que funcione para todos los índices) y luego use groupby:

 df.groupby(np.arange(len(df))//2).mean() Out[13]: abcd 0 3.0 30.5 31.5 35.0 1 7.0 35.0 21.5 25.0 2 11.0 37.5 41.5 38.5 3 15.0 10.0 16.0 18.5 4 19.0 15.5 27.0 38.0 

La forma NumPythonic sería extraer los elementos como una matriz NumPy con df.values , luego remodelar a una matriz 3D con 2 elementos a lo largo de los axis=1 y 4 largo de axis=2 y realizar la reducción promedio a lo largo de axis=1 y finalmente volver a convertir un dataframe, como tal –

 pd.DataFrame(df.values.reshape(-1,2,df.shape[1]).mean(1)) 

Como resultado, puede introducir la herramienta muy eficiente de NumPy: np.einsum para hacer esta average-reduction como una combinación de sum-reduction de la sum-reduction scaling-down , como así:

 pd.DataFrame(np.einsum('ijk->ik',df.values.reshape(-1,2,df.shape[1]))/2.0) 

Tenga en cuenta que los enfoques propuestos suponen que el número de filas es divisible por 2 .

También como lo noted by @DSM , para conservar los nombres de las columnas, debe agregar columns=df.columns al volver a convertir a Dataframe, es decir:

 pd.DataFrame(...,columns=df.columns) 

Ejecución de la muestra

 >>> df 0 1 2 3 0 2 50 25 26 1 4 11 38 44 2 6 33 16 25 3 8 37 27 25 4 10 28 48 32 5 12 47 35 45 6 14 8 16 7 7 16 12 16 30 8 18 22 39 29 9 20 9 15 47 >>> pd.DataFrame(df.values.reshape(-1,2,df.shape[1]).mean(1)) 0 1 2 3 0 3 30.5 31.5 35.0 1 7 35.0 21.5 25.0 2 11 37.5 41.5 38.5 3 15 10.0 16.0 18.5 4 19 15.5 27.0 38.0 >>> pd.DataFrame(np.einsum('ijk->ik',df.values.reshape(-1,2,df.shape[1]))/2.0) 0 1 2 3 0 3 30.5 31.5 35.0 1 7 35.0 21.5 25.0 2 11 37.5 41.5 38.5 3 15 10.0 16.0 18.5 4 19 15.5 27.0 38.0 

Pruebas de tiempo de ejecución

En esta sección, @ayhan's solution with groupby los tres enfoques enumerados hasta ahora para resolver el problema de rendimiento, incluida @ayhan's solution with groupby .

 In [24]: A = np.random.randint(0,9,(200,50)) In [25]: df = pd.DataFrame(A) In [26]: %timeit df.groupby(df.index//2).mean() # @ayhan's solution 1000 loops, best of 3: 1.61 ms per loop In [27]: %timeit pd.DataFrame(df.values.reshape(-1,2,df.shape[1]).mean(1)) 1000 loops, best of 3: 317 µs per loop In [28]: %timeit pd.DataFrame(np.einsum('ijk->ik',df.values.reshape(-1,2,df.shape[1]))/2.0) 1000 loops, best of 3: 266 µs per loop 
 df.set_index(np.arange(len(df)) // 2).mean(level=0) 

Puede abordar este problema usando pd.rolling() para crear un promedio móvil y luego tomar cada segundo elemento usando iloc

 df = df.rolling(2).mean() df = df.iloc[::2, :] 

Tenga en cuenta que faltará la primera observación (es decir, la rodadura comienza en la parte superior)