Forma más rápida de calcular subconjunto de matriz de correlación

Soy parcial al uso de pandas builtin corr method para marcos de datos. Sin embargo, estoy tratando de calcular la matriz de correlación de un dataframe con 45,000 columnas. Y luego repita esto 250 veces. El cálculo está aplastando a mi carnero (16 GB, mac book pro). Estoy tomando estadísticas sobre las columnas de la matriz de correlación resultante. Así que necesito la correlación de una columna con cada otra columna para calcular esas estadísticas. Mi solución es calcular la correlación de un subconjunto de columnas con cada columna, pero necesito una forma eficiente de hacerlo.

Considerar:

 import pandas as pd import numpy as np np.random.seed([3,1415]) df = pd.DataFrame(np.random.rand(6, 4), columns=list('ABCD')) df 

introduzca la descripción de la imagen aquí

Quiero calcular las correlaciones para solo ['A', 'B']

 corrs = df.corr()[['A', 'B']] corrs 

introduzca la descripción de la imagen aquí

Lo terminaré calculando la media o alguna otra estadística.

No puedo usar el código que utilicé para crear el ejemplo porque cuando lo amplío, no tengo la memoria para hacerlo. Al realizar el cálculo, debe usar una cantidad de memoria proporcional al número de columnas elegidas para calcular las correlaciones en relación con todo lo demás.

Estoy buscando la solución más eficaz a escala. Tengo una solución, pero estoy buscando otras ideas para asegurar que estoy obteniendo lo mejor. Cualquier respuesta provista que devuelva la respuesta correcta como se muestra en la demostración y cumpla con la restricción de memoria será votada por mí (y me gustaría alentar la votación entre ellos también).

A continuación se muestra mi código:

 def corr(df, k=0, l=10): d = df.values - df.values.mean(0) d_ = d[:, k:l] s = d.std(0, keepdims=True) return pd.DataFrame(dTdot(d[:, k:l]) / sTdot(s[:, k:l]) / d.shape[0], df.columns, df.columns[k:l]) 

Usar productos de punto para calcular la correlación (como en su ejemplo) parece ser un buen enfoque. Voy a describir dos mejoras, luego codificarlas implementándolas.

Mejora 1: tirar significa fuera del producto punto

Podemos extraer los medios del producto de puntos, para evitar tener que restarlos de cada valor (de manera similar a cómo se eliminaron las desviaciones estándar del producto de puntos, lo que también haremos).

Sean x, y sean vectores con n elementos. Sean a, b escalares. Deje que denote el producto de puntos entre x y y.

La correlación entre x y y se puede express utilizando el producto punto

 <(x-mean(x))/std(x), (y-mean(y))/std(y)> / n 

Para sacar las desviaciones estándar del producto de puntos, podemos usar la siguiente identidad (como lo hizo anteriormente):

  = a*b* 

Para sacar los medios del producto punto, podemos derivar otra identidad:

  =  + a*sum(y) + b*sum(x) + a*b*n 

En el caso de que a = -mean(x), b = -mean(y) , esto se simplifica a:

  =  - sum(x)*sum(y)/n 

Usando estas identidades, la correlación entre x y y es equivalente a:

 ( - sum(x)*sum(y)/n) / (std(x)*std(y)*n) 

En la función a continuación, esto se expressá mediante la multiplicación de matrices y productos externos para manejar múltiples variables simultáneamente (como en su ejemplo).

Mejora 2: sums previas al cálculo y desviaciones estándar

Podemos pre-calcular las sums y las desviaciones estándar, para evitar volver a calcularlas para todas las columnas cada vez que se llama a la función.

Código

Al juntar las dos mejoras, tenemos lo siguiente (no hablo de pandas, por lo que está en números):

 def corr_cols(x, xsum, xstd, lo, hi): n = x.shape[0] return ( (np.dot(xT, x[:, lo:hi]) - np.outer(xsum, xsum[lo:hi])/n) / (np.outer(xstd, xstd[lo:hi])*n) ) # fake data w/ 10 points, 5 dimensions x = np.random.rand(10, 5) # precompute sums and standard deviations along each dimension xsum = np.sum(x, 0) xstd = np.std(x, 0) # calculate columns of correlation matrix for dimensions 1 thru 3 r = corr_cols(x, xsum, xstd, 1, 4) 

Mejor codigo

El cálculo previo y el almacenamiento de las sums y las desviaciones estándar se pueden ocultar dentro de un cierre, para brindar una interfaz más agradable y mantener el código principal limpio. Funcionalmente, las operaciones son equivalentes al código anterior.

 def col_correlator(x): n = x.shape[0] xsum = np.sum(x, 0) xstd = np.std(x, 0) return lambda lo, hi: ( (np.dot(xT, x[:, lo:hi]) - np.outer(xsum, xsum[lo:hi])/n) / (np.outer(xstd, xstd[lo:hi])*n) ) # construct function to compute columns of correlation matrix cc = col_correlator(x) # compute columns of correlation matrix for dimensions 1 thru 3 r = cc(1, 4) 

EDITAR: (piRSquared)

Quería poner mi edición en esta publicación para fomentar aún más la votación de esta respuesta.

Este es el código que implementé utilizando este consejo. Esta solución se traduce de ida y vuelta entre pandas y adormecidos.

 def corr_closure(df): d = df.values sums = d.sum(0, keepdims=True) stds = d.std(0, keepdims=True) n = d.shape[0] def corr(k=0, l=10): d2 = dTdot(d[:, k:l]) sums2 = sums.T.dot(sums[:, k:l]) stds2 = stds.T.dot(stds[:, k:l]) return pd.DataFrame((d2 - sums2 / n) / stds2 / n, df.columns, df.columns[k:l]) return corr 

Caso de uso:

 corr = corr_closure(df) corr(0, 2) 

introduzca la descripción de la imagen aquí