Pandas知识点-详解转换函数transform
transform()是pandas中的转换函数,对DataFrame执行传入的函数后返回一个相同形状的DataFrame。用于对DataFrame中的数据进行转换,本文将对transform()函数进行详细介绍。
transform()参数和用法介绍
transform(func, axis=0, *args, **kwargs):
- func: 用于转换数据的函数,函数必须满足传入一个DataFrame能正常使用,或传递到DataFrame.apply()中能正常使用。
func可以接收函数的名字、函数名的字符串、函数组成的列表、行/列标签和函数名组成的字典。 - axis: 设置按列还是按行转换。设置为0或index,表示对每列应用转换函数,设置为1或columns,表示对每行应用转换函数。
- args: 传递给函数func的位置参数。
- kwargs: 传递给函数func的关键字参数。
返回数据:
- 返回的结果是一个与自身形状相同的DataFrame。
transform()传入单个函数
# coding=utf-8
import pandas as pd
import numpy as np
df = pd.DataFrame({
'Col-1': [1, 3, 5], 'Col-2': [5, 7, 9]})
print(df)
res1 = df.transform(np.square)
print(res1)
res2 = df.transform('sqrt')
print(res2)
res3 = df.transform(lambda x: x*10)
print(res3)
Col-1 Col-2
0 1 5
1 3 7
2 5 9
Col-1 Col-2
0 1 25
1 9 49
2 25 81
Col-1 Col-2
0 1.000000 2.236068
1 1.732051 2.645751
2 2.236068 3.000000
Col-1 Col-2
0 10 50
1 30 70
2 50 90
在transform()中传入单个函数进行转换,transform()的结果与apply()/applymap()等效。
函数可以是库函数、自定义函数或匿名函数。因为transform()的返回结果与自身形状相同,所以不支持直接传入会将DataFrame“降维”的函数,如会将Series处理成标量的聚合函数min,mean,std等。传入这些函数时,会报错:ValueError: Function did not transform.
虽然transform()是按行/列来处理数据,但它对数据的处理有点像元素级的处理。上面这种传入单个函数对DataFrame做行列级处理的情况,更推荐使用apply()。
res4 = np.square(df)
print(res4)
Col-1 Col-2
0 1 25
1 9 49
2 25 81
对于传入单个函数的情况,直接用函数对DataFrame处理结果也一样。
transform()传入多个函数
res5 = df.transform([np.square, np.sqrt])
print(res5)
# 传入相同名字的函数时,只有最后一个函数生效
res6 = df.transform([np.abs, lambda x: x+10, lambda x:np.square(x)])
print(res6)
Col-1 Col-2
square sqrt square sqrt
0 1 1.000000 25 2.236068
1 9 1.732051 49 2.645751
2 25 2.236068 81 3.000000
Col-1 Col-2
absolute <lambda> absolute <lambda>
0 1 1 5 25
1 3 9 7 49
2 5 25 9 81
在transform()中传入多个函数对DataFrame进行转换,结果中的索引变成多层索引,第一层索引是DataFrame的列名,第二层索引是执行的函数名。按第一层索引来比较,DataFrame的形状并没有变化。
在传入多个函数时,传入的方式是用列表传入,此时如果有多个名字相同的函数,只有最后一个函数生效。(聚合函数agg()中多个匿名函数可以同时生效)
按字典的方式传入函数
res7 = df.transform({
'Col-1': np.square, 'Col-2': np.sqrt})
print(res7)
res8 = df.transform({
'Col-1': [np.square, lambda x: x*10], 'Col-2': np.sqrt})
print(res8)
Col-1 Col-2
0 1 2.236068
1 9 2.645751
2 25 3.000000
Col-1 Col-2
square <lambda> sqrt
0 1 10 2.236068
1 9 30 2.645751
2 25 50 3.000000
在transform()中可以用字典的方式给不同的列传入不同的函数,如果字典中的value有列表,则结果的索引变成多层索引。
transform()对Series进行转换
s = pd.Series(range(3))
print(s)
res9 = s.transform(lambda x: x+10)
print(res9)
print(res9.shape)
print('-' * 20)
res10 = s.transform([np.square, lambda x: x*x])
print(res10)
print(res10.shape)
print(type(res10))
0 0
1 1
2 2
dtype: int64
0 10
1 11
2 12
dtype: int64
(3,)
--------------------
square <lambda>
0 0 0
1 1 1
2 4 4
(3, 2)
<class 'pandas.core.frame.DataFrame'>
transform()传入单个函数对Series转换时,结果是Series,传入多个函数对Series转换时,结果是DataFrame,列名是调用的函数名。
在这种情况下,Series的形状发生了变化。(也可以理解成结果是多层索引,第一层是Series的列索引,第二层是函数名,只是Series的列索引没有显示。)
与groupby()配合使用
score_df = pd.DataFrame(
{
'姓名': ['Tom', 'Tom', 'Tom', 'Andy', 'Andy', 'Andy', 'Andy', 'Kitty', 'Kitty', 'Kitty'],
'课程': ['高数', '英语', '体育', '高数', '英语', '体育', '计算机', '高数', '英语', '体育'],
'成绩': [60, 90, 80, 90, 80, 70, 90, 70, 90, 80],
'评级': ['C', 'A', 'B', 'A', 'B', 'C', 'A', 'C', 'A', 'B']}
)
print(score_df)
姓名 课程 成绩 评级
0 Tom 高数 60 C
1 Tom 英语 90 A
2 Tom 体育 80 B
3 Andy 高数 90 A
4 Andy 英语 80 B
5 Andy 体育 70 C
6 Andy 计算机 90 A
7 Kitty 高数 70 C
8 Kitty 英语 90 A
9 Kitty 体育 80 B
result = score_df.groupby('姓名')['成绩'].transform(np.sum)
print('-'*30, '\n', result, sep='')
result = score_df.groupby('姓名')['成绩'].agg(np.sum)
print('-'*30, '\n', result, sep='')
------------------------------
0 230
1 230
2 230
3 330
4 330
5 330
6 330
7 240
8 240
9 240
Name: 成绩, dtype: int64
------------------------------
姓名
Andy 330
Kitty 240
Tom 230
Name: 成绩, dtype: int64
在与分组函数groupby()配合使用时,transform()转换的结果与agg()聚合的结果不一样,transform()会保持每一个分组的形状与原始数据形状相同,而agg()会将每个分组的结果聚合成一个标量值。
这是transform()和agg()最主要的功能差异,也是最有用的一点,在适合的场景里非常有用。
下面来看两个具体的案例。
案例1:计算每个人的成绩与同一门课程平均成绩的差。
score_df['平均成绩'] = score_df.groupby('课程')['成绩'].transform(np.mean)
print('-'*30, '\n', score_df, sep='')
score_df['成绩差异'] = score_df['成绩'] - score_df['平均成绩']
print('-'*30, '\n', score_df, sep='')
------------------------------
姓名 课程 成绩 评级 平均成绩
0 Tom 高数 60 C 73.333333
1 Tom 英语 90 A 86.666667
2 Tom 体育 80 B 76.666667
3 Andy 高数 90 A 73.333333
4 Andy 英语 80 B 86.666667
5 Andy 体育 70 C 76.666667
6 Andy 计算机 90 A 90.000000
7 Kitty 高数 70 C 73.333333
8 Kitty 英语 90 A 86.666667
9 Kitty 体育 80 B 76.666667
------------------------------
姓名 课程 成绩 评级 平均成绩 成绩差异
0 Tom 高数 60 C 73.333333 -13.333333
1 Tom 英语 90 A 86.666667 3.333333
2 Tom 体育 80 B 76.666667 3.333333
3 Andy 高数 90 A 73.333333 16.666667
4 Andy 英语 80 B 86.666667 -6.666667
5 Andy 体育 70 C 76.666667 -6.666667
6 Andy 计算机 90 A 90.000000 0.000000
7 Kitty 高数 70 C 73.333333 -3.333333
8 Kitty 英语 90 A 86.666667 3.333333
9 Kitty 体育 80 B 76.666667 3.333333
第一步,使用groupby()函数按课程分组,然后使用transform()计算出每门课程成绩的平均值。这里transform()会保证结果的形状与原来相同,极大地方便了下一步计算差值。
第二步,用成绩减平均成绩,就可以得到成绩与此门课程平均成绩的差。
实际使用时,这两个步骤可以直接合并成一步,因为“平均成绩”只是用于计算差值的中间值,不需要保存,直接一步计算出差值即可。
score_df['成绩差异'] = score_df.groupby('课程')['成绩'].transform(lambda x: x - x.mean())
print('-'*30, '\n', score_df, sep='')
案例2:填充缺失值,用每门课程的平均值填充第四位同学的成绩。
score_truman = pd.DataFrame(
{
'姓名': ['Truman', 'Truman', 'Truman'],
'课程': ['高数', '英语', '体育'],
'成绩': [np.NAN, np.NAN, np.NAN],
'评级': [np.NAN, np.NAN, np.NAN]}
)
score_df = pd.concat([score_df, score_truman]).reset_index()
print('-'*30, '\n', score_df, sep='')
------------------------------
index 姓名 课程 成绩 评级
0 0 Tom 高数 60.0 C
1 1 Tom 英语 90.0 A
2 2 Tom 体育 80.0 B
3 3 Andy 高数 90.0 A
4 4 Andy 英语 80.0 B
5 5 Andy 体育 70.0 C
6 6 Andy 计算机 90.0 A
7 7 Kitty 高数 70.0 C
8 8 Kitty 英语 90.0 A
9 9 Kitty 体育 80.0 B
10 0 Truman 高数 NaN NaN
11 1 Truman 英语 NaN NaN
12 2 Truman 体育 NaN NaN
score_df['成绩'] = score_df.groupby('课程')['成绩'].transform(lambda x: x.fillna(x.mean()))
print('-'*30, '\n', score_df, sep='')
------------------------------
index 姓名 课程 成绩 评级
0 0 Tom 高数 60.000000 C
1 1 Tom 英语 90.000000 A
2 2 Tom 体育 80.000000 B
3 3 Andy 高数 90.000000 A
4 4 Andy 英语 80.000000 B
5 5 Andy 体育 70.000000 C
6 6 Andy 计算机 90.000000 A
7 7 Kitty 高数 70.000000 C
8 8 Kitty 英语 90.000000 A
9 9 Kitty 体育 80.000000 B
10 0 Truman 高数 73.333333 NaN
11 1 Truman 英语 86.666667 NaN
12 2 Truman 体育 76.666667 NaN
用平均值来填充同一组数据中的空值,这是数据处理时非常常用的方式,借用transform()可以一步完成此功能。
以上就是pandas中转换函数transform()的用法介绍和分析,如果本文的内容对你有帮助,欢迎点赞、收藏和评论,也可以关注和联系我一起交流讨论。
参考文档:
[1] pandas中文网:https://www.pypandas.cn/docs/
相关阅读:
Pandas知识点-详解聚合函数agg