Как построить вложенную условную логику в фрейме данных Pandas?

#python #pandas #bernoulli-probability

#python #pandas #бернулли-вероятность

Вопрос:

У меня есть фрейм данных, где каждая строка содержит список строк. Я написал функцию, которая выполняет испытание типа Бернулли для каждой строки, где с некоторой вероятностью (здесь 0,5) каждое слово удаляется, если испытание прошло успешно. Смотрите ниже:

 import numpy as np
import pandas as pd

def bernoulli_trial (sublist, prob = 0.5):

    # create mask of trial outcomes per each object in sublist
    mask = np.random.binomial(n=1, p=prob, size=len(sublist))

    # perform transformation on bernoulli successes
    transformed_sublist = [token for delete, token in zip(mask, sublist) if not delete]

    return transformed_sublist
 

Это работает, как и ожидалось, когда я передаю каждую строку фрейма данных, согласно:

 df = pd.DataFrame(data={'store': [1,2,3], 'colours': [['red','blue','yellow','green','brown','pink'],
                                                      ['black','white'],
                                                      ['purple','orange','cyan','mauve']]})

df['colours'] = df['colours'].apply(bernoulli_trial)

Out: 
0      [red, green]
1           [black]
2    [orange, cyan]
Name: colours, dtype: object
 

Однако вместо того, чтобы применять функцию равномерно по каждому подсписку и для каждой строки, я теперь хочу применить условия для (а) будет ли данный подсписок передан функции (да / нет), и (б) какие строки в этом подсписке будут применены (т. Е. С помощьюуказав, что я хочу тестировать только определенные цвета).

Я думаю, что у меня есть рабочее решение для части (a) — путем обертывания функции Бернулли внутри функции, которая проверяет, выполнено ли заданное условие (т. Е. Длина подсписка больше 2 объектов?) — это работает (см. Ниже), но я не уверен, есть ли более эффективный (подробнее pythonic) способ сделать это.

 def sublist_condition_check(sublist):
    if len(sublist) > 2:
        sublist = bernoulli_trial(sublist)
    else:
        sublist = sublist
    return sublist
 

Обратите внимание, что любые подсписки, которые не соответствуют условию, должны оставаться неизменными.

 df['colours'].apply(sublist_condition_check)

Out: 
0      [red, brown]
1    [black, white] # this sublist had only two elements so remains unchanged
2           [mauve]
Name: colours, dtype: object
 

Тем не менее, я полностью зациклен на том, как применять условную логику к каждому слову. Скажем, например, я хотел применить пробную версию только к заранее заданному списку цветов [‘red’, ‘mauve’,’black’] — при условии прохождения проверки состояния подсписка — как я мог это сделать?

Псевдокод для того, чего я надеюсь достичь, будет примерно следующим:

 for sublist in df:
    if len(sublist) > 2:     # check if sublist contains more than two objects
        for colour in sublist:     # cycle through each colour within the sublist
            if colour in ['red','mauve','black']:     
                colour = bernoulli_trial (colour)     # only run bernoulli if colour in list
            else:
                colour = colour     # if colour not in list, colour remains unchanged
        else:
            sublist = sublist     # if sublist <= 2, sublist remains unchanged
 

Я знаю, что буквальная интерпретация этого не сработает, поскольку начальная функция bernoulli_trial получает список, а не отдельную строку. Но, надеюсь, это описывает то, чего я хочу достичь.

Ответ №1:

Не уверен в этикете, касающемся ответа на мой собственный вопрос, но подумал, что я бы предоставил некоторые подробности рабочего решения, которое я определил, на случай, если кто-нибудь столкнется с подобной ситуацией.

Я расширил начальную функцию Бернулли, включив в нее дополнительный оператор if в зависимости от того, соответствует ли каждая строка критериям включения.

 # internal function - bernoulli trial for each string in sublist
def bernoulli_trial (sublist, prob = 0.50):

    # set token criteria for performing bernoulli trial
    token_criteria = ['red','black','purple'] # perform trial only on these strings

    # create mask of trial outcomes per each word in sublist
    mask = np.random.binomial(n=1, p=prob, size=len(turn))

    # perform transformation (deletion) on bernoulli successes
    transformed_turn = []
    for token, delete in zip(turn, mask):             
        if token not in token_criteria:
            transformed_turn.append(token)
        else:
            if delete == 0: # retain only those strings not marked for deletion
                transformed_turn.append(token)

    return transformed_sublist
 

В сочетании с sublist_condition_check функцией, описанной в вопросе, это теперь работает так, как ожидалось