python - Datos dispersos de pandas Marco de matriz dispersa, sin generar una matriz densa en la memoria
scipy sparse-matrix (5)
Pandas 0.20.0+:
A partir de la versión de pandas 0.20.0, lanzada el 5 de mayo de 2017, hay una frase para esto:
from scipy import sparse
def sparse_df_to_csr(df):
return sparse.csr_matrix(df.to_coo())
Esto usa el nuevo
método
to_coo()
.
Versiones mas antiguas:
Basándose en la respuesta de Victor May, aquí hay una implementación un poco más rápida, pero solo funciona si todo el
SparseDataFrame
es escaso con todos los
BlockIndex
(nota: si fue creado con
get_dummies
, este será el caso).
Editar : modifiqué esto para que funcione con un valor de relleno distinto de cero. CSR no tiene un valor de relleno nativo distinto de cero, por lo que deberá registrarlo externamente.
import numpy as np
import pandas as pd
from scipy import sparse
def sparse_BlockIndex_df_to_csr(df):
columns = df.columns
zipped_data = zip(*[(df[col].sp_values - df[col].fill_value,
df[col].sp_index.to_int_index().indices)
for col in columns])
data, rows = map(list, zipped_data)
cols = [np.ones_like(a)*i for (i,a) in enumerate(data)]
data_f = np.concatenate(data)
rows_f = np.concatenate(rows)
cols_f = np.concatenate(cols)
arr = sparse.coo_matrix((data_f, (rows_f, cols_f)),
df.shape, dtype=np.float64)
return arr.tocsr()
¿Hay alguna forma de convertir un
pandas.SparseDataFrame
a
scipy.sparse.csr_matrix
, sin generar una matriz densa en la memoria?
scipy.sparse.csr_matrix(df.values)
no funciona, ya que genera una matriz densa que se
csr_matrix
.
¡Gracias por adelantado!
Aquí hay una solución que llena la matriz dispersa columna por columna (se supone que puede ajustar al menos una columna en la memoria).
import pandas as pd
import numpy as np
from scipy.sparse import lil_matrix
def sparse_df_to_array(df):
""" Convert sparse dataframe to sparse array csr_matrix used by
scikit learn. """
arr = lil_matrix(df.shape, dtype=np.float32)
for i, col in enumerate(df.columns):
ix = df[col] != 0
arr[np.where(ix), i] = df.ix[ix, col]
return arr.tocsr()
La respuesta de @Marigold hace el truco, pero es lenta debido al acceso a todos los elementos en cada columna, incluidos los ceros. Sobre la base de esto, escribí el siguiente código rápido y sucio, que se ejecuta aproximadamente 50 veces más rápido en una matriz 1000x1000 con una densidad de aproximadamente 1%. Mi código también maneja columnas densas apropiadamente.
def sparse_df_to_array(df):
num_rows = df.shape[0]
data = []
row = []
col = []
for i, col_name in enumerate(df.columns):
if isinstance(df[col_name], pd.SparseSeries):
column_index = df[col_name].sp_index
if isinstance(column_index, BlockIndex):
column_index = column_index.to_int_index()
ix = column_index.indices
data.append(df[col_name].sp_values)
row.append(ix)
col.append(len(df[col_name].sp_values) * [i])
else:
data.append(df[col_name].values)
row.append(np.array(range(0, num_rows)))
col.append(np.array(num_rows * [i]))
data_f = np.concatenate(data)
row_f = np.concatenate(row)
col_f = np.concatenate(col)
arr = coo_matrix((data_f, (row_f, col_f)), df.shape, dtype=np.float64)
return arr.tocsr()
Pandas docs habla sobre una conversión experimental a scipy sparse, SparseSeries.to_coo:
http://pandas-docs.github.io/pandas-docs-travis/sparse.html#interaction-with-scipy-sparse
================
editar: esta es una función especial de un índice múltiple, no un marco de datos. Vea las otras respuestas para eso. Tenga en cuenta la diferencia en las fechas.
============
A partir de 0.20.0, hay un
sdf.to_coo()
y un
ss.to_coo()
.
Dado que una matriz dispersa es intrínsecamente 2d, tiene sentido requerir un índice múltiple para las series de datos 1d (efectivamente).
Mientras que el marco de datos puede representar una tabla o una matriz 2D.
Cuando respondí por primera vez a esta pregunta, esta característica de serie / marco de datos escasa fue experimental (junio de 2015).
EDITAR : Este método en realidad tiene una representación densa en alguna etapa, por lo que no resuelve la pregunta.
Debería poder utilizar el
.to_coo()
experimental
.to_coo()
en pandas [1] de la siguiente manera:
df, idx_rows, idx_cols = df.stack().to_sparse().to_coo()
df = df.tocsr()
Este método, en lugar de tomar un
DataFrame
(filas / columnas), toma una
Series
con filas y columnas en un
MultiIndex
(es por eso que necesita el método
.stack()
).
Esta
Series
con el
MultiIndex
debe ser una
SparseSeries
, e incluso si su entrada es un
SparseDataFrame
,
.stack()
devuelve una
Series
normal.
Por lo tanto, debe usar el método
.to_sparse()
antes de llamar a
.to_coo()
.
La
Series
devuelta por
.stack()
, incluso si no es una
SparseSeries
solo contiene los elementos que no son nulos, por lo que no debería ocupar más memoria que la versión dispersa (al menos con
np.nan
cuando el tipo es
np.float
)