How to repeat a function for all rows for each col in R

Viewed 551

I have a df with 150 rows values, and I would like R to loop or otherwise repeat the following for each row and column in the df;

Example of data

df <- data.frame('criteria1' = c('x','1', 'X', '', 'X'), "criteria2" = c('y','3', '', 'X', ''), "criteria3" = c('y','7', '', 'X', 'X'))

If a X appears in a row, I would like the function to take the values from the two first rows, and paste them together with a '=' between them. The following works fine for the first column.

df$criteria1 <- ifelse(df$criteria1 == 'X', paste(df$criteria1 [1], '=', df$criteria1 [2]),'')
head(df)

The problem is when I try to do it for all the columns in the dataframe

df[] <- lapply(df, function(x) ifelse(df$x== 'X', paste(x[1], '=', x[2]),''))

All cells become NA. I have tried with different versions of the code above, but nothing gives the expected output which is;

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')))

3 Answers

Use sapply instead of lapply... there are many references for the difference between the two, but I believe the issue comes from the fact that ifelse applies to a vector, and not a list, and so you cannot pass it to lapply.

sapply(df, function(x) ifelse(x == 'X', paste(x[[1]], '=', x[[2]]),''))

A dplyr solution:

EDIT

Based on @Chuk P's comment(see below),the following is an edit to the answer:

 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

This is comparable to the output below:

ifelse(df$criteria1 == 'X', paste(df$criteria1 [1], '=', df$criteria1 [2]),'')
[1] ""      ""      "x = 1" ""      "x = 1"

Original answer(see comments,answer left here in case it may be useful in the future)

df %>% 
   mutate_all(~ifelse(.%in%c(letters,LETTERS),paste0(.,"=",.[grep("\\d",.)]),.))

Or using dplyr >= 0.8.89.9000:

df %>% 
  mutate(across(everything(),~ifelse(.%in%c(letters,LETTERS),
                                     paste0(.,"=",.[grep("\\d",.)]),.)))

Result:

   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

If you want blanks instead:

 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

NOTE:

Simpler methods to do this may exist. This is to add variety to the answers.

You need to use mapply here, i.e.

df[] <- mapply(function(x, y)replace(x, x == 'X', y), df, paste(df[1,], df[2,], sep = '='))

which gives,

  criteria1 criteria2 criteria3
1         x         y         y
2         1         3         7
3       x=1                    
4                 y=3       y=7
5       x=1                 y=7
Related