Функция Pandas groupby-transform()
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()Самое базовое использование — вычислить среднее значение каждой группы, а затем транслировать результат обратно в каждую строку.
Пример
# Создаём данные с оценками студентов
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
Пояснение к коду:
transform('mean')Вычислите среднее арифметическое по математике для каждого класса.- Поскольку Чжан Сань, Ли Сы, Ван У и Чжэн Ши учатся в классе A, полученное ими «Среднее по математике в классе» — это среднее арифметическое оценок по математике класса A.
- Длина возвращаемого значения совпадает с количеством строк в исходных данных; это ключевое различие между transform и agg.
Пример 2: Стандартизация внутри группы (Z-оценка)
transform()Он часто используется для вычисления стандартизированных значений внутри группы, что очень полезно при сравнении относительного положения отдельных наблюдений внутри группы.
Пример
# Создание данных о зарплате сотрудников
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 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 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: общие функции