2017-06-28 5 views
1

Je lis à partir d'une base de données qui a beaucoup de colonnes de type tableau, qui pd.read_sql me donne un dataframe avec des colonnes qui sont dtype=object, contenant des listes.pandas série de listes déroulant plus rapide pour le codage à chaud unique?

Je voudrais un moyen efficace de trouver les lignes ont des tableaux contenant un élément:

s = pd.Series(
    [[1,2,3], [1,2], [99], None, [88,2]] 
) 
print s 

..

0 [1, 2, 3] 
1  [1, 2] 
2   [99] 
3   None 
4  [88, 2] 

tables de fonction 1-codées à chaud pour une application ML et je souhaitez finir avec des tables comme:

contains_1 contains_2, contains_3 contains_88 
0 1   ... 
1 1 
2 0 
3 nan 
4 0 
... 

je peux déroulez une série de tableaux comme ceci:

s2 = s.apply(pd.Series).stack() 

0 0  1.0 
    1  2.0 
    2  3.0 
1 0  1.0 
    1  2.0 
2 0 99.0 
4 0 88.0 
    1  2.0 

qui me reçoit au pouvoir de trouver les éléments répondre à certains tests:

>>> print s2[(s2==2)].index.get_level_values(0) 
Int64Index([0, 1, 4], dtype='int64') 

Woot! Cette étape:

s.apply(pd.Series).stack() 

produit un intermédiaire structure de données (s2) qui est rapide à parcourir pour chaque catégorie. Cependant, l'étape apply est extrêmement lente (plusieurs dizaines de secondes pour une seule colonne avec 500k lignes avec des listes de 10 éléments), et j'ai beaucoup de colonnes.


Mise à jour: Il semble probable que les données d'une série de listes commencent par être assez lentes. L'exécution de dérouler du côté SQL semble difficile (j'ai beaucoup de colonnes que je veux dérouler). Existe-t-il un moyen d'extraire des données matricielles dans une meilleure structure?

Répondre

1
import numpy as np 
import pandas as pd 
import cytoolz 

s0 = s.dropna() 
v = s0.values.tolist() 
i = s0.index.values 
l = [len(x) for x in v] 
c = cytoolz.concat(v) 
n = np.append(0, np.array(l[:-1])).cumsum().repeat(l) 
k = np.arange(len(c)) - n 

s1 = pd.Series(c, [i.repeat(l), k]) 

MISE À JOUR: Ce qui a fonctionné pour moi ...

def unroll(s): 
    s = s.dropna() 
    v = s.values.tolist() 
    c = pd.Series(x for x in cytoolz.concat(v)) # 16 seconds! 
    i = s.index 
    lens = np.array([len(x) for x in v]) #s.apply(len) is slower 
    n = np.append(0, lens[:-1]).cumsum().repeat(lens) 
    k = np.arange(sum(lens)) - n 

    s = pd.Series(c) 
    s.index = [i.repeat(lens), k] 

    s = s.dropna() 
    return s 

Il devrait être possible de remplacer:

s = pd.Series(c) 
    s.index = [i.repeat(lens), k] 

avec:

 s = pd.Series(c, index=[i.repeat(lens), k]) 

Mais ce n » t travail. (Dit est ok here)

+0

Au sommet de ma tête! Faites-moi savoir si cela fonctionne. – piRSquared

+0

Merci - J'ai ajouté quelques réglages. Et mettez dans votre réponse. cytoolz.concat est un ordre ou une magnitude plus rapide que .apply (Series), mais est toujours ~ 16 secondes par colonne pour mon application. – user48956