Агрегировать df с пользовательской функцией - PullRequest
0 голосов
/ 05 ноября 2019

У меня есть вопрос, касающийся объединения кадров данных pandas с пользовательскими функциями. Если у меня есть датафрейм и я работаю с группой или без нее, результат агрегируется при использовании встроенных функций. С другой стороны, если я использую пользовательскую функцию, она работает, как и предполагалось, когда используется groupby. Когда не используется групповой режим, агрегация не выполняется. Есть ли способ агрегирования без группировки и использования пользовательской функции? Я знаю, что можно просто добавить фиктивную переменную, но это не предпочтительное решение. Тест 1-3 работает как задумано, но не тест 4.

df = pd.DataFrame(columns=['a', 'b', 'c'])
n=1000
np.random.seed(0)

df['a'] = np.random.rand(n)
df['a'] = np.random.rand(n)
df['c'] = np.random.randint(1, 4, size=n)

def CoV(_s):
    return pd.Series({'CoV' : np.std(_s)/np.mean(_s)})

test1 = df.agg({'a':['std', np.mean]})
print(test1)

test2 = df.groupby(['c']).agg({'a':['std', np.mean]})
print(test2)

test3 = df.groupby(['c']).agg({'a':[CoV]})
print(test3)

# does not work as intended, no aggregation
test4 = df.agg({'a':[CoV]})
print(test4)

Ответы [ 3 ]

1 голос
/ 05 ноября 2019

Это даст вам желаемый результат:

df.assign(k=1).groupby('k')['a'].apply(CoV).reset_index(drop=True)

Таким образом, вы назначаете k, просто чтобы использовать его для groupby, а затем удалите его с помощью reseting и droping индекс.

1 голос
/ 05 ноября 2019

Попробуйте использовать .apply(): df.apply(CoV, axis=0)

Это также работает для меня: test4 = df.agg(CoV, axis=0)

То, что вы получите, - это кадр данных со скалярными результатами примененной функции:

            a         b         c
CoV  0.585977  0.584645  0.406688

Затем просто нарежьте нужную вам серию.

Допущения: Вы хотите применить пользовательскую скалярную функцию single (от ряда к скаляру) к различным столбцам без группировки.

Edit: Если вы хотите объединить несколько функций, вы можете также представить все их как выходные данные вашей функции (которая возвращает pd.Series). Например, вы можете переписать свою пользовательскую функцию следующим образом:

def myfunc(_s):
    return pd.Series({'mean': _s.mean(), 
                       'std': _s.std(), 
                       'CoV' : np.std(_s)/np.mean(_s)})

Затем выполнение этой функции с .apply() даст несколько результатов. df.apply(myfunc) теперь выдаст:

               a           b           c
mean    0.495922    0.511350    2.011000
std     0.290744    0.299108    0.818259
CoV     0.585977    0.584645    0.406688

Подробнее здесь: Панды, как применить несколько функций к фрейму данных

0 голосов
/ 05 ноября 2019

Ни один из ответов здесь не адресован , почему это терпит неудачу. Если вы покопаетесь в коде панд, когда UDF передается в df.agg, объект Series для каждого столбца будет передан в UDF.

В вашем случае при использовании словаря выбирается объект Series (столбец), а затем UDF передается в функцию Series.agg объекта Series. Поскольку это не известная функция (например, строка 'mean'), в итоге она передается в Series.apply, который отображает функцию на каждое значение в объекте Series. Это результат, который вы видите.

К счастью, передача UDF в Series.apply происходит в блоке try/except. Если он не работает с использованием Series.apply(func), он переходит к передаче объекта Series в функцию через func(Series). Вы можете использовать это, чтобы изменить ваш код, чтобы вызвать ошибку, если переданный объект не является Series или DataFrame.

def CoV(_s):
    if not isinstance(_s, (pd.Series, pd.DataFrame, np.array)):
        raise TypeError()
    return pd.Series({'CoV' : np.std(_s)/np.mean(_s)})

Теперь передача его в .agg работает так, как вы ожидаете. Это обходной обходной путь, но он работает.

df.agg({'a': CoV})
# returns:
            a
CoV  0.584645

РЕДАКТИРОВАТЬ:

Чтобы заставить его работать с другими функциями, такими как 'mean', вам нужно будет передать их какUDFs также, к сожалению. Хуже того, накопление результатов для UDF отличается от встроенных функций. Панды просто складывают их горизонтально с помощью иерархического индекса столбца. Простые stack и reset_index исправляют это.

def check_input(fn):
    def wrapper(_s, *args, **kwargs):
        if not isinstance(_s, (pd.Series, pd.DataFrame, np.array)):
            raise TypeError()
        return fn(_s, *args, **kwargs)
    wrapper.__name__ = fn.__name__
    return wrapper

@check_input
def Mean(_s):
    return pd.Series({'Mean': np.mean(_s)})

@check_input
def CoV(_s):
    return pd.Series({'CoV' : np.std(_s)/np.mean(_s)})

df.agg({'a': [CoV, Mean], 'c': Mean}).stack().reset_index(level=-1, drop=True)
# returns:
             a      c
CoV   0.584645    NaN
Mean  0.511350  2.011
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...