2017-06-07 1 views
2

Hej, J'essaye de vectoriser des objets qui peuvent appartenir à plusieurs catégories et les mettre dans un cadre de données pandas. J'ai déjà trouvé une solution mais c'est très lent. Alors, voici ce que je fais:Vectorisation de données multi-catégories avec des pandas

Voilà comment mes données ressemble à:

data = { 
    'A':['c1','c2','c3'], 
    'B':['c4','c5','c2'], 
    'C':['c2','c1','c4'] 
} 

J'ai trois articles (A-C) qui appartiennent à cinq catégories différentes (C1-C5).

donc créer un un dataframe vide, itérer sur les points qui les transforment en objets de la série booléennes avec l'index droit et les ajouter:

df = pd.SparseDataFrame() 
for k, v in data.items(): 
    s = pd.Series(np.ones_like(v, dtype=bool), index=v, name=k) 
    df = df.append(s) 

Mon résultat ressemble à ceci:

Resulting Dataframe

Je suis content de ce résultat mais mes vraies données ont ~ 200k catégories ce qui rend cette approche terriblement lente. Avez-vous des suggestions pour accélérer?

Remarque: toutes les catégories et extraction les faisant passer sous forme de colonnes dans le vide dataframe ne vous aide pas:

df = pd.SparseDataFrame(columns=all_categories) 

Répondre

1

Considérez l'approche d'économie mémoire suivante:

In [143]: df = pd.DataFrame([' '.join(data[k]) for k in data.keys()], 
          index=data.keys(), 
          columns=['text']) 

In [144]: df 
Out[144]: 
     text 
C c2 c1 c4 
A c1 c2 c3 
B c4 c5 c2 

In [145]: from sklearn.feature_extraction.text import CountVectorizer 

In [146]: cv = CountVectorizer() 

In [147]: df = pd.SparseDataFrame(cv.fit_transform(df['text']), 
            columns=cv.get_feature_names(), 
            index=df.index) 

In [148]: df 
Out[148]: 
    c1 c2 c3 c4 c5 
C 1.0 1 NaN 1.0 NaN 
A 1.0 1 1.0 NaN NaN 
B NaN 1 NaN 1.0 1.0 


In [149]: df.memory_usage() 
Out[149]: 
Index 80 
c1  16 
c2  24 
c3  8 
c4  16 
c5  8 
dtype: int64 
+0

Cette idée a l'air géniale, j'ai aussi pensé à rejoindre les catégories et à utiliser un sklearn Vectorizer. Malheureusement, je suis confronté à une erreur étrange lors du passage de la matrice csr adaptée au constructeur SparseDataFrame: Fichier "/home/user/anaconda3/lib/python3.6/site-packages/pandas/sparse/frame.py", ligne 125, dans __init__ generic.NDFrame.__init __ (self, mgr) UnboundLocalError: la variable locale 'mgr' référencée avant l'affectation – nadre

+0

@nadre, désolé, j'ai oublié de mentionner que le constructeur 'pd.SparseDataFrame (sparse_array)' a été ajouté dans Pandas v. 0.20, donc ça devrait fonctionner pour Pandas 0.20+ versions – MaxU

+0

@nadre, si vous ne pouvez pas mettre à jour votre version de Pandas, vous pouvez utiliser [l'approche suivante] (https://stackoverflow.com/questions/41916560/pandas-dataframe-memory-python/41916672#41916672 – MaxU

1

Vous pouvez essayer (source):

df = pd.DataFrame.from_dict(data, orient='index') 

puis

res = df.stack().reset_index().groupby(['level_0', 0]).size().unstack() 

Enfin, vous pouvez convertir la sortie en SparseDataFrame

result = pd.SparseDataFrame(res) 

Avec df.stack() vous avez:

A 0 c1 
    1 c2 
    2 c3 
B 0 c4 
    1 c5 
    2 c2 
C 0 c2 
    1 c1 
    2 c4 

Alors vous faites le reset_index:

level_0 level_1 0 
0  A  0 c1 
1  A  1 c2 
2  A  2 c3 
3  B  0 c4 
4  B  1 c5 
5  B  2 c2 
6  C  0 c2 
7  C  1 c1 
8  C  2 c4 

Vous pouvez changer les noms de colonnes pour être plus propre. Le groupe par la méthode faire le compte:

level_0 0 
A  c1 1 
     c2 1 
     c3 1 
B  c2 1 
     c4 1 
     c5 1 
C  c1 1 
     c2 1 
     c4 1 

Et enfin:

0   c1 c2 c3 c4 c5 
level_0 
A  1.0 1.0 1.0 NaN NaN 
B  NaN 1.0 NaN 1.0 1.0 
C  1.0 1.0 NaN 1.0 NaN 
+0

J'aime cette idée, mais il fonctionne aussi longtemps que les listes dans les données ont la même longueur. – nadre

+0

@nadre J'ai mis à jour la réponse. Dites-moi si cela a fonctionné et correspond à la mémoire. – Till