pandas, применять несколько функций из нескольких столбцов для группировки объекта

Я хочу применить несколько функций из нескольких столбцов к объекту groupby, что приводит к созданию нового pandas.DataFrame .

Я знаю, как это сделать на отдельных шагах:

 by_user = lasts.groupby('user') elapsed_days = by_user.apply(lambda x: (x.elapsed_time * x.num_cores).sum() / 86400) running_days = by_user.apply(lambda x: (x.running_time * x.num_cores).sum() / 86400) user_df = elapsed_days.to_frame('elapsed_days').join(running_days.to_frame('running_days')) 

В результате user_df : user_df

Однако я подозреваю, что есть лучший способ, например:

 by_user.agg({'elapsed_days': lambda x: (x.elapsed_time * x.num_cores).sum() / 86400, 'running_days': lambda x: (x.running_time * x.num_cores).sum() / 86400}) 

Однако это не работает, потому что agg() работает на pandas.Series .

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

6 Solutions collect form web for “pandas, применять несколько функций из нескольких столбцов для группировки объекта”

Я думаю, вы можете избежать agg или apply и, скорее, сначала несколько mul , затем div и last use groupby по index с sum :

 lasts = pd.DataFrame({'user':['a','s','d','d'], 'elapsed_time':[40000,50000,60000,90000], 'running_time':[30000,20000,30000,15000], 'num_cores':[7,8,9,4]}) print (lasts) elapsed_time num_cores running_time user 0 40000 7 30000 a 1 50000 8 20000 s 2 60000 9 30000 d 3 90000 4 15000 d 
 by_user = lasts.groupby('user') elapsed_days = by_user.apply(lambda x: (x.elapsed_time * x.num_cores).sum() / 86400) print (elapsed_days) running_days = by_user.apply(lambda x: (x.running_time * x.num_cores).sum() / 86400) user_df = elapsed_days.to_frame('elapsed_days').join(running_days.to_frame('running_days')) print (user_df) elapsed_days running_days user a 3.240741 2.430556 d 10.416667 3.819444 s 4.629630 1.851852 
 lasts = lasts.set_index('user') print (lasts[['elapsed_time','running_time']].mul(lasts['num_cores'], axis=0) .div(86400) .groupby(level=0) .sum()) elapsed_time running_time user a 3.240741 2.430556 d 10.416667 3.819444 s 4.629630 1.851852 

Еще одна твердая вариация решения заключается в том, чтобы сделать то, что @MaxU сделал с этим решением для аналогичного вопроса, и обернуть отдельные функции в серии Pandas , поэтому для reset_index() требуется только reset_index() .

Сначала определим функции для преобразований:

 def ed(group): return group.elapsed_time * group.num_cores).sum() / 86400 def rd(group): return group.running_time * group.num_cores).sum() / 86400 

Оберните их в серию, используя get_stats :

 def get_stats(group): return pd.Series({'elapsed_days': ed(group), 'running_days':rd(group)}) 

В заключение:

 lasts.groupby('user').apply(get_stats).reset_index() 

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

 import functools import pandas as pd #same data as other answer: lasts = pd.DataFrame({'user':['a','s','d','d'], 'elapsed_time':[40000,50000,60000,90000], 'running_time':[30000,20000,30000,15000], 'num_cores':[7,8,9,4]}) #define the desired lambda as a function: def myfunc(column, df, cores): return (column * df.ix[column.index][cores]).sum()/86400 #use the partial to define the function with a given column and df: mynewfunc = functools.partial(myfunc, df = lasts, cores = 'num_cores') #agg by the partial function lasts.groupby('user').agg({'elapsed_time':mynewfunc, 'running_time':mynewfunc}) 

Что дает нам:

  running_time elapsed_time user a 2.430556 3.240741 d 3.819444 10.416667 s 1.851852 4.629630 

Это не очень полезно для приведенного примера, но может быть более полезным в качестве общего примера.

Вот решение, которое очень напоминает оригинальную идею, выраженную в «Я подозреваю, что есть лучший способ».

Я буду использовать те же данные тестирования, что и другие ответы:

 lasts = pd.DataFrame({'user':['a','s','d','d'], 'elapsed_time':[40000,50000,60000,90000], 'running_time':[30000,20000,30000,15000], 'num_cores':[7,8,9,4]}) 

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

 def aggfunc(group): """ This function mirrors the OP's idea. Note the values below are lists """ return pd.DataFrame({'elapsed_days': [(group.elapsed_time * group.num_cores).sum() / 86400], 'running_days': [(group.running_time * group.num_cores).sum() / 86400]}) user_df = lasts.groupby('user').apply(aggfunc) 

Результат:

  elapsed_days running_days user a 0 3.240741 2.430556 d 0 10.416667 3.819444 s 0 4.629630 1.851852 

Второй заключается в том, что возвращаемый dataframe имеет иерархический индекс (этот столбец нулей), который может быть сплющен, как показано ниже:

 user_df.index = user_df.index.levels[0] 

Результат:

  elapsed_days running_days user a 3.240741 2.430556 d 10.416667 3.819444 s 4.629630 1.851852 

Эта функция agg может быть тем, что вы ищете.

Я добавил примерный набор данных и применил операцию к копии lasts_ которые я назвал lasts_ .

 import pandas as pd lasts = pd.DataFrame({'user' :['james','james','james','john','john'], 'elapsed_time':[ 200000, 400000, 300000,800000,900000], 'running_time':[ 100000, 100000, 200000,600000,700000], 'num_cores' :[ 4, 4, 4, 8, 8] }) # create temporary df to add columns to, without modifying original dataframe lasts_ = pd.Series.to_frame(lasts.loc[:,'user']) # using 'user' column to initialize copy of new dataframe. to_frame gives dataframe instead of series so more columns can be added below lasts_['elapsed_days'] = lasts.loc[:,'elapsed_time'] * lasts.loc[:,'num_cores'] / 86400 lasts_['running_days'] = lasts.loc[:,'running_time'] * lasts.loc[:,'num_cores'] / 86400 # aggregate by_user = lasts_.groupby('user').agg({'elapsed_days': 'sum', 'running_days': 'sum' }) # by_user: # user elapsed_days running_days # james 41.66666666666667 18.51851851851852 # john 157.4074074074074 120.37037037037037 

Если вы хотите сохранить «пользователь» как обычный столбец вместо столбца индекса, используйте:

 by_user = lasts_.groupby('user', as_index=False).agg({'elapsed_days': 'sum', 'running_days': 'sum'}) 

Чтобы использовать метод groupby объекта groupby с использованием данных из других столбцов одного и того же блока данных, вы можете сделать следующее:

  1. Определите свои функции ( lambda функции или нет), которые принимают в качестве входных данных Series , и получайте данные из других столбцов, используя df.loc[series.index, col] . В этом примере:

     ed = lambda x: (x * lasts.loc[x.index, "num_cores"]).sum() / 86400. rd = lambda x: (x * lasts.loc[x.index, "num_cores"]).sum() / 86400. 

    где lasts является основным DataFrame, и мы получаем доступ к данным в столбце num_cores благодаря методу .loc .

  2. Создайте словарь с этими функциями и имя для вновь созданных столбцов. Ключи – это имя столбцов, в которых должна применяться каждая функция, а значение – это другой словарь, где ключ – это имя функции, а значение – это функция.

     my_func = {"elapsed_time" : {"elapsed_day" : ed}, "running_time" : {"running_days" : rd}} 
  3. Группировка и совокупность:

     user_df = lasts.groupby("user").agg(my_func) user_df elapsed_time running_time elapsed_day running_days user a 3.240741 2.430556 d 10.416667 3.819444 s 4.629630 1.851852 
  4. Если вы хотите удалить старые имена столбцов:

      user_df.columns = user_df.columns.droplevel(0) user_df elapsed_day running_days user a 3.240741 2.430556 d 10.416667 3.819444 s 4.629630 1.851852 

НТН

  • Python PANDAS, измените одно значение на другое значение
  • Как пробовать массив numpy и эффективно выполнять вычисления по каждому образцу?
  • Как разбить столбец DataFrame на python
  • отключить кадр данных индекса pandas
  • Поиск нескольких строк в пандах без предопределения количества строк для использования
  • Как преобразовать результат SQL-запроса в структуру данных PANDAS?
  • эффективная матрица срочных документов с NLTK
  • Построение фрейма данных (панд) в pycharm, не отображающее
  •  
    Interesting Posts for Van-Lav

    Хранение и удаление тэгов и фильтров Django в моделях Django

    Поместите данные в список особого вида

    Что может изменить мое управляющее слово с плавающей запятой за моей спиной?

    Исключить скрытые теги при очистке с помощью b4

    Потоки двухстрочных графиков с использованием боке

    Используя Tornado, как мне обслуживать статические файлы и служить favicon.ico из другого каталога, чем статический путь?

    Эффективно выберите строки, которые соответствуют одному из нескольких значений в Pandas DataFrame

    pandas находят ряды среди рядов

    Почему time.sleep приостанавливает окно tkinter перед его открытием

    Ошибки Pygame2Exe, которые я не могу исправить

    Какова эквивалентность в Python 3 букв в Python 2?

    Pandas: выборка DataFrame

    Получить день недели / день недели для столбца Datetime в DataFrame

    эквивалент python '#define func ()' или как прокомментировать вызов функции в python

    Невозможно найти информацию о методе read () Python (python 2.7)

    Python - лучший язык программирования в мире.