如何为 R 中每个列的所有行重复一个函数
How to repeat a function for all rows for each col in R
我有一个包含 150 行值的 df,我希望 R 循环或以其他方式为 df 中的每一行和每一列重复以下操作;
数据示例
df <- data.frame('criteria1' = c('x','1', 'X', '', 'X'), "criteria2" = c('y','3', '', 'X', ''), "criteria3" = c('y','7', '', 'X', 'X'))
如果 X 出现在一行中,我希望该函数从前两行中获取值,并将它们粘贴在一起,并在它们之间添加一个“=”。以下内容适用于第一列。
df$criteria1 <- ifelse(df$criteria1 == 'X', paste(df$criteria1 [1], '=', df$criteria1 [2]),'')
head(df)
问题是当我尝试对数据框中的所有列执行此操作时
df[] <- lapply(df, function(x) ifelse(df$x== 'X', paste(x[1], '=', x[2]),''))
所有单元格都变为 NA。我试过上面代码的不同版本,但没有给出预期的输出;
head(data.frame('criteria1' = c('x','1', 'x=1', '', 'x=1'), "criteria2" = c('y','3', '', 'y=3', ''), "criteria3" = c('y','7', '', 'y=7', 'y=7')))
这里需要使用mapply
,即
df[] <- mapply(function(x, y)replace(x, x == 'X', y), df, paste(df[1,], df[2,], sep = '='))
这给出了,
criteria1 criteria2 criteria3
1 x y y
2 1 3 7
3 x=1
4 y=3 y=7
5 x=1 y=7
使用sapply
而不是lapply
...两者之间的区别有很多参考资料,但我认为问题出在ifelse适用于向量这一事实,而不是一个列表,所以你不能将它传递给 lapply.
sapply(df, function(x) ifelse(x == 'X', paste(x[[1]], '=', x[[2]]),''))
一个dplyr
解决方案:
编辑
根据@Chuk P 的评论(见下文),以下是对答案的编辑:
df %>%
mutate_all(~ifelse(.=="X",paste0(.[[1]],"=",.[[2]]),""))
criteria1 criteria2 criteria3
1
2
3 x=1
4 y=3 y=7
5 x=1 y=7
这与下面的输出相当:
ifelse(df$criteria1 == 'X', paste(df$criteria1 [1], '=', df$criteria1 [2]),'')
[1] "" "" "x = 1" "" "x = 1"
原答案(见评论,留下答案以防将来有用)
df %>%
mutate_all(~ifelse(.%in%c(letters,LETTERS),paste0(.,"=",.[grep("\d",.)]),.))
或使用 dplyr
>= 0.8.89.9000:
df %>%
mutate(across(everything(),~ifelse(.%in%c(letters,LETTERS),
paste0(.,"=",.[grep("\d",.)]),.)))
结果:
criteria1 criteria2 criteria3
1 x=1 y=3 y=7
2 1 3 7
3 X=1
4 X=3 X=7
5 X=1 X=7
如果你想要空白:
df %>%
mutate_all(~ifelse(.%in%c(letters),paste0(.,"=",.[grep("\d",.)]),.[!.%in%LETTERS]))
criteria1 criteria2 criteria3
1 x=1 y=3 y=7
2 1 3 7
3
4 x y
5 1 y 7
注意:
可能存在更简单的方法来执行此操作。这是为了增加答案的多样性。
我有一个包含 150 行值的 df,我希望 R 循环或以其他方式为 df 中的每一行和每一列重复以下操作;
数据示例
df <- data.frame('criteria1' = c('x','1', 'X', '', 'X'), "criteria2" = c('y','3', '', 'X', ''), "criteria3" = c('y','7', '', 'X', 'X'))
如果 X 出现在一行中,我希望该函数从前两行中获取值,并将它们粘贴在一起,并在它们之间添加一个“=”。以下内容适用于第一列。
df$criteria1 <- ifelse(df$criteria1 == 'X', paste(df$criteria1 [1], '=', df$criteria1 [2]),'')
head(df)
问题是当我尝试对数据框中的所有列执行此操作时
df[] <- lapply(df, function(x) ifelse(df$x== 'X', paste(x[1], '=', x[2]),''))
所有单元格都变为 NA。我试过上面代码的不同版本,但没有给出预期的输出;
head(data.frame('criteria1' = c('x','1', 'x=1', '', 'x=1'), "criteria2" = c('y','3', '', 'y=3', ''), "criteria3" = c('y','7', '', 'y=7', 'y=7')))
这里需要使用mapply
,即
df[] <- mapply(function(x, y)replace(x, x == 'X', y), df, paste(df[1,], df[2,], sep = '='))
这给出了,
criteria1 criteria2 criteria3 1 x y y 2 1 3 7 3 x=1 4 y=3 y=7 5 x=1 y=7
使用sapply
而不是lapply
...两者之间的区别有很多参考资料,但我认为问题出在ifelse适用于向量这一事实,而不是一个列表,所以你不能将它传递给 lapply.
sapply(df, function(x) ifelse(x == 'X', paste(x[[1]], '=', x[[2]]),''))
一个dplyr
解决方案:
编辑
根据@Chuk P 的评论(见下文),以下是对答案的编辑:
df %>%
mutate_all(~ifelse(.=="X",paste0(.[[1]],"=",.[[2]]),""))
criteria1 criteria2 criteria3
1
2
3 x=1
4 y=3 y=7
5 x=1 y=7
这与下面的输出相当:
ifelse(df$criteria1 == 'X', paste(df$criteria1 [1], '=', df$criteria1 [2]),'')
[1] "" "" "x = 1" "" "x = 1"
原答案(见评论,留下答案以防将来有用)
df %>%
mutate_all(~ifelse(.%in%c(letters,LETTERS),paste0(.,"=",.[grep("\d",.)]),.))
或使用 dplyr
>= 0.8.89.9000:
df %>%
mutate(across(everything(),~ifelse(.%in%c(letters,LETTERS),
paste0(.,"=",.[grep("\d",.)]),.)))
结果:
criteria1 criteria2 criteria3
1 x=1 y=3 y=7
2 1 3 7
3 X=1
4 X=3 X=7
5 X=1 X=7
如果你想要空白:
df %>%
mutate_all(~ifelse(.%in%c(letters),paste0(.,"=",.[grep("\d",.)]),.[!.%in%LETTERS]))
criteria1 criteria2 criteria3
1 x=1 y=3 y=7
2 1 3 7
3
4 x y
5 1 y 7
注意:
可能存在更简单的方法来执行此操作。这是为了增加答案的多样性。