Home > Software design >  R replace only specific values and keep all others in dataframe
R replace only specific values and keep all others in dataframe

Time:11-12

my code looks like this:

library(tidyverse)
df <- read.table(header=TRUE, text='
 subject sex control q1 q2
       1   M     7.9  1  1
       2   F     6.3  2  3
       3   F     9.5  3  1
       4   M    11.5  7  6
')

df %>% mutate_all(~case_when(
                              . == 1 ~ 7,
                              . == 7 ~ 1,
                              TRUE ~ . )
                  )

I want to replace all 1 with 7 and vice versa but keep everything else.

The error states:

Error: Problem with mutate() column subject. i subject = (structure(function (..., .x = ..1, .y = ..2, . = ..1) .... x must be a double vector, not an integer vector.

A solution indicates TRUE ~ as.numeric(as.character(.)) ) works, but then the sex colum is NA

How can I fix this?

Edit (add): A suggestion was to use nested if-else, which would work, but I really hope there is a better solution than: df %>% mutate_all(~ifelse(. == 1, 7, ifelse(. == 7, 1, .)))

imagine a long list of values to be replaced.

CodePudding user response:

You can use either mutate_at() or mutate_if() to selectively choose which columns to apply the transformation to.

df %>% mutate_at(c(3:5), ~case_when(
   . == 1 ~ 7,
   . == 7 ~ 1,
   TRUE ~ as.numeric(.))
)

df %>% mutate_if(is.numeric, ~case_when(
   . == 1 ~ 7,
   . == 7 ~ 1,
   TRUE ~ as.numeric(.))
)

Edit: a non-deprecated version without mutate_if would be:

df %>% mutate(
      across(
        where(is.numeric), 
          ~case_when(
           . == 1 ~ 7,
           . == 7 ~ 1,
           TRUE ~ as.numeric(.)
          )
      )
)

CodePudding user response:

Try this:

df %>% 
  mutate(across(starts_with("q"), 
                ~ ifelse(. == 1, 7,
                         ifelse(. == 7, 1, .))
                ))
  subject sex control q1 q2
1       1   M     7.9  7  7
2       2   F     6.3  2  3
3       3   F     9.5  3  7
4       4   M    11.5  1  6

A column-name independent solution is this:

df %>% 
  mutate(across(where(is.integer), 
                ~ ifelse(. == 1, 7,
                         ifelse(. == 7, 1, .))
  ))

CodePudding user response:

I would use a nested ifelse() call along with lapply() here:

cols <- c("q1", "q2")  # or whatever columns you want
df[cols] <- lapply(df[cols], function(x) ifelse(x == 1, 7, ifelse(x == 7, 1, x)))

CodePudding user response:

You could wrap the mutate_all in a function, to skip the characters e.g.

custom_fun <- function(x){
  if(is.integer(x)|is.numeric(x)){
    case_when(
      x == 1 ~ 7,
      x == 7 ~ 1,
      TRUE ~ as.numeric(x) 
    )
  }else{
    x
  }
} 

df %>% mutate_all(custom_fun)
  • Related