Альтернативы np.vectorize для применения функций в pandas - PullRequest
0 голосов
/ 16 января 2020

Скажем, у меня есть такой фрейм данных:

    ID Candy Wrapper Error
0  001  None       1  None
1  002     1    None  None
2  003     1       1   040
3  004  None       1   040
4  005     1       1  None

Я хочу новый столбец, подобный этому:

    ID Candy Wrapper Error                 Reason to Follow Up
0  001  None       1  None    Matching candy not manufactured.
1  002     1    None  None  Matching wrapper not manufactured.
2  003     1       1   040                Factory malfunction.
3  004  None       1   040                Factory malfunction.
4  005     1       1  None                                None

Я всегда делал это с помощью np.vectorize:

def reason_to_follow_up(manufacture_candy, manufacture_wrapper, error):

    msg = []

    def candy_not_manufactured(manufacture_candy, manufacture_wrapper, error):
        if not manufacture_candy:
            if manufacture_wrapper:
                if not error:
                    msg.append("Matching candy not manufactured.")

    def wrapper_not_manufactured(manufacture_candy, manufacture_wrapper, error):
        if not manufacture_wrapper:
            if manufacture_candy:
                if not error:
                    msg.append("Matching wrapper not manufactured.")

    def specific_error(error):
        if error:
            if "40" in error:
                msg.append("Factory malfunction.")

    candy_not_manufactured(manufacture_candy, manufacture_wrapper, error)
    wrapper_not_manufactured(manufacture_candy, manufacture_wrapper, error)
    specific_error(error)

    if not msg:
        return None
    else:
        return ', '.join(msg)

df['Reason to Follow Up'] = np.vectorize(reason_to_follow_up)(df['Candy'],
                                                              df['Wrapper'],
                                                              df['Error'])

Мне нравится это по нескольким причинам:

  1. Мне нравится инкапсулировать все логи c в одну пользовательскую функцию, а затем запустить эту функцию. Я обнаружил, что могу подробно прокомментировать свою функцию, она более модульная, она как бы следует построителю формул в Excel, с которым знакомы мои товарищи по команде, и т. Д. c. Кроме того, я не знаком со многими встроенными функциями pandas, и мне сложно их найти (например, df. Вызовет много предложений автозаполнения).

  2. np.vectorize выглядит действительно чисто.

Есть ли способы применить мою пользовательскую функцию в a для l oop, даже не импортируя numpy вверху?

Или, может быть, с pandas .apply или .map?

Полный пример кода:

import pandas as pd
import numpy as np

data = {'ID': ['001','002','003', '004', '005'],
            'Candy': [None, 1, 1, None, 1],
            'Wrapper': [1, None, 1, 1, 1],
            'Error': [None, None, "040", "040", None]}

df = pd.DataFrame(data, dtype=str)

def reason_to_follow_up(manufacture_candy,manufacture_wrapper, error):

    msg = []

    def candy_not_manufactured(manufacture_candy,manufacture_wrapper, error):
        if not manufacture_candy:
            if manufacture_wrapper:
                if not error:
                    msg.append("Matching candy not manufactured.")

    def wrapper_not_manufactured(manufacture_candy,manufacture_wrapper, error):
        if not manufacture_wrapper:
            if manufacture_candy:
                if not error:
                    msg.append("Matching wrapper not manufactured.")

    def specific_error(error):
        if error:
            if "40" in error:
                msg.append("Factory malfunction.")

    candy_not_manufactured(manufacture_candy, manufacture_wrapper, error)
    wrapper_not_manufactured(manufacture_candy, manufacture_wrapper, error)
    specific_error(error)

    if not msg:
        return None
    else:
        return ', '.join(msg)

df['Reason to Follow Up'] = np.vectorize(reason_to_follow_up)(df['Candy'],
                                                              df['Wrapper'],
                                                              df['Error'])

Ответы [ 2 ]

1 голос
/ 16 января 2020

Я сомневаюсь, что вы можете использовать pandas карту; тем не менее, вы можете сделать это проще, используя numpy выберите: https://docs.scipy.org/doc/numpy/reference/generated/numpy.select.html

 data = {'ID': ['001','002','003', '004', '005'],
        'Candy': [None, 1, 1, None, 1],
        'Wrapper': [1, None, 1, 1, 1],
        'Error': [None, None, "040", "040", None]}

 df = pd.DataFrame(data, dtype=str)



cond_candy=    df['Candy'].isnull() & df['Wrapper'].notnull() & 
               df['Error'].isnull()

cond_wrapper=  df['Candy'].notnull() & df['Wrapper'].isnull() & 
               df['Error'].isnull()

cond_error=    df['Error'].isin(['040'])

cond_else=     df['Candy'].notnull() & df['Wrapper'].notnull() & 
               df['Error'].isnull()

condlist =   [cond_candy, cond_wrapper, cond_error, cond_else]
choicelist = ["Matching candy not manufactured.",
              "Matching wrapper not manufactured.",
              "Factory malfunction.",
              None
              ]

df['reason'] = np.select(condlist,choicelist)

df


    ID      Candy   Wrapper     Error      reason
0   001     None        1       None    Matching candy not manufactured.
1   002     1         None      None    Matching wrapper not manufactured.
2   003     1           1       040     Factory malfunction.
3   004     None        1       040     Factory malfunction.
4   005     1           1       None    None
0 голосов
/ 17 января 2020

Итак, я знал о генераторах списков, но не понимал, что могу на самом деле сжать несколько столбцов и, следовательно, использовать значения из каждого из них:

df['Reason to Follow Up'] = [reason_to_follow_up(manufacture_candy,manufacture_wrapper,error)
                                 for manufacture_candy, manufacture_wrapper, error
                                 in zip(df['Candy'], df['Wrapper'], df['Error'])]

Это сработало.

Я рассчитал это по отношению к np.vectorize, и это обычно примерно вдвое быстрее.

Однако я все еще думаю, что я бы использовал np.vectorize, потому что он все еще чище, и узкое место в моем коде обычно находится в визуализации данных. а не часть операций.

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