панды: используйте value_counts в функции apply

#python #pandas #apply

Вопрос:

Вот игрушечный пример фрейма данных my pandas:

     country_market  language_market
0   United States   English
1   United States   French
2   Not used    Not used
3   Canada OR United States English
4   Germany English
5   United Kingdom  French
6   United States   German
7   United Kingdom  English
8   United Kingdom  English
9   Not used    Not used
10  United States   French
11  United States   English
12  United Kingdom  English
13  United States   French
14  Not used    English
15  Not used    English
16  United States   French
17  United States   Not used
18  Not used    English
19  United States   German
 

Я хочу добавить столбец top_country , который показывает, является ли значение в country_market одной из двух наиболее часто встречающихся стран в данных. Если это так, я хочу, чтобы в новом top_country столбце отображалось значение, country_market а если нет, то я хочу, чтобы в нем отображалось «Другое». Я хочу повторить этот процесс для language_market (и целого ряда других рыночных столбцов, которые я здесь не показываю).

Вот как я хотел бы, чтобы данные выглядели после обработки:

     country_market  language_market top_country top_language
0   United States   English United States   English
1   United States   French  United States   French
2   Not used    Not used    Not used    Other
3   Canada OR United States English Other   English
4   Germany English Other   English
5   United Kingdom  French  Other   French
6   United States   German  United States   Other
7   United Kingdom  English Other   English
8   United Kingdom  English Other   English
9   Not used    Not used    Not used    Other
10  United States   French  United States   French
11  United States   English United States   English
12  United Kingdom  English Other   English
13  United States   French  United States   French
14  Not used    English Not used    English
15  Not used    English Not used    English
16  United States   French  United States   French
17  United States   Not used    United States   Other
18  Not used    English Not used    English
19  United States   German  United States   Other
 

Я создал функцию original_top_markets_function для этого, но я не мог понять, как передать value_counts часть моей функции пандам apply . Я продолжал получать AttributeError: 'str' object has no attribute 'value_counts' .

 def original_top_markets_function(x):
top2 = x.value_counts().nlargest(2).index
for i in x:
    if i in top2: 
        return i
    else: 
        return 'Other'         
 

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

Поэтому я придумал эту top_markets функцию в качестве решения, используя список, который делает то, что я хочу, но не очень эффективен. Мне нужно будет применить эту функцию ко множеству различных рыночных столбцов, поэтому я хотел бы что-то более питоническое.

 def top_markets(x):
top2 = x.value_counts().nlargest(2).index
results = []
for i in x:
    if i in top2: 
        results.append(i)
    else: 
        results.append('Other')         
return results
 

Вот воспроизводимый пример. Пожалуйста, не могли бы вы как-нибудь помочь мне исправить мою top_markets функцию, чтобы я мог ее использовать apply ?

 import pandas as pd

d = {0: {'country_market': 'United States', 'language_market': 'English'},
 1: {'country_market': 'United States', 'language_market': 'French'},
 2: {'country_market': 'Not used', 'language_market': 'Not used'},
 3: {'country_market': 'Canada OR United States',
  'language_market': 'English'},
 4: {'country_market': 'Germany', 'language_market': 'English'},
 5: {'country_market': 'United Kingdom', 'language_market': 'French'},
 6: {'country_market': 'United States', 'language_market': 'German'},
 7: {'country_market': 'United Kingdom', 'language_market': 'English'},
 8: {'country_market': 'United Kingdom', 'language_market': 'English'},
 9: {'country_market': 'Not used', 'language_market': 'Not used'},
 10: {'country_market': 'United States', 'language_market': 'French'},
 11: {'country_market': 'United States', 'language_market': 'English'},
 12: {'country_market': 'United Kingdom', 'language_market': 'English'},
 13: {'country_market': 'United States', 'language_market': 'French'},
 14: {'country_market': 'Not used', 'language_market': 'English'},
 15: {'country_market': 'Not used', 'language_market': 'English'},
 16: {'country_market': 'United States', 'language_market': 'French'},
 17: {'country_market': 'United States', 'language_market': 'Not used'},
 18: {'country_market': 'Not used', 'language_market': 'English'},
 19: {'country_market': 'United States', 'language_market': 'German'}}

df = pd.DataFrame.from_dict(d, orient='index')

def top_markets(x):
    top2 = x.value_counts().nlargest(2).index
    results = []
    for i in x:
        if i in top2: 
            results.append(i)
        else: 
            results.append('Other')         
    return results

df['top_country'] = top_markets(df['country_market'])
df['top_language'] = top_markets(df['language_market'])

df

 

Ответ №1:

Если необходимо работать с несколькими столбцами DataFrame.apply в какой-либо функции, например, здесь lambda function используйте:

 cols = ['language_market', 'country_market']

f = lambda x: np.where(x.isin(x.value_counts().nlargest(2).index), x, 'Other')
df = df.join(df[cols].apply(f).add_prefix('total_'))
 

Решение без лямбда-функции:

 def top_markets(x):
    return np.where(x.isin(x.value_counts().nlargest(2).index), x, 'Other')

df = df.join(df[cols].apply(top_markets).add_prefix('total_'))
 

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

1. Идеально, делает то, что мне нужно, и я могу легко использовать это для 30 столбцов, которые мне нужно преобразовать таким образом, спасибо!

Ответ №2:

Я думаю, что вы можете просто использовать:

 df['top_country'] = np.where(df['country_market'].isin(df['country_market'].value_counts().nlargest(2).index), df['country_market'], 'Other')
df['top_language'] = np.where(df['language_market'].isin(df['language_market'].value_counts().nlargest(2).index), df['language_market'], 'Other')
 

Если вы хотите использовать свою собственную функцию, вы можете использовать:

 df['top_country'] = df[['country_market']].apply(top_markets)
df['top_language'] = df[['language_market']].apply(top_markets)

#OR
df[['top_country', 'top_language']] = df[['country_market', 'language_market']].apply(top_markets)
 

Редактировать в соответствии с обсуждением в комментариях:

 def top_markets(x, top):
    if x in top:
        return x
    else:
        'Other'

top_country = df['country_market'].value_counts().nlargest(2).index
top_languages = df['language_market'].value_counts().nlargest(2).index

df['top_country'] = df['country_market'].apply(lambda x: top_markets(x, top_country))
df['top_language'] = df['language_market'].apply(lambda x: top_markets(x, top_languages))
 

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

1. Отлично, это действительно дает мне то, что я ищу! Но, в более общем плане, есть ли способ использовать функцию value_counts как часть функции, которую я хочу передать apply ?

2. Я думаю, что это более быстрый способ сделать это, чем использование пользовательской функции

3. Да, я не возражаю против скорости np.where , но для моего собственного обучения я хотел бы знать, как использовать что-то вроде value_counts функции, к которой я перехожу apply .

4. @meenaparam — проверьте другой ответ.

5. Проверьте отредактированный ответ, если вы хотите использовать пользовательскую функцию