apply() 函数仅适用于某些列
apply() function to only certain columns
我有一个如下所示的数据框(带有可重现的代码):
# create the table
name <- c("Mary", "John", "Peter")
id1 <- c(50, 30, 25)
id2 <- c(8, 12, 90)
id3 <- c(14, 17, 34)
id4 <- c(9, 67, 89)
id5 <- c(20, 21, 22)
beep <- c(15, 20, 23)
# combine the df
df <- data.frame(name, id1, id2, id3, id4, id5, beep)
# show df
df
name id1 id2 id3 id4 id5 beep
1 Mary 50 8 14 9 20 15
2 John 30 12 17 67 21 20
3 Peter 25 90 34 89 22 23
我想将“id#”小于“beep”变量的每个单元格重新编码为 1,否则为 0。我尝试了以下方法:
apply(df, 2, function(x) {
ifelse(x < df$beep, 1, 0)})
这会产生以下向量:
name id1 id2 id3 id4 id5 beep
[1,] 0 0 1 1 1 0 0
[2,] 0 0 1 1 0 0 0
[3,] 0 0 0 0 0 1 0
上述向量的问题是我不想改变“name”或“beep”变量。有什么建议吗?
不需要apply
,可以试试下面的代码
df[startsWith(names(df), "id")] <- +(df[startsWith(names(df), "id")] < df$beep)
这给出了
> df
name id1 id2 id3 id4 id5 beep
1 Mary 0 1 1 1 0 15
2 John 0 1 1 0 0 20
3 Peter 0 0 0 0 1 23
如果你真的想使用apply
,下面是一个选项
idx <- grep("^id", names(df))
df[idx] <- apply(df[idx], 2, function(x) ifelse(x < df$beep, 1, 0))
1) mutate/across 使用 dplyr 可以使用 mutate/across。 across 的第一个参数定义要使用的列,第二个参数是应用于每个此类列的函数。公式的右边是函数体,点是函数的参数。我们使用 + 将逻辑结果转换为数字。
library(dplyr)
df %>% mutate(across(starts_with("id"), ~ +(. < beep)))
## name id1 id2 id3 id4 id5 beep
## 1 Mary 0 1 1 1 0 15
## 2 John 0 1 1 0 0 20
## 3 Peter 0 0 0 0 1 23
2) modify_if purrr 包有一个函数,它只会修改满足第二个参数定义的条件的列。它支持与 (1) 中相同的 shorthand 功能。
library(purrr)
modify_if(df, startsWith(names(df), "id"), ~ +(. < df$beep))
## name id1 id2 id3 id4 id5 beep
## 1 Mary 0 1 1 1 0 15
## 2 John 0 1 1 0 0 20
## 3 Peter 0 0 0 0 1 23
3) replace 这与另一个答案基本相同,但使用 grep
和 replace
代替。没有使用包。
ix <- grep("^id", names(df))
replace(df, ix, +(df[ix] < df$beep))
## name id1 id2 id3 id4 id5 beep
## 1 Mary 0 1 1 1 0 15
## 2 John 0 1 1 0 0 20
## 3 Peter 0 0 0 0 1 23
4) modifyList 它的 modifyList
使用名称匹配将第一个参数中的列替换为第二个参数中的列。两个参数都必须是列表或数据框(不是矩阵)。
ix <- grep("^id", names(df))
modifyList(df, +as.data.frame(df[ix] < df$beep))
## name id1 id2 id3 id4 id5 beep
## 1 Mary 0 1 1 1 0 15
## 2 John 0 1 1 0 0 20
## 3 Peter 0 0 0 0 1 23
(这曾经在 lattice 包中,但现在在作为基础 R 的一部分的 utils 中。)
您的数据中可能有 NA
,如果您与 <
进行比较,则数据会 return NA
。您可以使用 is.na
进行额外检查以处理 NA
值。
cols <- grep('id', names(df))
df[cols] <- +(df[cols] < df$beep & !is.na(df[cols]))
我有一个如下所示的数据框(带有可重现的代码):
# create the table
name <- c("Mary", "John", "Peter")
id1 <- c(50, 30, 25)
id2 <- c(8, 12, 90)
id3 <- c(14, 17, 34)
id4 <- c(9, 67, 89)
id5 <- c(20, 21, 22)
beep <- c(15, 20, 23)
# combine the df
df <- data.frame(name, id1, id2, id3, id4, id5, beep)
# show df
df
name id1 id2 id3 id4 id5 beep
1 Mary 50 8 14 9 20 15
2 John 30 12 17 67 21 20
3 Peter 25 90 34 89 22 23
我想将“id#”小于“beep”变量的每个单元格重新编码为 1,否则为 0。我尝试了以下方法:
apply(df, 2, function(x) {
ifelse(x < df$beep, 1, 0)})
这会产生以下向量:
name id1 id2 id3 id4 id5 beep
[1,] 0 0 1 1 1 0 0
[2,] 0 0 1 1 0 0 0
[3,] 0 0 0 0 0 1 0
上述向量的问题是我不想改变“name”或“beep”变量。有什么建议吗?
不需要apply
,可以试试下面的代码
df[startsWith(names(df), "id")] <- +(df[startsWith(names(df), "id")] < df$beep)
这给出了
> df
name id1 id2 id3 id4 id5 beep
1 Mary 0 1 1 1 0 15
2 John 0 1 1 0 0 20
3 Peter 0 0 0 0 1 23
如果你真的想使用apply
,下面是一个选项
idx <- grep("^id", names(df))
df[idx] <- apply(df[idx], 2, function(x) ifelse(x < df$beep, 1, 0))
1) mutate/across 使用 dplyr 可以使用 mutate/across。 across 的第一个参数定义要使用的列,第二个参数是应用于每个此类列的函数。公式的右边是函数体,点是函数的参数。我们使用 + 将逻辑结果转换为数字。
library(dplyr)
df %>% mutate(across(starts_with("id"), ~ +(. < beep)))
## name id1 id2 id3 id4 id5 beep
## 1 Mary 0 1 1 1 0 15
## 2 John 0 1 1 0 0 20
## 3 Peter 0 0 0 0 1 23
2) modify_if purrr 包有一个函数,它只会修改满足第二个参数定义的条件的列。它支持与 (1) 中相同的 shorthand 功能。
library(purrr)
modify_if(df, startsWith(names(df), "id"), ~ +(. < df$beep))
## name id1 id2 id3 id4 id5 beep
## 1 Mary 0 1 1 1 0 15
## 2 John 0 1 1 0 0 20
## 3 Peter 0 0 0 0 1 23
3) replace 这与另一个答案基本相同,但使用 grep
和 replace
代替。没有使用包。
ix <- grep("^id", names(df))
replace(df, ix, +(df[ix] < df$beep))
## name id1 id2 id3 id4 id5 beep
## 1 Mary 0 1 1 1 0 15
## 2 John 0 1 1 0 0 20
## 3 Peter 0 0 0 0 1 23
4) modifyList 它的 modifyList
使用名称匹配将第一个参数中的列替换为第二个参数中的列。两个参数都必须是列表或数据框(不是矩阵)。
ix <- grep("^id", names(df))
modifyList(df, +as.data.frame(df[ix] < df$beep))
## name id1 id2 id3 id4 id5 beep
## 1 Mary 0 1 1 1 0 15
## 2 John 0 1 1 0 0 20
## 3 Peter 0 0 0 0 1 23
(这曾经在 lattice 包中,但现在在作为基础 R 的一部分的 utils 中。)
您的数据中可能有 NA
,如果您与 <
进行比较,则数据会 return NA
。您可以使用 is.na
进行额外检查以处理 NA
值。
cols <- grep('id', names(df))
df[cols] <- +(df[cols] < df$beep & !is.na(df[cols]))