Pandas中map,applymap和apply方法之间的区别

mar*_*ion 409 python vectorization dataframe pandas

你能告诉我什么时候使用这些矢量化方法和基本的例子吗?

我看到这map是一种Series方法,而其余的是DataFrame方法.我对此感到困惑applyapplymap方法.为什么我们有两种方法将函数应用于DataFrame?再一次,说明用法的简单例子会很棒!

jer*_*dha 484

直接来自Wes McKinney的Python for Data Analysis一书,pg.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
Run Code Online (Sandbox Code Playgroud)

许多最常见的数组统计信息(如sum和mean)都是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
Run Code Online (Sandbox Code Playgroud)

名称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
Run Code Online (Sandbox Code Playgroud)

总结一下,apply在DataFrame的行/列基础上工作,在DataFrame applymap上以map元素方式工作,并在Series上以元素方式工作.

  • 严格地说,内部的applymap是通过apply传递函数参数实现的(稍微说一下,将`func`替换为`lambda x:[func(y)for y in x]`,并逐列应用) (26认同)
  • 感谢您的解释.由于`map`和`applymap`都是按元素工作的,我希望有一个方法(map`或`applymap`)可以同时用于Series和DataFrame.可能还有其他的设计考虑因素,Wes McKinney决定提出两种不同的方法. (5认同)
  • 我建议不要使用“format”作为函数名称(如示例 2 所示),因为“format”已经是一个内置函数。 (4认同)
  • 出于某种原因,我的副本中的第129页.第二版或任何东西都没有标签. (2认同)

Mar*_*ese 61

这些答案中提供了很多信息,但我正在添加自己的信息,以清楚地总结哪些方法在数组方面与元素方式相关.jeremiahbuddha大多是这样做的,但没有提到Series.apply.我没有代表评论.

  • DataFrame.apply 一次对整行或列进行操作.

  • DataFrame.applymap,Series.apply并且Series.map在一个元素上运行.

Series.apply和的功能之间存在很多重叠Series.map,这意味着在大多数情况下任何一个都可以工作.他们确实有一些细微的差别,其中一些在osa的答案中讨论过.


osa*_*osa 36

添加到其他答案,Series还有地图申请.

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
Run Code Online (Sandbox Code Playgroud)

此外,如果我有一个副作用的功能,如"连接到Web服务器",我可能apply只是为了清楚起见.

series.apply(download_file_for_every_element) 
Run Code Online (Sandbox Code Playgroud)

Map不仅可以使用函数,还可以使用字典或其他系列.假设你想操纵排列.

采取

1 2 3 4 5
2 1 4 5 3
Run Code Online (Sandbox Code Playgroud)

这种排列的平方是

1 2 3 4 5
1 2 5 3 4
Run Code Online (Sandbox Code Playgroud)

你可以用它来计算它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
Run Code Online (Sandbox Code Playgroud)

  • 此外,.apply()允许您将kwargs传递给函数,而.map()则不会. (2认同)

cs9*_*s95 35

比较mapapplymap和:上下文问题apply

第一个主要区别:定义

  • map 仅在系列上定义
  • applymap 仅在DataFrames上定义
  • apply 两者都定义

第二个主要区别:输入参数

  • map接受dictS, 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)

总结

在此处输入图片说明

脚注

  1. map通过字典/系列时,将基于该字典/系列中的键映射元素。缺少的值将在输出中记录为NaN。
  2. applymap在最新版本中,已针对某些操作进行了优化。您会发现applymapapply某些情况下要快一些。我的建议是对它们都进行测试,并使用更好的方法。

  3. map针对元素映射和转换进行了优化。涉及字典或系列的操作将使熊猫能够使用更快的代码路径来获得更好的性能。

  4. Series.apply返回用于汇总操作的标量,否则返回Series。同样适用于DataFrame.apply。需要注意的是apply,当某些NumPy的功能,如所谓的也有FastPaths的meansum等等。

  • pandas 2.1.0 (2023-08-30) [重命名](https://pandas.pydata.org/docs/whatsnew/v2.1.0.html#new-dataframe-map-method-and-support-for-extensionarrays ) `df.applymap()` 到 `df.map()`。前者暂时仍然有效,但会记录“FutureWarning”,建议使用更明智的名称。 (2认同)

use*_*752 19

@jeremiahbuddha提到apply适用于行/列,而applymap按元素工作.但似乎你仍然可以使用申请元素计算....

    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
Run Code Online (Sandbox Code Playgroud)

  • 好抓住这个.这个在你的例子中起作用的原因是因为np.sqrt是一个ufunc,即如果给它一个数组,它会将sqrt函数广播到数组的每个元素上.因此,当apply在每列上推送np.sqrt时,np.sqrt会在列的每个元素上自行运行,因此您实际上得到与applymap相同的结果. (28认同)

muo*_*uon 9

只是想指出,因为我有点挣扎

def f(x):
    if x < 0:
        x = 0
    elif x > 100000:
        x = 100000
    return x

df.applymap(f)
df.describe()
Run Code Online (Sandbox Code Playgroud)

这不会修改数据帧本身,必须重新分配

df = df.applymap(f)
df.describe()
Run Code Online (Sandbox Code Playgroud)

  • 通常,只有通过重新分配`df = modified_df`或者设置`inplace = True`标志来修改pandas数据帧.如果通过引用将数据帧传递给函数并且函数修改数据帧,则数据帧也会更改 (2认同)

Kat*_*ath 8

可能最简单的解释apply和applymap之间的区别:

apply将整列作为参数,然后将结果分配给此列

applymap将单独的单元格值作为参数,并将结果分配回此单元格.

注意如果apply返回单个值,则在赋值后将使用此值而不是列,最终将只有一行而不是矩阵.


小智 6

基于cs95的答案

  • map 仅在系列上定义
  • applymap 仅在 DataFrame 上定义
  • 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
Run Code Online (Sandbox Code Playgroud)


mik*_*wry 5

为了提供额外的上下文和直觉,这里有一个明确且具体的差异示例。

假设您有如下所示的功能。(此标签函数将根据您作为参数 (x) 提供的阈值,将值任意分割为“高”和“低”。)

def label(element, x):
    if element > x:
        return 'High'
    else:
        return 'Low'

Run Code Online (Sandbox Code Playgroud)

在此示例中,假设我们的数据框有一列包含随机数。

Df 有一列包含随机数

如果您尝试使用 map 映射标签函数:

df['ColumnName'].map(label, x = 0.8)
Run Code Online (Sandbox Code Playgroud)

您将出现以下错误:

TypeError: map() got an unexpected keyword argument 'x'
Run Code Online (Sandbox Code Playgroud)

现在采用相同的函数并使用 apply,您会发现它有效:

df['ColumnName'].apply(label, x=0.8)
Run Code Online (Sandbox Code Playgroud)

Series.apply()可以按元素获取附加参数,而Series.map()方法将返回错误。

现在,如果您尝试同时将相同的函数应用于数据框中的多个列,则使用DataFrame.applymap() 。

df[['ColumnName','ColumnName2','ColumnName3','ColumnName4']].applymap(label)
Run Code Online (Sandbox Code Playgroud)

最后,您还可以在数据帧上使用 apply() 方法,但 DataFrame.apply() 方法具有不同的功能。df.apply() 方法不是按元素应用函数,而是沿轴(按列或按行)应用函数。当我们创建一个与 df.apply() 一起使用的函数时,我们将其设置为接受一系列,最常见的是一列。

这是一个例子:

df.apply(pd.value_counts)
Run Code Online (Sandbox Code Playgroud)

当我们将 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)

Run Code Online (Sandbox Code Playgroud)

这将导致以下错误:

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')
Run Code Online (Sandbox Code Playgroud)

一般来说,我们应该只在向量化函数不存在时使用 apply() 方法。回想一下,pandas 使用矢量化(即一次对整个序列应用操作的过程)来优化性能。当我们使用 apply() 方法时,我们实际上是在循环行,因此向量化方法可以比 apply() 方法更快地执行等效任务。

apply、applymap、地图汇总

以下是您不想使用任何类型的 apply/map 方法重新创建的已存在的向量化函数的一些示例:

  1. Series.str.split() 分割Series中的每个元素
  2. Series.str.strip() 从 Series 中的每个字符串中去除空格。
  3. Series.str.lower() 将Series中的字符串转换为小写。
  4. Series.str.upper() 将Series中的字符串转换为大写。
  5. Series.str.get() 检索 Series 中每个元素的第 i 个元素。
  6. Series.str.replace() 用另一个字符串替换Series中的正则表达式或字符串
  7. Series.str.cat() 连接Series中的字符串。
  8. Series.str.extract() 从 Series 中提取与正则表达式模式匹配的子字符串。