2016-02-23 11 views
5

Ho una tabella SQL che posso leggere in un frame di dati Pandas, che ha la seguente struttura:Come creare in modo efficiente uno SparseDataFrame da una lunga tabella?

user_id value 
1   100 
1   200 
2   100 
4   200 

È una rappresentazione di una matrice, per cui tutti i valori sono 1 o 0. La fitta rappresentazione di questa matrice sarebbe simile a questa:

100 200 
1 1 1 
2 1 0 
4 0 1 

Normalmente, per fare questa conversione è possibile utilizzare perno, ma nel mio caso con decine o centinaia di milioni di righe nella prima tabella si ha una grande matrice densa piena di zeri che è costoso da trascinare. Puoi convertirlo in sparse, ma arrivare a destinazione richiede molte risorse.

In questo momento sto lavorando a una soluzione per assegnare i numeri di riga a ogni ID utente, l'ordinamento e quindi suddividere la colonna "valore" in SparseSeries prima di ricombinarla in SparseDataFrame. C'è un modo migliore?

+0

Non ho visto molte discussioni su SparseSeries su SO. Ho risposto ad alcune domande sul trasferimento avanti e indietro tra questo e le matrici 'scipy'' sparse'. La mia impressione è che la struttura 'sparse' di Pandas sia ancora in fase di sviluppo. – hpaulj

+0

Ne ho trovati alcuni, come la tua risposta qui http://stackoverflow.com/questions/34181494/populate-a-pandas-sparsedataframe-from-a-scipy-sparse-coo-matrix Il problema è che non funziona sembra in scala. In questo momento sto provando a convertire una matrice csc che è 40.000 x 15.000 ed è in esecuzione da più di 30 minuti. –

risposta

1

Sono arrivato a una soluzione, anche se leggermente imperfetta.

Ciò che si può fare è creare manualmente dalle colonne un certo numero di Pandas SparseSeries, combinarle in un dict e quindi eseguire il cast in un DataFrame (non in SparseDataFrame). Casting come SparseDataFrame colpisce attualmente un costruttore immaturo, che decostruisce l'intero oggetto in denso e quindi torna in forma sparsa indipendentemente dall'input. La costruzione di SparseSeries in un DataFrame convenzionale, tuttavia, mantiene la scarsità ma crea un oggetto DataFrame valido e altrimenti completo.

Ecco una dimostrazione di come farlo, scritto più per chiarezza che per le prestazioni. Una differenza con la mia implementazione è che ho creato il dict di vettori sparse come una comprensione del ditt invece di un ciclo.

import pandas 
import numpy 

df = pandas.DataFrame({'user_id':[1,2,1,4],'value':[100,100,200,200]}) 

# Get unique users and unique features 
num_rows = len(df['user_id'].unique()) 
num_features = len(df['value'].unique()) 
unique_users = df['user_id'].unique().copy() 
unique_features = df['value'].unique().copy() 
unique_users.sort() 
unique_features.sort() 


# assign each user_id to a row_number 
user_lookup = pandas.DataFrame({'uid':range(num_rows), 'user_id':unique_users}) 


vec_dict = {} 

# Create a sparse vector for each feature 
for i in range(num_features): 
    users_with_feature = df[df['value']==unique_features[i]]['user_id'] 

    uid_rows = user_lookup[user_lookup['user_id'].isin(users_with_feature)]['uid'] 

    vec = numpy.zeros(num_rows) 
    vec[uid_rows] = 1 

    sparse_vec = pandas.Series(vec).to_sparse(fill_value=0) 

    vec_dict[unique_features[i]] = sparse_vec 


my_pandas_frame = pandas.DataFrame(vec_dict)  
my_pandas_frame = my_pandas_frame.set_index(user_lookup['user_id']) 

I risultati:

>>> my_pandas_frame 
     100 200 
user_id   
1   1 1 
2   1 0 
4   0 1 

>>> type(my_pandas_frame) 
<class 'pandas.core.frame.DataFrame'> 

>>> type(my_pandas_frame[100]) 
<class 'pandas.sparse.series.SparseSeries'> 

completa, ma ancora scarsi. Ci sono alcune avvertenze, se si fa una semplice copia o sottoinsieme non-in-place, allora si dimenticherà di se stesso e si tenterà di ricollegarlo ad una densità, ma per i miei scopi sono abbastanza soddisfatto.