Pandas中map、applymap和应用方法的区别

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

我看到map是一个Series方法,而其余的是DataFrame方法。我对applyapplymap方法感到困惑。为什么我们有两个方法来应用一个函数到一个数据帧?再一次,简单的例子说明用法将是伟大的!

466401 次浏览

apply工作在数据帧的行/列基础上
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
许多最常见的数组统计(如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

@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

除了其他答案,在Series中还有地图应用

# EYZ0;然而,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)

假设你想要操作排列

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

我只是想指出一点,我在这方面有过一些挣扎

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


df.applymap(f)
df.describe()

# EYZ0:

df = df.applymap(f)
df.describe()

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

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

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

注意:如果apply返回单个值,你将得到这个值而不是赋值后的列,最终将只有一行而不是矩阵。

快速的总结

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

  • DataFrame.applymapSeries.applySeries.map操作其中一个

Series.applySeries.map是相似的,并且经常可以互换。下面的阻塞性睡眠呼吸暂停综合症的回答中讨论了它们之间的一些细微差异。

我的理解:

从功能上看:

如果函数中有变量需要在列/行中进行比较,则使用 # EYZ0。< /强> < / p >

例如:# EYZ0。

如果将函数应用于每个元素:

1>如果列/行已定位,则使用apply

如果应用于整个数据帧,使用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)

FOMO:

下面的例子显示了applyapplymap应用于DataFrame

map函数只能应用在Series上。不能在DataFrame上应用map

要记住的是,apply可以做任何东西 applymap,但apply额外的选项。

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函数混淆。

第一个应用在Series上,用于映射值,第二个应用到可迭代对象的每个项。


最后,不要混淆dataframe apply方法和groupby apply方法。

比较mapapplymapapply:环境很重要

第一个主要区别:定义

  • map仅在系列上定义
  • applymap只在数据帧上定义
  • apply定义在BOTH上

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

  • map接受dicts, Series或可调用
  • applymapapply只接受可调用对象

第三个主要区别:行为

  • map是系列的elementwise
  • applymap是数据框架的elementwise
  • 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的最合适的场景(注意不是很多,但也有一些-应用通常是)。


总结

enter image description here

脚注

  1. map当传递一个字典/Series时,将基于该字典/Series中的键映射元素。缺失的值将被记录为

    .输出中的NaN
  2. applymap在最近的版本中已经针对一些操作进行了优化。你会发现applymap略快于apply 某些情况下。我的建议是对它们都进行测试,并使用有效的方法 好。< / p >

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

  4. Series.apply返回用于聚合操作的标量,否则返回Series。DataFrame.apply也是如此。注意,apply也有 当使用某些NumPy函数(如mean)调用fastpaths时, # EYZ0等。< / p >

基于cs95的答案

  • map仅在系列上定义
  • applymap只在数据帧上定义
  • 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

为了增加上下文和直观感受,这里有一个明确而具体的例子来说明它们的区别。

假设你有如下函数。( 这个标签函数,将根据您提供的参数(x)的阈值,将值任意分为'High'和'Low'。)

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


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

Df with one column that have random numbers .

如果你尝试用map映射label函数:

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)

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

这里有一个例子:

df.apply(pd.value_counts)

当我们应用pd。函数,它计算了所有列的值计数。

注意,当我们使用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 summarization .

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

  1. 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()连接一个Series中的字符串。
  8. extract()从匹配正则表达式模式的Series中提取子字符串。