Создайте столбец в кадре данных, используя лямбду, на основе других столбцов с ненулевыми значениями - PullRequest
0 голосов
/ 08 января 2019

У меня есть датафрейм с названиями фильмов и столбцами с жанрами. Например, фильм с заголовком «Один» - это «Действие» и «Вестерн», потому что в соответствующих столбцах есть «1».

   Movie  Action  Fantasy  Vestern
0    One       1        0        1
1    Two       0        0        1
2  Three       1        1        0

Моя цель - создать столбец genres, который будет содержать названия всех жанров, которые есть у данного фильма. Для этого я пытался использовать lambda и list comprehension, потому что думал, что это помогает. Но после запуска запускается такая строка кода как:

df['genres'] = df.apply(lambda x: [x+"|"+x for x in df.columns if x!=0])

Я получил только NaN значение в каждой строке:

   Movie  Action  Fantasy  Vestern genres
0    One       1        0        1    NaN
1    Two       0        0        1    NaN
2  Three       1        1        0    NaN

Также пытался использовать groupby, но безуспешно.

Ожидаемый результат:

   Movie  Action  Fantasy  Vestern          genres
0    One       1        0        1  Action|Vestern
1    Two       0        0        1         Vestern
2  Three       1        1        0  Action|Fantasy

Код для воспроизведения:

import pandas as pd
import numpy as np

df = pd.DataFrame({"Movie":['One','Two','Three'],
                   "Action":[1,0,1],
                   "Fantasy":[0,0,1],
                   "Vestern":[1,1,0]})
print(df)

Спасибо за вашу помощь

Ответы [ 2 ]

0 голосов
/ 08 января 2019
import pandas as pd
import numpy as np

df = pd.DataFrame({"Movie":['One','Two','Three'],
                   "Action":[1,0,1],
                   "Fantasy":[0,0,1],
                   "Vestern":[1,1,0]})

cols = df.columns.tolist()[1:]

df['genres'] = df.apply(lambda x: "|".join(str(z) for z in [i for i in cols if x[i] !=0]) ,axis=1)
print(df)

выход

Movie  Action  Fantasy  Vestern          genres
0    One       1        0        1  Action|Vestern
1    Two       0        0        1         Vestern
2  Three       1        1        0  Action|Fantasy
0 голосов
/ 08 января 2019

Для повышения производительности возможно использовать dot все столбцы без первого со всеми столбцами без последнего с separator, последнее удаление последним | на rstrip:

df['new'] = df.iloc[:, 1:].dot(df.columns[1:] + '|').str.rstrip('|')
print (df)
   Movie  Action  Fantasy  Vestern             new
0    One       1        0        1  Action|Vestern
1    Two       0        0        1         Vestern
2  Three       1        1        0  Action|Fantasy

Или использовать списочные выражения для объединения всех значений без пустых строк:

arr = df.iloc[:, 1:].values * df.columns[1:].values
df['new'] = ['|'.join(y for y in x if y) for x in arr]
print (df)
   Movie  Action  Fantasy  Vestern             new
0    One       1        0        1  Action|Vestern
1    Two       0        0        1         Vestern
2  Three       1        1        0  Action|Fantasy

Производительность

In [54]: %timeit (jez1(df.copy()))
25.2 ms ± 2.31 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [55]: %timeit (jez2(df.copy()))
61.4 ms ± 769 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [56]: %timeit (csm(df.copy()))
1.46 s ± 35.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)



df = pd.DataFrame({"Movie":['One','Two','Three'],
                   "Action":[1,0,1],
                   "Fantasy":[0,0,1],
                   "Vestern":[1,1,0]})
#print(df)

#30k rows
df = pd.concat([df] * 10000, ignore_index=True)

def csm(df):
    cols = df.columns.tolist()[1:]
    df['genres'] = df.apply(lambda x: "|".join(str(z) for z in [i for i in cols if x[i] !=0]) ,axis=1)
    return df

def jez1(df):
    df['new'] = df.iloc[:, 1:].dot(df.columns[1:] + '|').str.rstrip('|')
    return df

def jez2(df):
    arr = df.iloc[:, 1:].values * df.columns[1:].values
    df['new'] = ['|'.join(y for y in x if y) for x in arr]
    return df
...