Pandas 中 map、applymap 和 apply 方法的区别
- 2024-12-03 08:45:00
- admin 原创
- 173
问题描述:
您能否通过基本示例告诉我何时使用这些矢量化方法?
我看到这map
是一个Series
方法,而其余的都是方法。但我对和方法DataFrame
感到困惑。为什么我们有两种将函数应用于 DataFrame 的方法?再次,说明用法的简单示例会很棒!apply
`applymap`
解决方案 1:
apply
以 DataFrame 的行 / 列为基础进行操作
applymap
以 DataFrame 的元素为基础进行
map
操作 以 Series 的元素为基础进行操作
直接摘自 Wes McKinney 的《Python 数据分析》一书,第 132 页(我强烈推荐这本书):
另一个常见操作是将一维数组上的函数应用到每一列或每一行。DataFrame 的 apply 方法正是这样做的:
In [116]: frame = DataFrame(np.random.randn(4, 3), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])
In [117]: frame
Out[117]:
b d e
Utah -0.029638 1.081563 1.280300
Ohio 0.647747 0.831136 -1.549481
Texas 0.513416 -0.884417 0.195343
Oregon -0.485454 -0.477388 -0.309548
In [118]: f = lambda x: x.max() - x.min()
In [119]: frame.apply(f)
Out[119]:
b 1.133201
d 1.965980
e 2.829781
dtype: float64
许多最常见的数组统计数据(如总和和平均值)都是 DataFrame 方法,因此不需要使用 apply。
也可以使用元素级 Python 函数。假设您想要从帧中的每个浮点值计算格式化的字符串。您可以使用 applymap 执行此操作:
In [120]: format = lambda x: '%.2f' % x
In [121]: frame.applymap(format)
Out[121]:
b d e
Utah -0.03 1.08 1.28
Ohio 0.65 0.83 -1.55
Texas 0.51 -0.88 0.20
Oregon -0.49 -0.48 -0.31
使用applymap这个名字的原因是Series有一个map方法可以应用元素函数:
In [122]: frame['e'].map(format)
Out[122]:
Utah 1.28
Ohio -1.55
Texas 0.20
Oregon -0.31
Name: e, dtype: object
解决方案 2:
比较map
、applymap
和apply
:语境很重要
主要区别在于:
定义
map
仅在 Series 上定义applymap
仅在 DataFrames 上定义apply
定义在两者上
输入参数
map
接受dict
、Series
或可调用applymap
并apply
仅接受可调用
行为
map
对于系列而言是逐元素的applymap
对于 DataFrames 来说,是元素级的apply
也适用于元素,但更适合更复杂的操作和聚合。行为和返回值取决于函数。
用例(最重要的区别)
map
用于将值从一个域映射到另一个域,因此针对性能进行了优化,例如,
df['A'].map({1:'a', 2:'b', 3:'c'})
applymap
适用于跨多行/列的元素转换,例如,
df[['A', 'B', 'C']].applymap(str.strip)
apply
用于应用任何不能矢量化的函数,例如,
df['sentences'].apply(nltk.sent_tokenize)
另请参阅我什么时候应该(不)在我的代码中使用 pandas apply()?这是我之前写的一篇关于使用 的最合适场景的文章apply
。 (请注意,这种情况并不多,但还是有的—— apply 通常很慢。)
总结
map | applymap | apply | |
---|---|---|---|
在系列上定义? | 是的 | 不 | 是的 |
在 DataFrame 上定义? | 不 | 是的 | 是的 |
争论 | dict ,Series ,或可调用1 | 可调用2 | 可调用 |
逐元素而言? | 是的 | 是的 | 是的 |
聚合? | 不 | 不 | 是的 |
用例 | 转换/映射3 | 转型 | 更复杂的功能 |
返回 | Series | DataFrame | 标量,Series ,或DataFrame 4 |
脚注
map
当传递字典/系列时,将根据该字典/系列中的键映射元素。缺失值将在输出中记录为 NaN。applymap
在较新的版本中,某些操作已得到优化。您会发现在某些情况下applymap
速度会略快apply
。我的建议是测试两者,并使用效果更好的那个。map
针对元素映射和转换进行了优化。涉及字典或系列的操作将使 Pandas 能够使用更快的代码路径来获得更好的性能。Series.apply
返回聚合操作的标量,Series
否则。对于 也类似DataFrame.apply
。请注意,在使用某些 NumPy 函数(例如、等)apply
调用时也具有快速路径。mean
`sum`
解决方案 3:
快速摘要
DataFrame.apply
一次对整行或整列进行操作。DataFrame.applymap
、、Series.apply
和Series.map
每次对一个元素进行操作。
Series.apply
和类似且经常可以互换。下面osa 的回答Series.map
讨论了它们的一些细微差别。
解决方案 4:
除了其他答案之外,在 a 中Series
还有map和apply。
Apply 可以从一个系列中创建一个 DataFrame;但是,map 只会将一个系列放在另一个系列的每个单元格中,这可能不是您想要的。
In [40]: p=pd.Series([1,2,3])
In [41]: p
Out[31]:
0 1
1 2
2 3
dtype: int64
In [42]: p.apply(lambda x: pd.Series([x, x]))
Out[42]:
0 1
0 1 1
1 2 2
2 3 3
In [43]: p.map(lambda x: pd.Series([x, x]))
Out[43]:
0 0 1
1 1
dtype: int64
1 0 2
1 2
dtype: int64
2 0 3
1 3
dtype: int64
dtype: object
此外,如果我有一个具有副作用的功能,例如“连接到网络服务器”,我可能会apply
只是为了清楚起见使用它。
series.apply(download_file_for_every_element)
Map
不仅可以使用函数,还可以使用字典或其他系列。假设您想要操作排列。
拿
1 2 3 4 5
2 1 4 5 3
这个排列的平方是
1 2 3 4 5
1 2 5 3 4
您可以使用 来计算map
。不确定自我应用是否有记录,但它在 中有效0.15.1
。
In [39]: p=pd.Series([1,0,3,4,2])
In [40]: p.map(p)
Out[40]:
0 0
1 1
2 4
3 2
4 3
dtype: int64
解决方案 5:
@jeremiahbuddha 提到,apply 适用于行/列,而 applymap 适用于元素。但似乎您仍然可以使用 apply 进行元素计算……
frame.apply(np.sqrt)
Out[102]:
b d e
Utah NaN 1.435159 NaN
Ohio 1.098164 0.510594 0.729748
Texas NaN 0.456436 0.697337
Oregon 0.359079 NaN NaN
frame.applymap(np.sqrt)
Out[103]:
b d e
Utah NaN 1.435159 NaN
Ohio 1.098164 0.510594 0.729748
Texas NaN 0.456436 0.697337
Oregon 0.359079 NaN NaN
解决方案 6:
可能最简单的解释是apply
和之间的区别applymap
:
apply
将整个列作为参数,然后将结果分配给该列
applymap
将单独的单元格值作为参数并将结果分配回该单元格。
注意:如果apply
返回单个值,则分配后您将获得该值而不是列,最终将只有一行而不是矩阵。
解决方案 7:
我只是想指出,因为我对这个问题有点困惑
def f(x):
if x < 0:
x = 0
elif x > 100000:
x = 100000
return x
df.applymap(f)
df.describe()
这不会修改数据框本身,必须重新分配:
df = df.applymap(f)
df.describe()
解决方案 8:
根据cs95的答案
map
仅在系列上定义applymap
仅在 DataFrames 上定义apply
在 BOTH 上都有定义
举一些例子
In [3]: frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])
In [4]: frame
Out[4]:
b d e
Utah 0.129885 -0.475957 -0.207679
Ohio -2.978331 -1.015918 0.784675
Texas -0.256689 -0.226366 2.262588
Oregon 2.605526 1.139105 -0.927518
In [5]: myformat=lambda x: f'{x:.2f}'
In [6]: frame.d.map(myformat)
Out[6]:
Utah -0.48
Ohio -1.02
Texas -0.23
Oregon 1.14
Name: d, dtype: object
In [7]: frame.d.apply(myformat)
Out[7]:
Utah -0.48
Ohio -1.02
Texas -0.23
Oregon 1.14
Name: d, dtype: object
In [8]: frame.applymap(myformat)
Out[8]:
b d e
Utah 0.13 -0.48 -0.21
Ohio -2.98 -1.02 0.78
Texas -0.26 -0.23 2.26
Oregon 2.61 1.14 -0.93
In [9]: frame.apply(lambda x: x.apply(myformat))
Out[9]:
b d e
Utah 0.13 -0.48 -0.21
Ohio -2.98 -1.02 0.78
Texas -0.26 -0.23 2.26
Oregon 2.61 1.14 -0.93
In [10]: myfunc=lambda x: x**2
In [11]: frame.applymap(myfunc)
Out[11]:
b d e
Utah 0.016870 0.226535 0.043131
Ohio 8.870453 1.032089 0.615714
Texas 0.065889 0.051242 5.119305
Oregon 6.788766 1.297560 0.860289
In [12]: frame.apply(myfunc)
Out[12]:
b d e
Utah 0.016870 0.226535 0.043131
Ohio 8.870453 1.032089 0.615714
Texas 0.065889 0.051242 5.119305
Oregon 6.788766 1.297560 0.860289
解决方案 9:
只是为了提供更多的背景信息和直觉,这里有一个明确而具体的差异例子。
假设您有如下所示的函数。(此标签函数将根据您作为参数 (x) 提供的阈值,将值任意拆分为“高”和“低”。)
def label(element, x):
if element > x:
return 'High'
else:
return 'Low'
在这个例子中,假设我们的数据框有一列随机数。
如果你尝试使用 map 映射标签函数:
df['ColumnName'].map(label, x = 0.8)
您将得到以下错误:
TypeError: map() got an unexpected keyword argument 'x'
现在采用相同的函数并使用 apply,您将看到它有效:
df['ColumnName'].apply(label, x=0.8)
Series.apply()可以按元素接受附加参数,而Series.map()方法将返回错误。
现在,如果您尝试同时将同一函数应用于数据框中的多列,则使用DataFrame.applymap() 。
df[['ColumnName','ColumnName2','ColumnName3','ColumnName4']].applymap(label)
最后,您还可以在数据框上使用 apply() 方法,但 DataFrame.apply() 方法具有不同的功能。df.apply() 方法不是按元素应用函数,而是沿轴(按列或按行)应用函数。当我们创建与 df.apply() 一起使用的函数时,我们将其设置为接受一系列,最常见的是一列。
以下是一个例子:
df.apply(pd.value_counts)
当我们将 pd.value_counts 函数应用于数据框时,它会计算所有列的值计数。
请注意,当我们使用 df.apply() 方法转换多列时,这一点非常重要。这仅仅是因为 pd.value_counts 函数对一系列进行操作。如果我们尝试使用 df.apply() 方法将按元素工作的函数应用于多列,则会收到错误:
例如:
def label(element):
if element > 1:
return 'High'
else:
return 'Low'
df[['ColumnName','ColumnName2','ColumnName3','ColumnName4']].apply(label)
这将导致以下错误:
ValueError: ('The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().', u'occurred at index Economy')
一般来说,我们只应在不存在矢量化函数时使用 apply() 方法。回想一下,pandas 使用矢量化(将操作一次性应用于整个系列的过程)来优化性能。当我们使用 apply() 方法时,我们实际上是在循环遍历行,因此矢量化方法可以比 apply() 方法更快地执行等效任务。
以下是一些已经存在的矢量化函数的示例,您不想使用任何类型的 apply/map 方法重新创建它们:
Series.str.split() 拆分 Series 中的每个元素
Series.str.strip() 去除 Series 中每个字符串的空格。
Series.str.lower() 将系列中的字符串转换为小写。
Series.str.upper() 将 Series 中的字符串转换为大写。
Series.str.get() 检索 Series 中每个元素的第 i 个元素。
Series.str.replace() 用另一个字符串替换 Series 中的正则表达式或字符串
Series.str.cat() 连接系列中的字符串。
Series.str.extract() 从与正则表达式模式匹配的系列中提取子字符串。
解决方案 10:
我的理解:
从功能上来说:
**如果函数有需要在列/行内进行比较的变量,请使用 apply
。**
例如:lambda x: x.max()-x.mean()
。
如果要将该函数应用于每个元素:
1> 如果位于列/行,则使用apply
2> 如果应用于整个数据框,则使用applymap
majority = lambda x : x > 17
df2['legal_drinker'] = df2['age'].apply(majority)
def times10(x):
if type(x) is int:
x *= 10
return x
df2.applymap(times10)
解决方案 11:
错失恐惧症(FOMO):
下面的示例显示apply
并applymap
应用于DataFrame
。
map
函数只能应用于 Series。不能应用于map
DataFrame。
要记住的是, apply
可以做任何事情 applymap
,但apply
有额外的选择。
X 因子选项为:axis
并且仅result_type
在(对于列)result_type
时有效axis=1
。
df = DataFrame(1, columns=list('abc'),
index=list('1234'))
print(df)
f = lambda x: np.log(x)
print(df.applymap(f)) # apply to the whole dataframe
print(np.log(df)) # applied to the whole dataframe
print(df.applymap(np.sum)) # reducing can be applied for rows only
# apply can take different options (vs. applymap cannot)
print(df.apply(f)) # same as applymap
print(df.apply(sum, axis=1)) # reducing example
print(df.apply(np.log, axis=1)) # cannot reduce
print(df.apply(lambda x: [1, 2, 3], axis=1, result_type='expand')) # expand result
附注:Seriesmap
函数不应与Pythonmap
函数混淆。
第一个应用于 Series,用于映射值,第二个应用于可迭代的每个项目。
最后,不要将 dataframeapply
方法与 groupbyapply
方法混淆。
解决方案 12:
观察:applymap
新版本中不再使用 pandas,而是将其重命名为map
https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.map.html#pandas.DataFrame.map