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

2024-12-03 08:45:00
admin
原创
173
摘要:问题描述:您能否通过基本示例告诉我何时使用这些矢量化方法?我看到这map是一个Series方法,而其余的都是方法。但我对和方法DataFrame感到困惑。为什么我们有两种将函数应用于 DataFrame 的方法?再次,说明用法的简单示例会很棒!apply`applymap`解决方案 1:apply以 Data...

问题描述:

您能否通过基本示例告诉我何时使用这些矢量化方法?

我看到这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:

比较mapapplymapapply:语境很重要

主要区别在于:

定义

  • map在 Series 上定义

  • applymap在 DataFrames 上定义

  • apply定义在两者上

输入参数

  • map接受dictSeries或可调用

  • applymapapply仅接受可调用

行为

  • 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 通常很慢。)


总结

mapapplymapapply
在系列上定义?是的是的
在 DataFrame 上定义?是的是的
争论dictSeries,或可调用1可调用2可调用
逐元素而言?是的是的是的
聚合?是的
用例转换/映射3转型更复杂的功能
返回SeriesDataFrame标量,Series,或DataFrame4

脚注

  1. map当传递字典/系列时,将根据该字典/系列中的键映射元素。缺失值将在输出中记录为 NaN。

  2. applymap在较新的版本中,某些操作已得到优化。您会发现在某些情况下applymap速度会略快apply。我的建议是测试两者,并使用效果更好的那个。

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

  4. Series.apply返回聚合操作的标量,Series否则。对于 也类似DataFrame.apply。请注意,在使用某些 NumPy 函数(例如、等)apply调用时也具有快速路径。mean`sum`

解决方案 3:

快速摘要

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

  • DataFrame.applymap、、Series.applySeries.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'

在这个例子中,假设我们的数据框有一列随机数。

具有一列随机数的 Df

如果你尝试使用 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、applymap、map 汇总

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

  1. Series.str.split() 拆分 Series 中的每个元素

  2. Series.str.strip() 去除 Series 中每个字符串的空格。

  3. Series.str.lower() 将系列中的字符串转换为小写。

  4. Series.str.upper() 将 Series 中的字符串转换为大写。

  5. Series.str.get() 检索 Series 中每个元素的第 i 个元素。

  6. Series.str.replace() 用另一个字符串替换 Series 中的正则表达式或字符串

  7. Series.str.cat() 连接系列中的字符串。

  8. 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):

下面的示例显示applyapplymap应用于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
pandas 更新

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用