如何为 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

注意:

可能存在更简单的方法来执行此操作。这是为了增加答案的多样性。