【问题标题】:Difference between map, applymap and apply methods in PandasPandas 中 map、applymap 和 apply 方法的区别
【发布时间】:2013-11-16 20:49:48
【问题描述】:

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

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

【问题讨论】:

  • 如果我错了,请纠正我,但我相信这些函数不是矢量化方法,因为它们都涉及对它们所应用的元素的循环。
  • Marillion,我在下面的回答中提供了非常简化和简单的示例。希望对您有所帮助!
  • 我应该在比较中添加DataFrame.pipe() 方法吗?

标签: python pandas dataframe vectorization


【解决方案1】:

直接来自 Wes McKinney 的 Python for Data Analysis 书,第 页。 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 方法来应用一个 element-wise 函数:

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)既适用于系列也适用于数据帧。可能还有其他设计考虑,Wes McKinney 决定提出两种不同的方法。
  • 出于某种原因,它在我的副本中的第 129 页上。没有第二版或任何东西的标签。
  • 有没有办法在 pandas 中实现 applymapgroupby 函数?
  • 如何对分组的列数据应用函数?
【解决方案2】:

比较 mapapplymapapply:上下文很重要

第一个主要区别:定义

  • map 仅在系列上定义
  • applymap 仅在 DataFrames 上定义
  • apply 定义在 BOTH

第二个主要区别:INPUT ARGUMENT

  • map 接受 dicts、Series 或可调用
  • applymapapply 仅接受可调用对象

第三个主要区别:行为

  • map 是系列的元素
  • applymap 是 DataFrames 的元素
  • 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 的最合适场景的文章(请注意,数量不多,但有一些——应用通常慢`)。


#总结

脚注

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

  2. applymap 在最近的版本中已针对某些操作进行了优化。你会发现applymapapply 稍微快一点 一些案例。我的建议是测试它们并使用任何有效的方法 更好。

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

  4. Series.apply 返回一个用于聚合操作的标量,否则返回 Series。 DataFrame.apply 也是如此。请注意,apply 也有 使用某些 NumPy 函数(例如 mean)调用时的快速路径, sum

【讨论】:

    【解决方案3】:

    快速总结

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

    • DataFrame.applymapSeries.applySeries.map 对一个进行操作 时间元素。

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

    【讨论】:

      【解决方案4】:

      除了其他答案之外,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() 则不能。
      【解决方案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
      

      【讨论】:

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

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

      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 之后是否必须重新分配。这对我来说主要是反复试验,但我敢打赌它的工作原理是有逻辑的(我错过了)。
      • 一般来说,pandas 数据框只能通过重新分配df = modified_df 或设置inplace=True 标志来修改。如果您通过引用将数据框传递给函数并且该函数修改了数据框,那么数据框也会发生变化
      • 这并不完全正确,想想.ix.where 等。不知道什么时候需要重新分配,什么时候不需要。
      【解决方案7】:

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

      应用将整列作为参数,然后将结果赋给该列

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

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

      【讨论】:

        【解决方案8】:

        根据cs95的回答

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

        举几个例子

        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() 方法更快地执行等效任务。

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

          1. Series.str.split() 拆分系列中的每个元素
          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() 用另一个字符串替换系列中的正则表达式或字符串
          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)
            

            【讨论】:

            • 请同时提供 df2 以获得更清晰的信息,以便我们测试您的代码。
            【解决方案11】:

            FOMO:

            以下示例显示applyapplymap 应用于DataFrame

            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
            

            作为旁注,系列 map 函数不应与 Python map 函数混淆。

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


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

            【讨论】:

              猜你喜欢
              • 2022-11-15
              • 1970-01-01
              • 2015-09-09
              • 2020-04-24
              • 2014-03-16
              • 2018-11-17
              相关资源
              最近更新 更多