2015-10-23 3 views
0

Проблема: Учитывая приведенный ниже раздел данных, я пытаюсь придумать код, который будет применять функцию к трем отдельным столбцам без необходимости писать три отдельных вызова функций.Применение подобных функций для нескольких столбцов в python/pandas

Код для данных:

import pandas as pd 
data = {'name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 
    'days': [365, 365, 213, 318, 71], 
    'spend_30day': [22, 241.5, 0, 27321.05, 345], 
    'spend_90day': [22, 451.55, 64.32, 27321.05, 566.54], 
    'spend_365day': [854.56, 451.55, 211.65, 27321.05, 566.54]} 

df = pd.DataFrame(data) 
cols = df.columns.tolist() 
cols = ['name', 'days', 'spend_30day', 'spend_90day', 'spend_365day'] 
df = df[cols] 
df 

Функция ниже существенно пересчитывать на год тратить; если у кого-то меньше, чем, скажем, 365 дней в колонке «дней», следующая функция будет сказать мне, что расходы были бы, если бы они 365 дней:

def annualize_spend_365(row): 
    if row['days']/(float(365)) < 1: 
     return (row['spend_365day']/(row['days']/float(365))) 
    else: 
     return row['spend_365day'] 

Тогда я применить функцию к частному столбец:

df.spend_365day = df.apply(annualize_spend_365, axis=1).round(2) 
df 

Это работает точно так же, как я хочу, для этой колонки. Однако я не хочу переписывать это для каждой из трех разных столбцов «тратить» (30, 90, 365). Я хочу иметь возможность писать код, который будет обобщать и применять эту функцию к нескольким столбцам за один проход.

Я думал, что я мог бы создать списки столбцов и их соответствующие дни, используйте функцию «почтовый», и гнездо функции в цикле, но моя попытка ниже в конечном счете не:

spend_cols = [df.spend_30day, df.spend_90day, df.spend_365day] 
days_list = [30, 90, 365] 

for col, day in zip(spend_cols, days_list): 
    def annualize_spend(row): 
     if (row.days/(float(day)) < 1: 
      return (row.col)/((row.days)/float(day)) 
     else: 
      return row.col 
    col = df.apply(annualize_spend, axis = 1) 

Ошибка :

AttributeError: ("'Series' object has no attribute 'col'") 

Я не уверен, почему подход петли терпит неудачу. Независимо от того, я надеюсь получить рекомендации о том, как обобщать функцию приложения в пандах. Заранее спасибо!

ответ

0

Посмотрите на ваших двух определений функций:

def annualize_spend_365(row): 
    if row['days']/(float(365)) < 1: 
     return (row['spend_365day']/(row['days']/float(365))) 
    else: 
     return row['spend_365day'] 

и

#col in [df.spend_30day, df.spend_90day, df.spend_365day] 
def annualize_spend(row): 
    if (row.days/(float(day)) < 1: 
     return (row.col)/((row.days)/float(day)) 
    else: 
     return row.col 

разницу? С одной стороны, в первом случае вы получаете доступ к полям с явными именами полей, и это работает. Во втором случае вы пытаетесь получить доступ к row.col, что не удается, но в этом случае col принимает значения соответствующих полей в df. Вместо этого попробуйте

spend_cols = ['spend_30day', 'spend_90day', 'spend_365day'] 

перед вашей петлей. С другой стороны, в синтаксисе df.days имя поля фактически «дни», но в df.col имя поля не является строкой «col», но значение строки col. Поэтому в последнем случае вы можете использовать row[col]. И вообще, я не уверен, насколько разумно принимать col в качестве выходной переменной внутри вашей петли над col.


Я незнаком с pandas.DataFrame.apply, но это, вероятно, можно использовать одно определение функции, которая принимает число дней и поле интереса в качестве входных переменных:

def annualize_spend(col,day,row): 
    if (row['days']/(float(day)) < 1: 
     return (row[col])/((row['days'])/float(day)) 
    else: 
     return row[col] 

spend_cols = ['spend_30day', 'spend_90day', 'spend_365day'] 
days_list = [30, 90, 365] 

for col, day in zip(spend_cols, days_list): 
    col = df.apply(lambda row,col=col,day=day: annualize_spend(col,day,row), axis = 1) 

lambda будет убедитесь, что только один входной параметр вашей функции оборван, когда он получает apply d.

+0

Работали как очарование. Я решил, что моя проблема была концептуальной, поэтому благодарим за то, что мы изложили более согласованный план. Если у вас есть время, можете ли вы объяснить аргументы в лямбда-функции (строка, col = col, day = day)? –

+0

@UncleMilton sure :) Лямбда может использовать только переменные, которые явно переданы ей, поэтому вы передаете ей 'col' и' day'. Это ленивая вещь, чтобы назвать параметры лямбда таким образом, вероятно, это было бы яснее: 'lambda var1, var2 = col, var3 = day: annualize_spend (var2, var3, var1)'. Таким образом, вы устанавливаете значения по умолчанию для * последних * двух параметров лямбда, тем самым эффективно предоставляя ему функцию с одним входом для 'apply'. Поскольку это только значения по умолчанию, лямбда также может работать в синтаксисе 2 или 3 входа, но 'apply' использует только одну переменную, поэтому она должна иметь не более 1 параметра, отличного от значения по умолчанию. –

+0

: Ничего себе, спасибо за объяснение. Еще один (общий) вопрос: зачем передавать лямбду в функцию apply вместо самой функции? Вот почему df.apply (lambda row, col = col, day = day: annualize_spend (col, day, row) вместо просто df.apply (annualize_spend)? Какая эффективность/ценность достигается за счет использования лямбда-функциональности, когда функция уже был создан? (Я видел этот подход для гораздо более простых функций, и было любопытно, почему обращение к лямбда было необходимо, когда функция уже была создана). Еще раз спасибо, самое полезное! –

Смежные вопросы