前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >R语言中 "apply" 函数详解

R语言中 "apply" 函数详解

作者头像
磐创AI
发布2021-04-21 10:55:10
19.9K0
发布2021-04-21 10:55:10
举报

磐创AI分享

作者 | PURVA HUILGOL

编译 | Flin

来源 | analyticsvidhya

概述

  • 数据操作是机器学习生命周期中最关键的步骤之一
  • 让我们学习最广泛使用的apply函数集来转换R中的数据

介绍

数据操作是机器学习生命周期中最关键的步骤之一。它需要转换所提供的数据,以便用于建立预测模型。

此外,一个熟练的数据科学家运用他们的直觉和经验,从数据中提取尽可能多的信息。因此,在Python和R中都有大量的函数和工具可以帮助我们完成这项任务,这一点也不奇怪。

今天,我们将使用R并学习在R中转换数据时使用最广泛的一组“apply”函数。这组函数提供了对数据的高效和快速操作。当我们只想处理某些列时,这特别有用。这组函数称为apply()函数。除了sapply()、mapply()等变体之外,我们还提供了一把用于数据处理的多用途"瑞士军刀"。

如果你对从事数据科学和学习这些东西感兴趣,我建议你看看我们的Certified AI & ML BlackBelt Accelerate计划。

  • https://blackbelt.analyticsvidhya.com/accelerate

目录

该系列中的各种函数包括:

  • 设置上下文
  • apply
  • lapply
  • sapply
  • vapply
  • tapply
  • mapply
设置上下文

我将首先通过使用简单的数据集介绍上面的每个函数是如何工作的,然后我们将使用一个真实的数据集来使用这些函数。

让我们开始吧!

我们不需要安装任何其他库来使用apply函数。因此,让我们首先创建一个简单的数值矩阵,从1到20,分布在5行4列中:

代码语言:javascript
复制
data <- matrix(c(1:20), nrow = 5 , ncol = 4)
data

这就是我们矩阵的样子。现在,让我们从apply()函数开始

apply()

apply() 函数的一般语法可以通过帮助部分获得。只需执行此代码即可获得详细的文档

代码语言:javascript
复制
?apply

我们可以看到,apply函数的结构是apply(X,MARGIN,FUN,…)。

这里,

  • X是指我们将对其应用操作的数据集(在本例中是矩阵)
  • MARGIN参数允许我们指定是按行还是按列应用操作
  • 行边距=1
  • 列边距=2
  • FUN指的是我们想要在X上“应用”的任何用户定义或内置函数

让我们看看计算每行平均数的简单示例:

代码语言:javascript
复制
mean_rows <- apply(data, 1, mean)
mean_rows

那相当简单!我们可以看到如何使用apply()函数来总结数据。同样,让我们试着沿着每列求和:

代码语言:javascript
复制
sum_cols <- apply(data, 2, sum)
sum_cols

如果我们想在所有元素上应用函数,我们只需这样编写apply函数:

代码语言:javascript
复制
all_sqrt <- apply(data, 1:2, sqrt)
all_sqrt

如果我们想对数据应用一个用户定义的函数呢?例如,我有一个函数,它为每行查找(x–1)的平方根:

代码语言:javascript
复制
fn = function(x)
{
   return(sqrt(x - 1))
}

然后,我们在每行应用此函数:

代码语言:javascript
复制
apply(data, 1, fn)

到目前为止,我们只使用了一个参数的函数,并将它们应用于数据。apply家族最棒的部分是,它们也处理具有多个参数的函数!让我们应用一个用户定义的函数,该函数包含3个参数:

代码语言:javascript
复制
fn = function(x1, x2, x3)
{    
    return(x1^2 + x2 * x1 + x3)
}

我们将x1作为'data'中的每个值,将x2,x3作为其他参数,这些参数将首先声明,然后通过apply函数传递:

代码语言:javascript
复制
b = 2
c = 1

# apply along each row:
row_fn <- apply(data, 1, fn, x2 = b, x3 = c)

# apply along each column:
col_fn <- apply(data, 2, fn, x2 = b, x3 = c)

让我们检查一下第fn行和第fn列

代码语言:javascript
复制
row_fn
代码语言:javascript
复制
col_fn

apply()系列的其余部分遵循类似的结构,除了一些更改外,其他参数也类似。接下来让我们使用lappy()函数。

lapply()

上面的apply()函数有一个约束,数据必须是至少2维的矩阵,apply()函数才能对其执行。lapply()函数删除了这个约束。lapply()是list apply的缩写,可以对列表或向量使用lapply函数。无论是一个向量列表还是一个简单的向量,lappy()都可以在这两个向量上使用。由于我们现在处理的是向量/列表,lapply函数也不需要MARGIN参数。也就是说,lapply的返回类型也是一个列表。

它仅将数据和函数作为基本参数:

lapply(X, FUN)

让我们看一些例子:

代码语言:javascript
复制
# define a list
cart <- c("BREAD","BUTTER","MILK","COOKIES")

# use lapply to convert all to lower case
cart_lower <- lapply(cart, tolower)

#output
cart_lower

我们现在要看一个更复杂的列表:

代码语言:javascript
复制
data <- list(l1 = c(1, 2, 3, 4),
             l2 = c(5, 6, 7, 8),
             l3 = c(9, 10, 11, 12))

# apply the 'sum' function on data:
sum_list <- lapply(data, sum)

#output
sum_list
sapply()

sapply()函数(simplified apply的缩写)类似于lappy函数。唯一的区别是输出的返回类型——sapply()根据返回的值简化了输出。我创建了一个简单的表,告诉我们返回的类型:

返回值

每个元素的长度

输出

列表

1个

向量

列表

> 1并且长度相同

矩阵

列表

> 1,且长度可变

列表

我们将看到上述所有场景的示例:

场景1:每个元素的长度=1

代码语言:javascript
复制
data <- list(l1 = c(1, 2, 3, 4))

# apply the 'sum' function on data:
sum_sapply1 <- sapply(data, sum)

#output
sum_sapply1

使用lapply查看输出的差异:

代码语言:javascript
复制
sum_lapply1 <- lapply(data, sum)
sum_lapply1

场景2:每个元素的长度>1且相同

代码语言:javascript
复制
data <- list(l1 = c(1, 2, 3, 4),
             l2 = c(5, 6, 7, 8),
             l3 = c(9, 10, 11, 12))

# apply the 'sum' function on data:
sum_sapply2 <- sapply(data, sum)

#output
sum_sapply2

lapply()提供了什么输出?

代码语言:javascript
复制
sum_lapply2 <- lapply(data, sum)
sum_lapply2

场景3:每个元素的长度>1且不同

代码语言:javascript
复制
data <- list(l1 = c(1, 2, 3),
l2 = c(5, 6, 7, 8),
l3 = c(9, 10))

# apply the 'sum' function on data:
sum_sapply3 <- sapply(data, sum)

#output
sum_sapply3

让我们将其与lappy()在相同数据上的输出进行比较:

代码语言:javascript
复制
sum_lapply3 <- lapply(data, sum)

#output
sum_lapply3

你可以看到输出与上面返回列表的lappy有何不同

vapply()

来到vapply()函数。lapply()、apply()和vapply()这三个函数是专门为所有类型的向量定制的。与lappy()和sapply()为我们决定输出的数据类型不同,vapply()允许我们选择输出结构的数据类型。因此,vapply()的参数是:

vapply(X,FUN,FUN.VALUE)

在这里 FUN.VALUE 用于提供所需的数据类型。

当lost/vectors包含数字和字符串的混合时,这是最有用的:

代码语言:javascript
复制
data <- list(l1 = c(1, 2, 3, 4),
             l2 = c(5, 6, 7, 8),
             l3 = c(9, 10, 11, 12),
             l4 = c("a", "b", "c", "a"))

# apply the 'max' function on data:
sum_vapply <- vapply(data, max, numeric(1))

正如预期的那样,我们得到了一个错误,因为无法从字符列表中计算最大值。numeric(1)指定我们希望输出为单个数值,其中每个元素的长度为1。如果我们使用lapply()或sapply()呢?

代码语言:javascript
复制
lapply(data, max)
sapply(data, max)

因此,我们可以看到lappy()和sapply()实际上都提供了相同的输出。实际上,sapply()甚至将输出转换为character类型的向量。理想情况下,这不是我们想要的。

通常,这就是我们使用vapply()函数的方式

代码语言:javascript
复制
data <- list(l1 = c(1, 2, 3, 4),
             l2 = c(5, 6, 7, 8),
             l3 = c(9, 10, 11, 12))

max_vapply <- vapply(data, max, numeric(1))

max_vapply

因此,在处理具有不同数据类型特性的数据帧时,最好使用vapply()。

tapply()

简单地说,tapply()允许我们将数据分组,并对每个分组执行操作。因此,当你提供一个向量作为输入时,tapply()会对向量的每个子集执行指定的操作。需要的参数包括:

tapply(X, INDEX, FUN)

其中INDEX表示要用于分隔数据的因子。听起来耳熟吗?是的,tapply()只不过是执行groupy操作并对分组数据应用某些函数的简单方法!

为了观察tapply()的工作原理,让我们创建两个简单的向量

代码语言:javascript
复制
item_cat <- c("HOME", "SNACKS", "BEVERAGE", "STORAGE", "CLEANING", "STORAGE", "HOME", "BEVERAGE", "ELECTRONICS", "SNACKS")
item_qty <-c(25, 30, 45, 66, 15, 50, 35, 20, 15, 35)

现在让我们使用tapply来获得每个项目类别的平均数量:

代码语言:javascript
复制
tapply(item_qty, item_cat, mean)

tapply()函数做了什么?我们将item_qty向量按item_cat向量分组,以创建向量的子集。然后我们计算每个子集的平均值。

使用tapply()非常容易,因为它会自动从item_cat 向量 中获取唯一的值,并几乎立即对数据应用所需的函数。我们甚至可以在每个子集上获得多个值:

代码语言:javascript
复制
tapply(item_qty, item_cat, function(x) c(mean(x), sum(x)))

现在,我们来看看apply()函数家族中的最后一个函数——mapply()函数。

mapply()

mapply()代表multivariable apply,基本上是sapply()的multivariable版本。mapply函数最好用例子来解释——所以让我们先使用它,然后再尝试理解它是如何工作的。

首先,让我们看一个通常不以2个列表或2个向量作为参数的函数,例如max函数。我们先看两张列表:

代码语言:javascript
复制
list1 <- list(a = c(1, 2, 3), b = c(4, 5, 6), c = c(7, 8, 9))
list2 <- list(a = c(10, 11, 12), b = c(13, 14, 15), c = c(16, 17, 18))

现在,如果我们想找出每对列表元素之间的最大值呢?

代码语言:javascript
复制
max(list1$a, list2$a)

现在,这个函数不能同时应用于list1和list2的所有元素。在这种情况下,我们使用mapply()函数:

代码语言:javascript
复制
mapply(function(num1, num2) max(c(num1, num2)), list1, list2)

因此,mapply函数用于对通常不接受多个列表/向量作为参数的数据执行函数。当你要创建新列时,它也很有用。让我们首先从最初定义的矩阵创建一个数据帧:

代码语言:javascript
复制
df <- as.data.frame(data)

现在,我们将创建一个新变量,该变量包含V1列和V3列的乘积:

代码语言:javascript
复制
mapply(function(x, y) x/y, df$V1, df$V3)

因此,在处理数据帧时,mapply是一个非常方便的函数。

现在,让我们看看如何在实际数据集上使用这些函数。为了简单起见,让我们使用iris数据集:

代码语言:javascript
复制
iris_df<-datasets::iris

head(iris_df)

我们现在可以使用apply()函数计算每行的间隔长度和间隔宽度的平均值:

代码语言:javascript
复制
iris_df['Sepal_mean'] <- apply(iris_df[c("Sepal.Length", "Sepal.Width")], 1, mean)

类似地,我们可以获得数据框中每个物种的每列的摘要值:

代码语言:javascript
复制
tapply(iris_df$Sepal.Width, iris_df$Species, mean)

我们还可以使用mapply()函数创建一个显示花瓣长度和花瓣宽度之和的新列:

代码语言:javascript
复制
iris_df['Sum_Petal'] <- mapply(function(x, y) x+y, iris_df$Petal.Length, iris_df$Petal.Width)

尾注

到目前为止,我们学习了R中apply()函数族中的各种函数。这些函数集提供了在一瞬间对数据应用各种操作的极其有效的方法。本文介绍了这些函数的基础知识,目的是让你了解这些函数是如何工作的。

我鼓励你在更复杂的数据集上尝试更复杂的函数,以充分了解这些函数有多有用。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 磐创AI 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 磐创AI分享
    • 设置上下文
      • apply()
        • lapply()
          • sapply()
            • vapply()
              • tapply()
                • mapply()
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档