pandas быстрее разворачивает серию списков для быстрого кодирования?

Я читаю из базы данных, в которой было много столбцов типа массива, и pd.read_sql дает мне фрейм данных со столбцами, которые dtype=object, содержащие списки.

Мне нужен эффективный способ найти, в каких строках есть массивы, содержащие какой-либо элемент:

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]

1 с горячим кодированием таблиц функций для приложения ML, и я хотел бы получить такие таблицы, как:

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

Я могу развернуть ряд таких массивов:

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

что дает мне возможность найти элементы, соответствующие некоторому тесту:

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

Woot! Этот шаг:

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

создает отличную промежуточную структуру данных (s2), которую можно быстро перебирать для каждой категории. Однако шаг apply невероятно медленный (много десятков секунд для одного столбца с 500 тыс. Строк со списками из 10 элементов), а у меня много столбцов.


Обновление: кажется вероятным, что наличие данных в серии списков для начала происходит довольно медленно. Выполнение развертывания на стороне SQL кажется сложным (у меня есть много столбцов, которые я хочу развернуть). Есть ли способ превратить данные массива в лучшую структуру?


person user48956    schedule 28.06.2017    source источник


Ответы (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])

ОБНОВЛЕНИЕ: что сработало для меня ...

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

Должна быть возможность заменить:

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

с участием:

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

Но это не работает. (Говорит, что можно здесь)

person piRSquared    schedule 29.06.2017
comment
Спасибо - добавил пару настроек. И вложите в свой ответ. cytoolz.concat на порядок быстрее, чем .apply (Series), но для моего приложения он по-прежнему составляет ~ 16 секунд на столбец. - person user48956; 30.06.2017