Могу ли я использовать лямбду внутри df.apply () для вставки единиц в фрейм данных, где комбинация имен индекса и столбца находится в другом столбце фрейма данных? - PullRequest
1 голос
/ 16 марта 2020

У меня есть этот фрейм данных:

In [6]: import pandas as pd                                                                                                            

In [7]: import numpy as np                                                                                                             

In [8]: df = pd.DataFrame(data = np.nan, 
   ...:                   columns = ['A', 'B', 'C', 'D', 'E'], 
   ...:                   index = ['A', 'B', 'C', 'D', 'E']) 
   ...:                  
   ...: df['list_of_codes'] = [['A' , 'B'], 
   ...:                        ['A', 'B', 'E'], 
   ...:                        ['C', 'D'], 
   ...:                        ['B', 'D'], 
   ...:                        ['E']] 
   ...:  
   ...: df                                                                                                                             
Out[8]: 
    A   B   C   D   E list_of_codes
A NaN NaN NaN NaN NaN        [A, B]
B NaN NaN NaN NaN NaN     [A, B, E]
C NaN NaN NaN NaN NaN        [C, D]
D NaN NaN NaN NaN NaN        [B, D]
E NaN NaN NaN NaN NaN           [E]

И теперь я хочу вставить '1', где и индекс, и имя столбца присутствуют внутри списка в столбце df ['list_of_codes']. Результат будет выглядеть так:

    A   B   C   D   E list_of_codes
A   1   1   0   0   0        [A, B]
B   1   1   0   0   1     [A, B, E]
C   0   0   1   1   0        [C, D]
D   0   1   0   1   0        [B, D]
E   0   0   0   0   1           [E]

Я пробовал что-то вроде этого:

df.apply(lambda x: 1 if x[:-1] in (x[-1]) else 0, axis=1, result_type='broadcast')

, но получаю ошибку:

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

Я не мне кажется, я точно понимаю эту ошибку, но затем я пытаюсь:

df.apply(lambda x: 1 if x[:-1].any() in (x[-1]) else 0, axis=1, result_type='broadcast')

Этот запускает , но не дает мне желаемого результата. Вместо этого он возвращает:

    A   B   C   D   E list_of_codes
A   0   0   0   0   0             0
B   0   0   0   0   0             0
C   0   0   0   0   0             0
D   0   0   0   0   0             0
E   0   0   0   0   0             0

Может ли кто-нибудь помочь мне понять, что мне нужно в моих функциях pd.apply () и lambda, чтобы транслировать «1» так, как я пытаюсь? Заранее спасибо!

Ответы [ 2 ]

2 голосов
/ 17 марта 2020

IIU C, Series.explode, а затем Series.str.get_dummies для проверки. Наконец, мы можем использовать groupby.max для присвоения исходному фрейму данных

df = df.assign(**df['list_of_codes'].explode()
                            .str.get_dummies()
                            .groupby(level=0).max())
print(df)

Вывод

   A  B  C  D  E list_of_codes
A  1  1  0  0  0        [A, B]
B  1  1  0  0  1     [A, B, E]
C  0  0  1  1  0        [C, D]
D  0  1  0  1  0        [B, D]
E  0  0  0  0  1           [E]

Альтернатива без explode

df = df.assign(**pd.DataFrame(df['list_of_codes'].tolist(),
                               index = df.index).stack()
                                                .str.get_dummies()
                                                .groupby(level=0)
                                                .max())

РЕДАКТИРОВАТЬ

Я думаю, что разнесение происходит несколько быстрее, поскольку в альтернативе, которую я предлагаю в конце, мы создаем фрейм данных, а затем используем стек , Мы можем положиться на этот пост: SO explode, чтобы использовать взорваться. С другой стороны, мы можем использовать аксессор уровня вместо groupby. Хорошо, попробуйте взорваться другим методом публикации и найдите метод, который обеспечивает лучшую производительность.

index = df.index
df[index] = pd.get_dummies(pd.Series(data = np.concatenate(s.values),
                         index = index.repeat(s.str.len()))).sum(level=0)

Другой подход с pd.Index.isin:

index=df.index
df[index] = [index.isin(l).astype(int) for l in df['list_of_codes']]

Я думаю, что это может быть самый быстрый

Мы могли бы также рассмотреть написание только true или false. Это было бы быстрее.

index=df.index
df[index] = [index.isin(l) for l in df['list_of_codes']]
0 голосов
/ 17 марта 2020

Я не могу комментировать «репутация менее 50», но я протестировал решение ansev с df размером 15000 * 15000. Вот способ построения тестового df:

import numpy as np
import pandas as pd
nelem = 15000
elements = range(nelem)

x=np.random.randint(low=1, high=len(elements), size=nelem)
list_of_codes=[]
for i in range(nelem):
    list_of_codes.append(np.random.choice(elements,size=x[i]))
df = pd.DataFrame(data = {"list_of_codes":list_of_codes})
for x in elements:
    df[x]=np.nan 

Я тестировал это на колабе дало мне такой результат:

%timeit df[index] = [index.isin(l) for l in df['list_of_codes']]

The slowest run took 26.21 times longer than the fastest. This could mean that an intermediate result is being cached.
1 loop, best of 3: 3.04 s per loop

Так что решение ansev работает в вашем случае.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...