Применение условия case when к столбцу фрейма данных

#python #pandas

#python #pandas

Вопрос:

Я пытаюсь воспроизвести оператор Case в моем скрипте python (с участием pandas), который применяется к фрейму данных и заполняет новый столбец в зависимости от того, как обрабатывается каждая строка, но кажется, что каждая строка попадает в условие else из-за того, что каждое значение в новом столбце является Other . Моя первая мысль заключается в том, что это соответствует any() условию, которое я использовал, но я чувствую, что могу использовать совершенно неправильный подход. Есть какие-либо рекомендации относительно направления, которое я должен предпринять?

Пример строк:

 index | source_name
1 | CLICK TO CALL - New Mexico
2 | Las Vegas Community Partner
3 | Facebook - Test Camp - Los Angeles
4 | Google - Test Camp - Los Angeles

index | landing_page_url
1 | NaN
2 | https://lp.example.com/fb/la/test/
3 | https://lp.example.com/fb/la/test/?utm_source=facebook
4 | https://lp.example.com/google/la/test/?utm_source=google
  

Критерии кода:

 # Criteria
fb_landing_page_crit = [
    'utm_source=facebook', 
    'fbclid',
    'test.com/fb/'
]
fb_source_crit = [
    'fb',
    'facebook'
]
google_landing_page_crit = [
    'gclid'
]
google_source_crit = [
    'click to call',
    'discovery',
    'call',
    'website',
    'landing page',
    'display - lp'
]
local_listings_source_crit = [
    'gmb'
]
partner_source_crit = [
    'vegas community',
    'new orleans community',
    'dc community',
]
  

Условный:

 def network_parse(df):
    if isinstance(df, str):
        if any(x in df['landing_page_url'] for x in fb_landing_page_crit):
            return 'Facebook'
        elif any(x in df['landing_page_url'] for x in google_landing_page_crit):
            return 'Google'
        elif any(x in df['source_name'] for x in fb_source_crit):
            return 'Facebook'
        elif any(x in df['source_name'] for x in google_source_crit):
            return 'Google'
        elif any(x in df['source_name'] for x in local_listings_source_crit):
            return 'Local Listings'
        elif any(x in df['source_name'] for x in partner_source_crit):
            return 'Partner - Community Partnership'
        else:
            return 'Other'
    else:
        return 'Other'
  

Вызов функции:

 df['network'] = df.apply(network_parse, axis=1) # Every row returns "Other"
  

Комментарии:

1. Похоже, что вы передаете серию в network_parse, а затем проверяете, является ли серия строкой. Это всегда будет false . В вашем обновленном решении вы вместо этого тестируете isinstance(df[‘landing_page_url’], str), который является строкой, если его NaN .

Ответ №1:

Я нашел лучший подход к проблеме. Вместо того, чтобы использовать методы contains, я решил запустить поиск по регулярным выражениям, чтобы узнать, найдены ли объединенные значения списка в строке столбца, и если они присутствуют, затем примените это значение. Ниже приведены мои обновления:

Списки:

 fb_landing_page_crit = [
    'utm_source=facebook', 
    'fbclid',
    'test.com/fb/'
]
fb_landing_page_regex = "|".join(fb_landing_page_crit)

google_landing_page_crit = [
    'gclid'
]
google_landing_page_regex = "|".join(google_landing_page_crit)

fb_source_crit = [
    'fb',
    'facebook'
]
fb_source_regex = "|".join(fb_source_crit)

google_source_crit = [
    'click to call',
    'discovery',
    'call',
    'website',
    'landing page',
    'display - lp'
]
google_source_regex = "|".join(google_source_crit)

local_listings_source_crit = [
    'gmb'
]
local_listings_source_regex = "|".join(local_listings_source_crit)

partner_source_crit = [
    'vegas community',
    'new orleans community',
    'dc community',
]
partner_source_regex =  "|".join(partner_source_crit)
  

Функция:

 def network_parse(df):
    if isinstance(df['landing_page_url'], str):
        if bool(re.search(fb_landing_page_regex,df['landing_page_url'].lower())) or bool(re.search(fb_source_regex,df['source_name'].lower())):
            return 'Facebook'
        if bool(re.search(google_landing_page_regex,df['landing_page_url'].lower())) or bool(re.search(google_source_regex,df['source_name'].lower())):
            return 'Google'
        if bool(re.search(local_listings_source_regex,df['source_name'].lower())):
            return 'Local Listings'
        if bool(re.search(partner_source_regex,df['source_name'].lower())):
            return 'Partner - Community Partnership'
        else:
            return 'Other'
    else:
        return 'Other'
  

Вызов функции:

 df['network'] = df.apply(network_parse, axis=1)
  

Ответ №2:

Прямо сейчас проблема заключается не any в этом, а в x in df['source_name'] части (я взял source_name , поскольку там проще объяснить). Вы проверяете, равна ли какая-либо строка фрейма данных (например) 'Google' , а не содержит ли она слово. Для достижения последнего вы можете вложить for операторы:

 ...
if any((x in y for y in df['landing_page_url']) for x in fb_landing_page_crit):
    return 'Facebook'
  

Однако я почти уверен, что это не самый элегантный и эффективный способ, поскольку он многократно повторяет один и тот же столбец, но для небольших фреймов данных это может быть нормально. В противном случае это может помочь вам найти более эффективное решение.

Редактировать: чтобы исследовать вашу проблему, вы можете запустить следующие два фрагмента, где первый дает False , а второй дает True :

 test = ['This', 'thought']

a = ['This is', 'a', 'longer Text than', 'I thought']
print(any(x in in a for x in test))  # This is principally what you coded


test2 = ['This', 'I thought']
print(any(x in a for x in test2)