Функция Pandas groupby-transform()

Pandas 常用函数Pandas: общие функции


groupby.transform()Это функция в Pandas для преобразования данных после группировки. В отличие от агрегирующих функций,агрегация возвращает сводный результат для каждой группы, тогда как преобразование возвращает результат той же длины, что и исходные данные.。

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


Базовый синтаксис и параметры

transform()Это функция-член объекта GroupBy. Сначала нужно использоватьgroupby()группировку, прежде чем вызывать его.

Формат синтаксиса

GroupBy.transform(func, axis=0, *args, engine=None, engine_kwargs=None, **kwargs)

Описание параметра

Параметр Type Описание Значение по умолчанию
func str, list, dict или callable Функция преобразования. Это может быть имя встроенной функции (например, 'mean', 'sum', 'std'), список функций, словарь, задающий разные функции для каждого столбца, или пользовательская функция. Отсутствует
axis int Направление оси для применения преобразования. 0 означает по строкам (измерение группировки), 1 означает по столбцам. 0
args tuple Дополнительные позиционные аргументы, передаваемые функции преобразования. ()
engine str Указывает вычислительный движок, который может быть 'cython' или 'numba'. None означает, что Pandas автоматически выбирает его. Отсутствует
engine_kwargs dict Словарь дополнительных параметров, передаваемых в базовый движок. Отсутствует

Возвращаемое значение

  • Тип возвращаемого значения:SeriesилиDataFrame
  • Описание: Возвращает результат с тем же числом строк, что и исходные данные. Каждая строка исходных данных получает результат вычисления для соответствующей ей группы.

Примеры

Давайте подробно разберём на серии примеров от простого к сложному,groupby.transform()использование.

Пример 1: Базовое использование — вычисление среднего значения по группе

transform()Самое базовое использование — вычислить среднее значение каждой группы, а затем транслировать результат обратно в каждую строку.

Пример

import pandas as pd

# Создаём данные с оценками студентов
data = {
    'Имя': ['Чжан Сань', 'Ли Сы', 'Ван У', 'Чжао Лю', Сунь Ци, Чжоу Ба, У Цзю, Чжэн Ши],
    Класс: ['A', 'A', 'A', 'B', 'B', 'B', 'B', 'A'],
    Китайский: [85, 92, 78, 88, 95, 82, 90, 87],
    Математика: [90, 85, 92, 78, 88, 91, 85, 89]
}

# Создать DataFrame
df = pd.DataFrame(data)

print("Данные об оценках студентов:")
print(df)
print()

# Вычисляем среднее по математике для каждого класса, затем транслируем в каждую строку
df['Class Math Average'] = df.groupby(Class)[Mathematics].transform('mean')

# Вычислить разницу между баллом каждого ученика и средним по классу
df['Difference from average score'] = df[Mathematics] - df['Class Math Average']

print("Данные после добавления среднего по классу и разницы:")
print(df)
print()

# Вычисляем среднее по классу одновременно для нескольких столбцов
df[['Class Chinese Average', 'Class Math Average 2']] = df.groupby(Class)[[Chinese, Mathematics]].transform('mean')

print(Данные после добавления средних значений по нескольким столбцам:)
print(df)

Результат выполнения:

学生成绩数据:
   姓名  班级  语文  数学
0  张三   A   85   90
1  李四   A   92   85
2  王五   A   78   92
3  赵六   B   88   78
4  孙七   B   95   88
5  周八   B   82   91
6  吴九   B   90   85
7  郑十   A   87   89

添加班级平均分和差值后的数据:
   姓名  班级   语文   数学   班级数学平均分   与平均分差值
0  张三   A   85   90  89.000000   1.000000
1  李四   A   92   85  89.000000  -4.000000
2  王五   A   78   92  89.000000   3.000000
3  赵六   B   88   78  85.666667   -7.666667
4  孙七   B   95   88  85.666667   2.333333
5  周八   B   82   91  85.666667   5.333333
6  吴九   B   90   85  85.666667   -0.666667
7  郑十   A   87   89  89.000000   0.000000

添加多列平均值后的数据:
   姓名  班级   语文   数学  班级语文平均分  班级数学平均分2
0  张三   A   85   90  85.500000         89.0
1  李四   A   92   85  85.500000         89.0
2  王五   A   78   92  85.500000         89.0
3  赵六   B   88   78  88.750000         85.5
4  孙七   B   95   88  88.750000         85.5
5  周八   B   82   91  88.750000         85.5
6  吴九   B   90   85  88.750000         85.5
7  郑十   A   87   89  85.500000         89.0

Пояснение к коду:

  1. transform('mean')Вычислите среднее арифметическое по математике для каждого класса.
  2. Поскольку Чжан Сань, Ли Сы, Ван У и Чжэн Ши учатся в классе A, полученное ими «Среднее по математике в классе» — это среднее арифметическое оценок по математике класса A.
  3. Длина возвращаемого значения совпадает с количеством строк в исходных данных; это ключевое различие между transform и agg.

Пример 2: Стандартизация внутри группы (Z-оценка)

transform()Он часто используется для вычисления стандартизированных значений внутри группы, что очень полезно при сравнении относительного положения отдельных наблюдений внутри группы.

Пример

import pandas as pd

# Создание данных о зарплате сотрудников
data = {
    'Имя': ['Чжан Сань', 'Ли Сы', 'Ван У', 'Чжао Лю', Сунь Ци, Чжоу Ба, У Цзю, Чжэн Ши],
    'Department': [Sales, Sales, Sales, 'Technology', 'Technology', 'Technology', Administration, Administration],
    'salary': [5000, 8000, 6000, 9000, 12000, 10000, 4500, 7000]
}
df = pd.DataFrame(data)

print("Данные о зарплате сотрудников:")
print(df)
print()

# Вычислите Z-оценку (стандартизированную оценку) зарплаты каждого сотрудника в отделе
# Z-оценка = (исходное значение - среднее внутри группы) / стандартное отклонение внутри группы
df['Department average salary'] = df.groupby('Department')['salary'].transform('mean')
df['Department Salary Standard Deviation'] = df.groupby('Department')['salary'].transform('std')
df['Z-Score'] = (df['salary'] - df['Department average salary']) / df['Department Salary Standard Deviation']

print(Данные после добавления статистики по отделу и Z-оценки:)
print(df)
print()

# Вычислить Z-оценку сразу с помощью пользовательской функции
def z_score(x):
    """Вычислить Z-оценку"""
    return (x - x.mean()) / x.std()

df['Z-Score2'] = df.groupby('Department')['salary'].transform(z_score)

print(Z-оценка, рассчитанная с помощью пользовательской функции:)
print(df[['Name', 'Department', 'salary', 'Z-Score2']])

Результат выполнения:

员工工资数据:
  姓名  部门    工资
0  张三   销售   5000
1  李四   销售   8000
2  王五   销售   6000
3  赵六   技术   9000
4  孙七   技术  12000
5  周八   技术  10000
6  吴九   行政   4500
7  郑十   行政   7000

添加部门统计和Z-Score后的数据:
   姓名   部门    工资    部门平均工资   部门工资标准差    Z-Score
0  张三   销售  5000  6333.333333  1525.755   -0.872871
1  李四   销售  8000  6333.333333  1525.755    1.092091
2  王五   销售  6000  6333.333333  1525.755   -0.218218
3  赵六   技术  9000 10333.333333  1525.755   -0.872871
4  孙七   技术 12000 10333.333333  1525.755    1.092091
5  周八   技术 10000 10333.333333  1525.755   -0.218218
6  吴九   行政   4500  5750.000000  1767.767   -0.707107
7  郑十   行政   7000  5750.000000  1767.767    0.707107

使用自定义函数计算的 Z-Score:
   姓名  部门    工资   Z-Score2
0  张三   销售  5000  -0.872871
1  李四   销售  8000   1.092091
2  王五   销售  6000  -0.218218
3  赵六   技术  9000  -0.872871
4  孙七   技术 12000   1.092091
5  周八   技术 10000  -0.218218
6  吴九   行政  4500  -0.707107
7  郑十   行政  7000   0.707107

Пояснение к коду:

  • Z-оценка указывает количество стандартных отклонений исходного значения от среднего. Положительное значение означает, что значение выше среднего, а отрицательное — ниже среднего.
  • С помощью Z-оценки можно устранить влияние различий в уровне зарплат между отделами, что позволяет сравнивать относительное положение сотрудников внутри их отделов.
  • Z-оценка Чжан Саня равна -0.87, что означает, что его зарплата примерно на 0.87 стандартного отклонения ниже среднего уровня отдела продаж.

Пример 3: Ранжирование внутри группы и процентиль

transform()Его также можно использовать для расчёта рейтингов внутри группы, что очень полезно при сортировке и конкурентном анализе.

Пример

import pandas as pd
import numpy as np

# Создаём данные с оценками студентов
data = {
    'Имя': ['Чжан Сань', 'Ли Сы', 'Ван У', 'Чжао Лю', Сунь Ци, Чжоу Ба, У Цзю, Чжэн Ши],
    Класс: ['A', 'A', 'A', 'B', 'B', 'B', 'B', 'A'],
    Математика: [90, 85, 92, 78, 88, 91, 85, 89]
}
df = pd.DataFrame(data)

print("Данные об оценках студентов:")
print(df)
print()

# Вычислить рейтинг каждого ученика в классе (от высшего к низшему)
df['班levelinside排name'] = df.groupby(Class)[Mathematics].transform(lambda x: x.rank(ascending=False))

# Вычислить процентиль каждого ученика (процентный рейтинг) в классе
df['Percentile within class'] = df.groupby(Class)[Mathematics].transform(lambda x: x.rank(pct=True))

print("Данные после добавления внутриклассового рейтинга и процентиля:")
print(df)
print()

# Другой способ заполнения пропущенных значений: заполнение средним по группе
data_with_na = {
    'Имя': ['Чжан Сань', 'Ли Сы', 'Ван У', 'Чжао Лю', Сунь Ци, Чжоу Ба],
    Класс: ['A', 'A', 'A', 'B', 'B', 'B'],
    'Score': [85, np.nan, 78, 88, np.nan, 82]
}
df_na = pd.DataFrame(data_with_na)

print(Данные с пропущенными значениями:)
print(df_na)
print()

# Заполнить пропущенные значения средним по группе
df_na['Filled score'] = df_na.groupby(Class)['Score'].transform(lambda x: x.fillna(x.mean()))

print(Результат после заполнения средним по группе:)
print(df_na)

Результат выполнения:

学生成绩数据:
   姓名  班级  数学
0  张三   A   90
1  李四   A   85
2  王五   A   92
3  赵六   B   78
4  孙七   B   88
5  周八   B   91
6  吴九   B   85
7  郑十   A   89

添加班级内排名和百分位后的数据:
   姓名   班级   数学   班级内排名   班级内百分位
0  张三   A   90   2.0   0.666667
1  李四   A   85   3.0   1.000000
2  王五   A   92   1.0   1.000000
3  赵六   B   78   4.0   0.250000
4  孙七   B   88   2.0   0.500000
5  周八   B   91   1.0   0.750000
6  吴九   B   85   3.0   0.500000
7  郑十   A   89   4.0   0.333333

包含缺失值的数据:
   姓名  班级   成绩
0  张三   A   85.0
1  李四   A    NaN
2  王五   A   78.0
3  赵六   B   88.0
4  孙七   B    NaN
5  周八   B   82.0

使用组内均值填充后的结果:
   姓名  班级   成绩  填充后的成绩
0  张三   A   85.0         85.0
1  李四   A    NaN         81.5
2  王五   A   78.0         78.0
3  赵六   B   88.0         88.0
4  孙七   B    NaN         85.0
5  周八   B   82.0         82.0

Пояснение к коду:

  • rank(ascending=False)Вычислите ранжирование, при этом более высокие значения получают более высокий ранг.
  • rank(pct=True)Вычислите процентильное ранжирование, при этом результаты находятся в диапазоне от 0 до 1.
  • transform(lambda x: x.fillna(x.mean()))Это распространённый шаблон обработки пропущенных значений: использование среднего значения каждой группы для заполнения пропущенных значений внутри этой группы.

Пример 4: Использование пользовательской функции преобразования

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

Пример

import pandas as pd
import numpy as np

# Создание данных о ценах акций
data = {
    'Stock Code': ['AAPL', 'AAPL', 'AAPL', 'AAPL', 'GOOG', 'GOOG', 'GOOG', 'GOOG'],
    'Date': ['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04'] * 2,
    'Close Price': [100, 105, 102, 110, 80, 85, 82, 90]
}
df = pd.DataFrame(data)

print("Данные о ценах акций:")
print(df)
print()

# Пользовательская функция: вычисление дневной доходности для каждой акции
def calc_daily_return(prices):
    """Вычислить дневную доходность: (сегодняшняя цена - вчерашняя цена) / вчерашняя цена"""
    return prices.pct_change()

# Рассчитать дневную доходность для каждой акции
df['Daily Return'] = df.groupby('Stock Code')['Close Price'].transform(calc_daily_return)

print("Данные после добавления дневной доходности:")
print(df)
print()

# Пользовательская функция: вычислить нормализованное значение на основе максимума и минимума в группе
def normalize(x):
    """Нормализация: (x - min) / (max - min)"""
    return (x - x.min()) / (x.max() - x.min())

# Рассчитать нормализованную цену для каждой акции
df['Normalized Price'] = df.groupby('Stock Code')['Close Price'].transform(normalize)

print("Данные после добавления нормализованной цены:")
print(df)

Результат выполнения:

股票价格数据:
  股票代码         日期   收盘价
0  AAPL  2024-01-01   100
1  AAPL  2024-01-02   105
2  AAPL  2024-01-03   102
3  AAPL  2024-01-04   110
4  GOOG  2024-01-01    80
5  GOOG  2024-01-02    85
6  GOOG  2024-01-03    82
7  GOOG  2024-01-04    90

添加日收益率后的数据:
   股票代码         日期  收盘价     日收益率
0  AAPL  2024-01-01   100         NaN
1  AAPL  2024-01-02   105    0.050000
2  AAPL  2024-01-03   102   -0.028571
3  AAPL  2024-01-04   110    0.078431
4  GOOG  2024-01-01    80         NaN
5  GOOG  2024-01-02    85    0.062500
6  GOOG  2024-01-03    82   -0.035294
7  GOOG  2024-01-04    90    0.097561

添加归一化价格后的数据:
   股票代码         日期  收盘价     日收益率   归一化价格
0  AAPL  2024-01-01   100         NaN    0.000000
1  AAPL  2024-01-02   105    0.050000    0.500000
2  AAPL  2024-01-03   102   -0.028571    0.200000
3  AAPL  2024-01-04   110    0.078431    1.000000
4  GOOG  2024-01-01    80         NaN    0.000000
5  GOOG  2024-01-02    85    0.062500    0.500000
6  GOOG  2024-01-03    82   -0.035294    0.200000
7  GOOG  2024-01-04    90    0.097561    1.000000

Пояснение к коду:

  • Функция преобразования получает Series (данные внутри каждой группы).
  • Длина возвращаемого Series должна совпадать с длиной входного Series.
  • Первое значение — NaN, потому чтоpct_change()он вычисляет относительное изменение, а для первого дня нет данных за предыдущий день.

Ключевое различие:agg()иtransform()Разница заключается в следующем:agg()возвращает агрегированный результат для каждой группы (число строк равно числу групп), тогда какtransform()возвращает результат той же длины, что и исходные данные. Простое запоминание: agg сжимает данные, transform сохраняет форму данных.


Pandas 常用函数Общие функции Pandas

Другие расширения