Применить против преобразования объекта групповой


рассмотрим следующий фрейм данных:

     A      B         C         D
0  foo    one  0.162003  0.087469
1  bar    one -1.156319 -1.526272
2  foo    two  0.833892 -1.666304
3  bar  three -2.026673 -0.322057
4  foo    two  0.411452 -0.954371
5  bar    two  0.765878 -0.095968
6  foo    one -0.654890  0.678091
7  foo  three -1.789842 -1.130922

работают следующие команды:

> df.groupby('A').apply(lambda x: (x['C'] - x['D']))
> df.groupby('A').apply(lambda x: (x['C'] - x['D']).mean())

но ни одна из следующих работ:

> df.groupby('A').transform(lambda x: (x['C'] - x['D']))
ValueError: could not broadcast input array from shape (5) into shape (5,3)

> df.groupby('A').transform(lambda x: (x['C'] - x['D']).mean())
 TypeError: cannot concatenate a non-NDFrame object

почему?пример по документации похоже, что вызов transform в группе позволяет выполнять обработку операций по строкам:

# Note that the following suggests row-wise operation (x.mean is the column mean)
zscore = lambda x: (x - x.mean()) / x.std()
transformed = ts.groupby(key).transform(zscore)

другими словами, Я думал, что преобразование по существу является конкретным типом применения (тот, который не делает совокупный.) Где я ошибаюсь?

для справки, ниже приведена конструкция исходного фрейма данных выше:

df = pd.DataFrame({'A' : ['foo', 'bar', 'foo', 'bar',
                          'foo', 'bar', 'foo', 'foo'],
                   'B' : ['one', 'one', 'two', 'three',
                         'two', 'two', 'one', 'three'],
                   'C' : randn(8), 'D' : randn(8)})
2 77

2 ответа:

как я чувствовал себя так же путают с .transform операция против .apply я нашел несколько ответов пролить свет на этот вопрос. ответ например, было очень полезно.

мой вынос до сих пор это .transform будет работать (или заниматься) с Series (столбцы) в изоляции друг от друга. Это означает, что в ваших последних двух вызовах:

df.groupby('A').transform(lambda x: (x['C'] - x['D']))
df.groupby('A').transform(lambda x: (x['C'] - x['D']).mean())

Вы спрашивали .transform чтобы взять значения из двух столбцов и "это" на самом деле не делает видеть их обоих одновременно (так сказать). transform будет смотреть на столбцы dataframe один за другим и возвращать назад серию (или группу серий) "сделанных" из скаляров, которые повторяются len(input_column) раза.

Итак, этот скаляр, который должен использоваться .transform сделать Series является результатом некоторой функции сокращения, применяемой на входе Series (и только по одной серии/колонке за раз).

рассмотрим этот пример (на вашем таблицы данных):

zscore = lambda x: (x - x.mean()) / x.std() # Note that it does not reference anything outside of 'x' and for transform 'x' is one column.
df.groupby('A').transform(zscore)

даст:

       C      D
0  0.989  0.128
1 -0.478  0.489
2  0.889 -0.589
3 -0.671 -1.150
4  0.034 -0.285
5  1.149  0.662
6 -1.404 -0.907
7 -0.509  1.653

что точно так же, как если бы вы использовали его только на одном столбце одновременно:

df.groupby('A')['C'].transform(zscore)

урожайность:

0    0.989
1   -0.478
2    0.889
3   -0.671
4    0.034
5    1.149
6   -1.404
7   -0.509

отметим, что .apply в последнем примере (df.groupby('A')['C'].apply(zscore)) будет работать точно так же, но это не удастся, если вы попытаетесь использовать его на фрейме данных:

df.groupby('A').apply(zscore)

выдает ошибку:

ValueError: operands could not be broadcast together with shapes (6,) (2,)

так где же еще .transform полезное? Самый простой case пытается назначить результаты функции сокращения обратно в исходный фрейм данных.

df['sum_C'] = df.groupby('A')['C'].transform(sum)
df.sort('A') # to clearly see the scalar ('sum') applies to the whole column of the group

урожайность:

     A      B      C      D  sum_C
1  bar    one  1.998  0.593  3.973
3  bar  three  1.287 -0.639  3.973
5  bar    two  0.687 -1.027  3.973
4  foo    two  0.205  1.274  4.373
2  foo    two  0.128  0.924  4.373
6  foo    one  2.113 -0.516  4.373
7  foo  three  0.657 -1.179  4.373
0  foo    one  1.270  0.201  4.373

пробуем то же самое с .apply даст NaNs на sum_C. Потому что .apply вернул бы уменьшенный Series, который он не знает, как транслировать обратно:

df.groupby('A')['C'].apply(sum)

даем:

A
bar    3.973
foo    4.373

бывают также случаи, когда .transform используется для фильтрации данных:

df[df.groupby(['B'])['D'].transform(sum) < -1]

     A      B      C      D
3  bar  three  1.287 -0.639
7  foo  three  0.657 -1.179

я надеюсь, что это добавляет немного большая ясность.

два основных различия между apply и transform

есть два основных различия между transform и apply методы groupby.

  • apply неявно передает все столбцы для каждой группы как DataFrame к пользовательской функции, в то время как transform проходит каждый столбец для каждой группы как серия к пользовательской функции
  • пользовательская функция передается в apply может возвращать скалярное, или ряд или фрейм данных (или массив numpy или даже список). Пользовательская функция передается в transform должна возвращать последовательность (одномерный ряд, массив или список) той же длины, что и группа.

и transform работает только на одной серии за раз а apply работает на весь фрейм данных сразу.

проверка пользовательской функции

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

примеры

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

df = pd.DataFrame({'State':['Texas', 'Texas', 'Florida', 'Florida'], 
                   'a':[4,5,1,3], 'b':[6,10,3,11]})
df

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

def inspect(x):
    print(type(x))
    raise

теперь давайте передадим эту функцию как groupby apply и transform методы, чтобы увидеть, какой объект передается это:

df.groupby('State').apply(inspect)

<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.frame.DataFrame'>
RuntimeError

как вы можете видеть, фрейм данных передается в