Как структурировать векторизованную функцию с пандами?

avatar
harmonica141
8 августа 2021 в 16:22
86
2
2

Я не знаю, как структурировать функцию, которую я хочу векторизовать в pandas.

У меня есть два таких df:

contents = pd.DataFrame({
'Items': [1, 2, 3, 1, 1, 2],
})

cats = pd.DataFrame({
'Cat1': ['1|2|4'],
'Cat2': ['3|2|5'],
'Cat3': ['6|9|11'],
})

Моей целью является .insert новый столбец для contents, который в каждой строке будет либо 1, если contents['Items'] является элементом cats['cat1'], либо 0 в противном случае. Это должно быть повторено по cat.

Формат цели:

contents = pd.DataFrame({
'Items': [1, 2, 3, 1, 1, 2],
'contains_Cat1': [1, 1, 0, 1, 1, 1],
'contains_Cat2': [0, 1, 1, 0, 0, 1],
'contains_Cat3': [0, 0, 0, 0, 0, 0],
})

Поскольку мое содержимое df большое(!) я хотел бы его векторизовать. Мой подход к каждой кошке состоит в том, чтобы сделать что-то вроде этого

contents.insert(
    loc=len(contents.columns),
    column='contains_Cat1',
    value=has_content(contents, cats['Cat1'])

def has_content(contents: pd.DataFrame, cat: pd.Series) -> pd.Series:
    # Initialization of pd.Series here??
    if contents['Items'] in cat:
        return True
    else:
        return False

Мой вопрос: как мне структурировать мой has_content(...)? Особенно мне непонятно, как я инициализирую этот pd.Series, чтобы он содержал все значения False. Мне даже нужно? После этого я знаю, как проверить, содержится ли что-то в чем-то другом. Но могу ли я сделать это по столбцам, как указано выше, и сразу вернуться, не переходя на ячейки?

Источник

Ответы (2)

avatar
Pygirl
8 августа 2021 в 16:34
3

Простой метод:

contents = (contents.join([pd.Series(contents.Items.astype(str).
                                     str.contains(cats[c][0]).astype(int), 
                                     name="Contains_"+c) for c in cats]))

содержание:

    Items   contains_Cat1   contains_Cat2   contains_Cat3
0   1       1               0               0
1   2       1               1               0
2   3       0               1               0
3   1       1               0               0
4   1       1               0               0
5   2       1               1               0

Сравнение времени:

%%timeit -n 2000
(contents.join([pd.Series(contents.Items.astype(str).
                                     str.contains(cats[c][0]).astype(int), 
                                     name="Contains_"+c) for c in cats]))

3,01 мс ± 344 мкс на цикл (среднее значение ± стандартное отклонение для 7 запусков, по 2000 циклов в каждом)


%%timeit -n 2000
cats.stack().str.get_dummies().stack()\
          .unstack(level=1).reset_index(level=0,drop=True)\
           .reindex(contents.Items.astype(str))

5,13 мс ± 584 мкс на цикл (среднее значение ± стандартное отклонение для 7 запусков, по 2000 циклов в каждом)


%%timeit -n 2000
cats.stack().str.get_dummies().droplevel(0).T\
        .add_prefix('contains_').reindex(contents['Items'].astype(str)).reset_index()

4,58 мс ± 512 мкс на цикл (среднее значение ± стандартное отклонение для 7 запусков, по 2000 циклов в каждом)

avatar
BENY
8 августа 2021 в 16:30
5

Попробуйте с str.get_dummies, затем измените форму с помощью stack и unstack

out = cats.stack().str.get_dummies().stack()\
          .unstack(level=1).reset_index(level=0,drop=True)\
           .reindex(contents.Items.astype(str))
Out[229]: 
       Cat1  Cat2  Cat3
Items                  
1         1     0     0
2         1     1     0
3         0     1     0
1         1     0     0
1         1     0     0
2         1     1     0

Улучшение:

out=cats.stack().str.get_dummies().droplevel(0).T\
        .add_prefix('contains_').reindex(contents['Items'].astype(str)).reset_index()

Out[230]: 

    Items   contains_Cat1   contains_Cat2   contains_Cat3
0   1       1               0               0
1   2       1               1               0
2   3       0               1               0
3   1       1               0               0
4   1       1               0               0
5   2       1               1               0
harmonica141
8 августа 2021 в 17:30
1

Просто чтобы уточнить: я получаю здесь df, верно? Итак, я вообще пропускаю .insert() и отдельную функцию?