Панды разрежают фрейм данных на разреженную матрицу, не генерируя плотную матрицу в памяти

есть ли способ, чтобы преобразовать pandas.SparseDataFrame to scipy.sparse.csr_matrix, не генерируя плотную матрицу в памяти?

scipy.sparse.csr_matrix(df.values)

не работает, поскольку он генерирует плотную матрицу, которая отбрасывается на csr_matrix.

спасибо заранее!

5 ответов


Pandas docs говорит об экспериментальном преобразовании в scipy sparse, SparseSeries.to_coo:

http://pandas-docs.github.io/pandas-docs-travis/sparse.html#interaction-with-scipy-sparse

================

edit - это специальная функция из мультииндекса, а не фрейм данных. Увидеть другие ответы, для этого. Обратите внимание на разницу в датах.

============

по состоянию на 0.20.0, есть sdf.to_coo() и a multiindex ss.to_coo(). Поскольку разреженная матрица по своей сути является 2d, имеет смысл требовать multiindex для (эффективно) 1D dataseries. В то время как dataframe может представлять собой таблицу или 2D-массив.

когда я впервые ответил на этот вопрос, эта разреженная функция dataframe/series была экспериментальной (июнь 2015).


панды 0.20.0+:

по состоянию на pandas версии 0.20.0, выпущенный 5 мая 2017 года, для этого есть один лайнер:

from scipy import sparse


def sparse_df_to_csr(df):
    return sparse.csr_matrix(df.to_coo())

это использует новый to_coo() метод.

Более Ранние Версии:

основываясь на ответе Виктора Мая, вот немного более быстрая реализация, но она работает только в том случае, если весь SparseDataFrame скудно со всеми BlockIndex (примечание: если он был создан с get_dummies, это будет случай.)

редактировать: я изменил это, чтобы он работал с ненулевым значением заполнения. CSR не имеет собственного ненулевого значения заполнения, поэтому вам придется записывать его извне.

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()

ответ @Marigold делает трюк, но он медленный из-за доступа ко всем элементам в каждом столбце, включая нули. Основываясь на этом, я написал следующий быстрый и грязный код, который работает примерно на 50x быстрее на матрице 1000x1000 с плотностью около 1%. Мой код также обрабатывает плотные столбцы соответствующим образом.

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()

вот решение, которое заполняет столбец разреженной матрицы по столбцам (предполагается, что вы можете поместить хотя бы один столбец в память).

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()

редактировать: этот метод фактически имеет плотное представление на каком-то этапе, поэтому он не решает вопрос.

вы должны иметь возможность использовать экспериментальный .to_coo() метод в pandas [1] следующим образом:

df, idx_rows, idx_cols = df.stack().to_sparse().to_coo()
df = df.tocsr()

этот метод, вместо того, чтобы DataFrame (строки / столбцы) требуется Series со строками и столбцами в MultiIndex (вот почему вам нужен .stack() метод). Это Series С MultiIndex должен быть SparseSeries, и даже если ваш вход является SparseDataFrame, .stack() возвращает обычный Series. Итак, вам нужно использовать .to_sparse() метод перед вызовом .to_coo().

на Series возвращено .stack(), даже если это не SparseSeries содержит только элементы, которые не являются нулевыми, поэтому он не должен занимать больше памяти, чем разреженная версия (по крайней мере, с np.nan когда тип np.float).

  1. http://pandas.pydata.org/pandas-docs/stable/sparse.html#interaction-with-scipy-sparse