ChatGPT解决这个技术问题 Extra ChatGPT

Pandas 中 map、applymap 和 apply 方法的区别

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

我看到 map 是一个 Series 方法,而其余的是 DataFrame 方法。不过,我对 applyapplymap 方法感到困惑。为什么我们有两种方法可以将函数应用于 DataFrame?同样,说明用法的简单示例会很棒!

如果我错了,请纠正我,但我相信这些函数不是矢量化方法,因为它们都涉及对它们应用的元素的循环。
我在这里看不出有什么不同:gist.github.com/MartinThoma/e320cbb937afb4ff766f75988f1c65e6
Marillion,我在下面的回答中提供了非常简化和简单的示例。希望能帮助到你!
我应该在比较中添加 DataFrame.pipe() 方法吗?

j
jeremiahbuddha

直接来自 Wes McKinney 的 Python for Data Analysis 书,第 1 页。 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

许多最常见的数组统计信息(如 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

名称 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

总而言之,apply 在 DataFrame 的行/列基础上工作,applymap 在 DataFrame 上按元素工作,而 map 在 Series 上按元素工作。


严格来说,applymap 内部是通过 apply 实现的,对传递的函数参数进行了一些总结(粗略地说,将 func 替换为 lambda x: [func(y) for y in x],并按列应用)
感谢您的解释。由于 mapapplymap 都以元素方式工作,我希望有一个方法(mapapplymap)既适用于系列也适用于 DataFrame。可能还有其他设计考虑,Wes McKinney 决定提出两种不同的方法。
出于某种原因,它在我的副本的第 129 页上。没有第二版或任何东西的标签。
有没有办法在 pandas 中执行 applymapgroupby 功能?
我建议不要使用 format 作为函数名(如示例 2 所示),因为 format 已经是一个内置函数。
c
cs95

比较 map、applymap 和 apply:上下文很重要

第一个主要区别:定义

地图仅在系列上定义

applymap 仅在 DataFrames 上定义

apply 定义在 BOTH

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

map 接受 dicts、Series 或 callable

applymap 和 apply 仅接受可调用对象

第三个主要区别:行为

地图是系列的元素

applymap 对于 DataFrames 是 elementwise

apply 也可以按元素工作,但适用于更复杂的操作和聚合。行为和返回值取决于函数。

第四大区别(最重要的一个):USE CASE

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))。

另请参阅 When should I (not) want to use pandas apply() in my code?,了解我不久前写的关于使用 apply 的最合适场景的文章(请注意,数量不多,但也有一些 — 应用通常) .

总结

https://i.stack.imgur.com/IZys3.png

传递字典/系列时的脚注映射将根据该字典/系列中的键映射元素。缺失值将在输出中记录为 NaN。更新版本中的 applymap 已针对某些操作进行了优化。在某些情况下,您会发现 applymap 比 apply 稍微快一些。我的建议是测试它们并使用更好的方法。 map 针对元素映射和转换进行了优化。涉及字典或系列的操作将使 pandas 能够使用更快的代码路径以获得更好的性能。 Series.apply 返回一个用于聚合操作的标量,否则返回 Series。同样适用于 DataFrame.apply。请注意,当使用某些 NumPy 函数(例如 mean、sum 等)调用时,apply 也有快速路径。


M
MarredCheese

快速总结

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

DataFrame.applymap、Series.apply 和 Series.map 一次对一个元素进行操作。

Series.applySeries.map 相似且通常可以互换。下面的osa's answer中讨论了它们的一些细微差别。


s
scls

除了其他答案之外,在 Series 中还有 mapapply

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

此外,如果我有一个具有副作用的功能,例如“连接到 Web 服务器”,我可能会使用 apply 只是为了清楚起见。

series.apply(download_file_for_every_element) 

Map 不仅可以使用函数,还可以使用字典或其他系列。假设您要操作 permutations

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() 则不允许。
T
Tiago Martins Peres

@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

很好的抓住这一点。这在您的示例中有效的原因是因为 np.sqrt 是一个 ufunc,即如果您给它一个数组,它会将 sqrt 函数广播到数组的每个元素上。因此,当 apply 在每列上推送 np.sqrt 时,np.sqrt 会自行作用于列的每个元素,因此您基本上得到与 applymap 相同的结果。
K
Kath

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

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

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

注意如果应用返回单个值,您将在分配后拥有该值而不是列,最终将只有一行而不是矩阵。


n
np8

只是想指出,因为我为此挣扎了一会儿

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 执行某些操作后是否必须重新分配。对我来说,这主要是反复试验,但我敢打赌它的工作原理是有逻辑的(我错过了)。
一般来说,熊猫数据框只能通过重新分配 df = modified_df 或设置 inplace=True 标志来修改。如果您通过引用将数据框传递给函数并且该函数修改数据框,则数据框也会发生变化
这并不完全正确,想想 .ix.where 等。不确定何时需要重新分配以及何时不需要的完整解释是什么。
A
Alpha

根据cs95的答案

地图仅在系列上定义

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

m
mikelowry

只是为了获得额外的上下文和直觉,这里有一个明确而具体的差异示例。

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

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

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

https://i.stack.imgur.com/ZwKgf.png

如果您尝试使用 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() 方法更快地执行等效任务。

https://i.stack.imgur.com/fFQrv.png

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

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


V
Vicky Miao

我的理解:

从功能上看:

如果函数具有需要在列/行内比较的变量,请使用 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)

请提供 df2 以获得更好的清晰度,以便我们可以测试您的代码。
p
prosti

FOMO:

以下示例显示了应用于 DataFrameapplyapplymap

map 函数仅适用于系列。您不能在 DataFrame 上应用 map

要记住的是,apply 可以做任何事情 applymap 可以,但 applyeXtra 选项。

X 因子选项是:axisresult_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

作为旁注,Series map 函数不应与 Python map 函数相混淆。

第一个应用于系列,以映射值,第二个应用于可迭代的每个项目。

最后不要将 dataframe apply 方法与 groupby apply 方法混淆。