你能告诉我何时使用这些矢量化方法以及基本示例吗?
我看到map
是一个Series
方法,而其他方法都是DataFrame
方法。不过,我对apply
和applymap
方法感到困惑。为什么我们有两种方法可以将函数应用于DataFrame?同样,提供说明用法的简单示例将非常有帮助!
你能告诉我何时使用这些矢量化方法以及基本示例吗?
我看到map
是一个Series
方法,而其他方法都是DataFrame
方法。不过,我对apply
和applymap
方法感到困惑。为什么我们有两种方法可以将函数应用于DataFrame?同样,提供说明用法的简单示例将非常有帮助!
apply
方法在 DataFrame 的行 / 列基础上运作
applymap
方法在 DataFrame 的每个元素上运作
map
方法在 Series 的每个元素上运作
来自 Wes McKinney 的 Python for Data Analysis 一书,第 132 页 (我强烈推荐这本书):
另一个常见操作是将一个函数应用于每个列或行的 1D 数组。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函数。假设你想从frame中的每个浮点数值计算格式化的字符串,可以使用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
func
替换为lambda x:[func(y)for y in x]
,并按列应用)来实现的。 - alkomap
和applymap
都是逐元素操作,我期望有一个单一的方法(可以是map
或applymap
)既能用于Series
也能用于DataFrame
。可能存在其他设计考虑,Wes McKinney决定创建两个不同的方法。 - marilliongroupby
函数的同时执行 applymap
操作? - everestial007format
作为函数名称(例如示例2),因为format
已经是一个内置函数。 - Arturo Moncada-Torresmap
, applymap
和apply
:上下文很重要主要的区别如下:
map
仅在Series上定义applymap
仅在DataFrames上定义apply
在两者上都定义map
接受dict
,Series
或可调用对象applymap
和apply
只接受可调用对象map
对于Series是逐元素操作的。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)
apply
的最合适场景。需要注意的是,并不是很多情况下都适合使用apply,因为它通常比较慢。
map |
applymap |
apply |
|
---|---|---|---|
Defined on Series? | 是 | 否 | 是 |
Defined on DataFrame? | 否 | 是 | 是 |
Argument | dict 、Series 或可调用函数1 |
可调用函数2 | 可调用函数 |
Elementwise? | 是 | 是 | 是 |
Aggregation? | 否 | 否 | 是 |
Use Case | 转换/映射3 | 转换 | 更复杂的函数 |
Returns | Series |
DataFrame |
标量、Series 或DataFrame 4 |
脚注
map
当传入一个字典/序列时,将根据该字典/序列中的键进行映射。缺失值将在输出中记录为NaN。
applymap
在较新版本中已经针对某些操作进行了优化。在某些情况下,你会发现applymap
比apply
稍微快一些。我的建议是测试两者,并使用效果更好的那个。
map
针对逐元素映射和转换进行了优化。涉及字典或序列的操作将使pandas能够使用更快的代码路径以获得更好的性能。
Series.apply
对于聚合操作返回标量,否则返回Series
。对于DataFrame.apply
也是如此。请注意,当与某些NumPy函数(如mean
、sum
等)一起调用时,apply
也具有快速路径。
apply
是逐元素的?你是否将列和行视为数据框的元素? - wjandreadf.applymap()
为 df.map()
。目前前者仍然可用,但会记录一个FutureWarning
建议使用更合理的名称。 - undefinedDataFrame.apply
一次操作整个行或列。
DataFrame.applymap
,Series.apply
和Series.map
一次操作一个元素。
Series.apply
和 Series.map
相似且通常可互换使用。它们之间的一些细微差别在下面osa的答案中讨论。
除了其他回答之外,在 Series
中还有 map 和 apply。
apply 可以将一个 series 转换为 DataFrame;然而,map 只会将一个 series 放入另一个 series 的每个单元格中,这可能不是您想要的。
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
如果我有一个带有副作用的函数,比如"连接到web服务器",为了清晰明了起见,我可能会使用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
.apply()
允许在调用函数时传递关键字参数(kwargs),而 .map()
不允许。 - lineil@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
解释apply和applymap的区别最简单的方法是:
apply
将整个列作为参数,然后将结果赋值给该列。
applymap
将各个单元格的值作为参数,并将结果重新赋值给该单元格。
注意:如果apply
返回单个值,则在分配之后只会得到该值,最终会得到一个行而不是矩阵。
只是想指出,我曾经为此苦苦挣扎一段时间。
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()
df = modified_df
或者设置 inplace=True
标识进行修改。如果将数据框作为引用参数传递到函数中,并且该函数修改了数据框,则数据框也会发生变化。 - muon.ix
或 .where
等。不确定何时需要重新分配,何时不需要的完整解释是什么。 - Thanos根据cs95的回答:
map
只能用于 Seriesapplymap
只能用于 DataFramesapply
可以同时用于两者以下是一些例子:
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
为了更好地理解,这里举一个明确具体的例子来说明区别。
假设您有以下函数。 (此标签函数将根据您提供的阈值(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'
df['ColumnName'].apply(label, x=0.8)
Series.apply()可以逐个元素地使用额外的参数,而Series.map()方法会返回错误。
现在,如果您想同时将同一函数应用于数据帧中的多个列,则使用DataFrame.applymap()。
df[['ColumnName','ColumnName2','ColumnName3','ColumnName4']].applymap(label)
df.apply(pd.value_counts)
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方法重新创建它们:我的理解:
从功能角度来看:
如果函数中有需要在列/行内进行比较的变量,则使用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)