La forma más rápida de calcular la diferencia en todas las columnas.

Tengo un dataframe de todas las columnas flotantes. Por ejemplo:

import numpy as np import pandas as pd df = pd.DataFrame(np.arange(12.0).reshape(3,4), columns=list('ABCD')) # ABCD # 0 0.0 1.0 2.0 3.0 # 1 4.0 5.0 6.0 7.0 # 2 8.0 9.0 10.0 11.0 

Me gustaría calcular las diferencias de columnas para todas las combinaciones de columnas (por ejemplo, AB, AC, BC, etc.).

Por ejemplo, la salida deseada sería algo como:

  A_B A_C A_D B_C B_D C_D -1.0 -2.0 -3.0 -1.0 -2.0 -1.0 -1.0 -2.0 -3.0 -1.0 -2.0 -1.0 -1.0 -2.0 -3.0 -1.0 -2.0 -1.0 

Dado que la cantidad de columnas puede ser grande, me gustaría hacer los cálculos de la manera más eficiente / rápida posible. Asumo que obtendré un gran impulso de velocidad al convertir el dataframe en una matriz numpy primero, así que lo haré, pero me pregunto si hay alguna otra estrategia que pueda resultar en grandes ganancias de rendimiento. Tal vez algún álgebra matricial o truco de formato de datos multidimensionales que resulte en no tener que recorrer todas las combinaciones únicas. Cualquier sugerencia es bienvenida. Este proyecto está en Python 3.

En esta publicación se enumeran dos enfoques NumPy para el rendimiento: uno sería un enfoque completamente vectorizado y otro con un bucle.

Enfoque # 1

 def numpy_triu1(df): a = df.values r,c = np.triu_indices(a.shape[1],1) cols = df.columns nm = [cols[i]+"_"+cols[j] for i,j in zip(r,c)] return pd.DataFrame(a[:,r] - a[:,c], columns=nm) 

Ejecución de la muestra

 In [72]: df Out[72]: ABCD 0 0.0 1.0 2.0 3.0 1 4.0 5.0 6.0 7.0 2 8.0 9.0 10.0 11.0 In [78]: numpy_triu(df) Out[78]: A_B A_C A_D B_C B_D C_D 0 -1.0 -2.0 -3.0 -1.0 -2.0 -1.0 1 -1.0 -2.0 -3.0 -1.0 -2.0 -1.0 2 -1.0 -2.0 -3.0 -1.0 -2.0 -1.0 

Enfoque # 2

Si estamos de acuerdo con la matriz como salida o dataframe sin nombres de columna especializados, aquí hay otro:

 def pairwise_col_diffs(a): # a would df.values n = a.shape[1] N = n*(n-1)//2 idx = np.concatenate(( [0], np.arange(n-1,0,-1).cumsum() )) start, stop = idx[:-1], idx[1:] out = np.empty((a.shape[0],N),dtype=a.dtype) for j,i in enumerate(range(n-1)): out[:, start[j]:stop[j]] = a[:,i,None] - a[:,i+1:] return out 

Prueba de tiempo de ejecución

Dado que OP ha mencionado que la salida de matriz multi-dim también funcionaría para ellos, aquí están los enfoques basados ​​en matriz de otros autores:

 # @Allen's soln def Allen(arr): n = arr.shape[1] idx = np.asarray(list(itertools.combinations(range(n),2))).T return arr[:,idx[0]]-arr[:,idx[1]] # @DYZ's soln def DYZ(arr): result = np.concatenate([(arr.T - arr.T[x])[x+1:] \ for x in range(arr.shape[1])]).T return result 

pandas solución basada en pandas de la publicación de @Gerges Dib no se incluyó ya que salió muy lenta en comparación con otras.

Tiempos –

Usaremos tres tamaños de conjuntos de datos: 100 , 500 y 1000 :

 In [118]: df = pd.DataFrame(np.random.randint(0,9,(3,100))) ...: a = df.values ...: In [119]: %timeit DYZ(a) ...: %timeit Allen(a) ...: %timeit pairwise_col_diffs(a) ...: 1000 loops, best of 3: 258 µs per loop 1000 loops, best of 3: 1.48 ms per loop 1000 loops, best of 3: 284 µs per loop In [121]: df = pd.DataFrame(np.random.randint(0,9,(3,500))) ...: a = df.values ...: In [122]: %timeit DYZ(a) ...: %timeit Allen(a) ...: %timeit pairwise_col_diffs(a) ...: 100 loops, best of 3: 2.56 ms per loop 10 loops, best of 3: 39.9 ms per loop 1000 loops, best of 3: 1.82 ms per loop In [123]: df = pd.DataFrame(np.random.randint(0,9,(3,1000))) ...: a = df.values ...: In [124]: %timeit DYZ(a) ...: %timeit Allen(a) ...: %timeit pairwise_col_diffs(a) ...: 100 loops, best of 3: 8.61 ms per loop 10 loops, best of 3: 167 ms per loop 100 loops, best of 3: 5.09 ms per loop 

Creo que puedes hacerlo con NumPy. Sea arr=df.values . Primero, encontremos todas las combinaciones de dos columnas:

 from itertools import combinations column_combos = combinations(range(arr.shape[1]), 2) 

Ahora, reste las columnas por pares y convierta una lista de arreglos a un arreglo 2D:

 result = np.array([(arr[:,x[1]] - arr[:,x[0]]) for x in column_combos]).T #array([[1., 2., 3., 1., 2., 1.], # [1., 2., 3., 1., 2., 1.], # [1., 2., 3., 1., 2., 1.]]) 

Otra solución es algo más rápida (~ 15%) porque resta matrices 2D enteras en lugar de columnas, y tiene menos iteraciones del lado de Python:

 result = np.concatenate([(arr.T - arr.T[x])[x+1:] for x in range(arr.shape[1])]).T #array([[ 1., 2., 3., 1., 2., 1.], # [ 1., 2., 3., 1., 2., 1.], # [ 1., 2., 3., 1., 2., 1.]]) 

Puede convertir el resultado de nuevo a un DataFrame si desea:

 columns = list(map(lambda x: x[1]+x[0], combinations(df.columns, 2))) #['BA', 'CA', 'DA', 'CB', 'DB', 'DC'] pd.DataFrame(result, columns=columns) # BA CA DA CB DB DC #0 1.0 2.0 3.0 1.0 2.0 1.0 #1 1.0 2.0 3.0 1.0 2.0 1.0 #2 1.0 2.0 3.0 1.0 2.0 1.0 
 import itertools df = pd.DataFrame(np.arange(12.0).reshape(3,4), columns=list('ABCD')) df_cols = df.columns.tolist() #build a index array of all the pairs need to do the subtraction idx = np.asarray(list(itertools.combinations(range(len(df_cols)),2))).T #build a new DF using the pairwise difference and column names df_new = pd.DataFrame(data=df.values[:,idx[0]]-df.values[:,idx[1]], columns=[''.join(e) for e in (itertools.combinations(df_cols,2))]) df_new Out[43]: AB AC AD BC BD CD 0 -1.0 -2.0 -3.0 -1.0 -2.0 -1.0 1 -1.0 -2.0 -3.0 -1.0 -2.0 -1.0 2 -1.0 -2.0 -3.0 -1.0 -2.0 -1.0 

No estoy seguro de qué tan rápido se puede comparar esto con otros métodos posibles, pero aquí está:

 df = pd.DataFrame(np.arange(12.0).reshape(3,4), columns=list('ABCD')) # get the columns as list cols = list(df.columns) # define output dataframe out = pd.DataFrame() # loop over possible periods for period in range(1, df.shape[1]): names = [l1 + l2 for l1, l2, in zip(cols, cols[period:])] out[names] = df.diff(periods=period, axis=1).dropna(axis=1, how='all') print(out) # column name shows which two columns are subtracted AB BC CD AC BD AD 0 1.0 1.0 1.0 2.0 2.0 3.0 1 1.0 1.0 1.0 2.0 2.0 3.0 2 1.0 1.0 1.0 2.0 2.0 3.0