Групповой Применить Пользовательские Функции Панд - PullRequest
2 голосов
/ 12 апреля 2019

Я пытаюсь применить пользовательскую функцию в пандах, аналогичную функциональности groupby и mutate в dplyr.

То, что я пытаюсь сделать, это сказать, что для данных панд дан такой кадр:

df = pd.DataFrame({'category1':['a','a','a', 'b', 'b','b'],
  'category2':['a', 'b', 'a', 'b', 'a', 'b'],
  'var1':np.random.randint(0,100,6),
  'var2':np.random.randint(0,100,6)}
)

df
  category1 category2  var1  var2
0         a         a    23    59
1         a         b    54    20
2         a         a    48    62
3         b         b    45    76
4         b         a    60    26
5         b         b    13    70

применить некоторую функцию, которая возвращает то же количество элементов, что и количество элементов в группе:

def myfunc(s):
  return [np.mean(s)] * len(s)

, чтобы получить этот результат

df
  category1 category2  var1  var2   var3
0         a         a    23    59   35.5
1         a         b    54    20   54
2         a         a    48    62   35.5
3         b         b    45    76   29
4         b         a    60    26   60
5         b         b    13    70   29

Я думалчто-то вроде:

df['var3'] = df.groupby(['category1', 'category2'], group_keys=False).apply(lambda x: myfunc(x.var1))

, но не удалось найти соответствующий индекс.

В R с dplyr это будет

df <- df %>%
  group_by(category1, category2) %>%
  mutate(
    var3 = myfunc(var1)
  )

Так что я смог решить ее с помощью пользовательской функции, такой как:

def myfunc_data(data):

  data['var3'] = myfunc(data.var1)
  return data

и

df = df.groupby(['category1', 'category2']).apply(myfunc_data)

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

Ответы [ 3 ]

2 голосов
/ 12 апреля 2019

Используйте GroupBy.transform для возврата Series с размером sime, таким как оригинал DataFrame, поэтому возможно присвоение новому столбцу:

np.random.seed(123)

df = pd.DataFrame({'category1':['a','a','a', 'b', 'b','b'],
  'category2':['a', 'b', 'a', 'b', 'a', 'b'],
  'var1':np.random.randint(0,100,6),
  'var2':np.random.randint(0,100,6)}
)

df['var3'] = df.groupby(['category1', 'category2'])['var1'].transform(myfunc)
print (df)
  category1 category2  var1  var2  var3
0         a         a    66    86    82
1         a         b    92    97    92
2         a         a    98    96    82
3         b         b    17    47    37
4         b         a    83    73    83
5         b         b    57    32    37

Альтернатива с lambda function:

df['var3'] = (df.groupby(['category1', 'category2'])['var1']
                .transform(lambda s: [np.mean(s)] * len(s)))
2 голосов
/ 12 апреля 2019

Попробуйте следующее решение:

df.loc[:,'var3'] = df.groupby(['category1', 'category2']).var1.transform(myfunc)
1 голос
/ 12 апреля 2019

Технически вы можете добиться этого, используя apply, который я добавлю здесь для полноты, но я бы порекомендовал использовать метод transform - он проще и быстрее.

Проблема, с которой вы столкнулись, заключалась в том, что вы возвращали несколько значений, которые при использовании apply дают список для каждой строки. Вместо этого вы можете возвращать только одно значение и полагаться на pandas, чтобы быть умным при сопоставлении этих значений (и дублировании при необходимости) при добавлении в новый столбец. Однако, чтобы это работало, у нас должен быть тот же индекс, что и у серии, возвращаемой из groupby / apply. Вот как это можно сделать (обратите внимание на модификацию myfunc):

import pandas as pd

def myfunc(s):
    return np.mean(s)

df = pd.DataFrame({'category1':['a','a','a', 'b', 'b','b'],
  'category2':['a', 'b', 'a', 'b', 'a', 'b'],
  'var1':np.random.randint(0,100,6),
  'var2':np.random.randint(0,100,6)}
)

df = (df.set_index(["category1", "category2"])
         .assign(var3=df.groupby(["category1", "category2"]).var1.apply(myfunc))
         .reset_index()
      )
df
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...