Условно заменить значения подмножества строк на имя столбца в R, используя только tidy

Я ищу способ условно заменить определенные значения в подмножестве столбцов, при этом имя столбца останется в тидиверсе. См. Пример ниже:

wl <- data_frame(x=1:4,
  multi=c("Y","Y","Y","Y"),
  ABC=c("","Y","Y",""), 
  ABD=c("","","",""),
  ABE=c("Y","Y","","Y"))

# A tibble: 4 x 5
      x multi ABC   ABD   ABE  
  <int> <chr> <chr> <chr> <chr>
1     1 Y     ""    ""    Y    
2     2 Y     Y     ""    Y    
3     3 Y     Y     ""    ""   
4     4 Y     ""    ""    Y   

df <- wl %>% mutate_at(vars(matches("AB")),
         funs(replace(.,.=='Y',values="column name")))
# A tibble: 4 x 5
      x multi ABC         ABD   ABE        
  <int> <chr> <chr>       <chr> <chr>      
1     1 Y     ""          ""    column name
2     2 Y     column name ""    column name
3     3 Y     column name ""    ""         
4     4 Y     ""          ""    column name

за исключением того, что это будет фактическое имя столбца, а не «имя столбца». Я использовал здесь два разных ответа, чтобы перейти как к изменению условных столбцов (именно так я попал в свой пример), так и к тому, как заменить определенные значения именем столбца (не используется в моем текущем примере, но ответ здесь).

Я могу объединить два ответа, чтобы заставить его работать:

w <- which(df=="column name",arr.ind=TRUE)
df[w] <- names(df)[w[,"col"]]                 
# A tibble: 4 x 5
      x multi ABC   ABD   ABE  
  <int> <chr> <chr> <chr> <chr>
1     1 Y     ""    ""    ABE  
2     2 Y     ABC   ""    ABE  
3     3 Y     ABC   ""    ""   
4     4 Y     ""    ""    ABE 

Я подтверждаю, что описанный выше процесс полностью функционален, но из чистого любопытства есть ли способ сделать это без второго фрагмента кода? Есть ли какая-то функция, которую я могу подключить к values= части шага replace(.,.=='Y',values="column name"), которая может захватывать имя столбца и выполнять весь этот процесс в рамках одной mutate_at функции?


person c.custer    schedule 16.08.2018    source источник
comment
Вы можете оставаться в тидиверсе и gather, затем mutate, затем spread, например wl %>% gather(ColName,Result, matches("AB")) %>% mutate(Result = if_else(Result =="Y", ColName, Result)) %>% spread(ColName, Result)   -  person Kerry Jackson    schedule 16.08.2018
comment
@KerryJackson ах, очень умно и так просто ... Мне это нравится!   -  person c.custer    schedule 16.08.2018


Ответы (4)


Вы можете сделать небольшой выбор в своей mutate_at функции, чтобы получить имя столбца, а затем ifelse (или любую другую логическую структуру, которую вы можете захотеть) для замены определенных значений.

library(tidyverse)

wl %>%
  mutate_at(vars(starts_with("AB")), function(x) {
    x_var <- rlang::enquo(x)
    ifelse(x == "Y", rlang::quo_name(x_var), x)
  })
#> # A tibble: 4 x 5
#>       x multi ABC   ABD   ABE  
#>   <int> <chr> <chr> <chr> <chr>
#> 1     1 Y     ""    ""    ABE  
#> 2     2 Y     ABC   ""    ABE  
#> 3     3 Y     ABC   ""    ""   
#> 4     4 Y     ""    ""    ABE

Создано 16 августа 2018 г. пакетом REPEX (v0.2.0).

person camille    schedule 16.08.2018
comment
Отлично, работает как шарм и хранит его внутри mutate_at - person c.custer; 16.08.2018

Вы можете использовать replace_na:

wl%>%
   mutate_at(vars(starts_with('AB')),~`is.na<-`(.x,.x=='Y'))%>%
   replace_na(set_names(as.list(names(.)),names(.)))
# A tibble: 4 x 5
  x     multi ABC   ABD   ABE  
  <chr> <chr> <chr> <chr> <chr>
1 1     Y     ""    ""    ABE  
2 2     Y     ABC   ""    ABE  
3 3     Y     ABC   ""    ""   
4 4     Y     ""    ""    ABE  
person Onyambu    schedule 16.08.2018
comment
Еще один отличный ответ! Я думаю, что меня беспокоит только то, что мои настоящие данные могут считываться в NA, а не в фактических пустых символах (моя вина в том, что я не показываю это в примере). Хотя я полагаю, что просто дважды использую ваш код, чтобы заменить фактические NA, и тогда Y будет работать так же хорошо. Спасибо! - person c.custer; 16.08.2018

Вот вариант с imap

library(purrr)
AB_cols <- grep("^AB", names(wl)) # find positions of columns that start with "AB"
wl[AB_cols] <- imap(.x = wl[AB_cols], .f = ~replace(.x, .x == "Y", .y))
wl
# A tibble: 4 x 5
#      x multi ABC   ABD   ABE  
#  <int> <chr> <chr> <chr> <chr>
#1     1 Y     ""    ""    ABE  
#2     2 Y     ABC   ""    ABE  
#3     3 Y     ABC   ""    ""   
#4     4 Y     ""    ""    ABE 
person markus    schedule 16.08.2018
comment
Странно, что в вашем ответе он показывает, что ваш код заменяет несколько столбцов (чего я не хочу), но когда я запускаю его в моем R, он создает мою цель (заменяет только столбцы AB). - person c.custer; 16.08.2018
comment
Мне нужно, чтобы он работал с start_with (AB) или аналогично ему, потому что мой реальный фрейм данных намного больше, чем в моем примере, и я не хочу всегда вручную подсчитывать номера индексов столбцов. Тем не менее, это полезная функция для изучения! Спасибо - person c.custer; 16.08.2018
comment
@ c.custer Отредактировал мой ответ относительно вашего второго комментария. Но что вы имеете в виду, когда ваш код заменяет многоколоночный столбец? - person markus; 16.08.2018
comment
посмотрите на свой выходной тиббл. Все буквы Y были заменены на multi, что является его столбцом. Это странно, потому что когда я запускал ваш код, он этого не делал. - person c.custer; 16.08.2018

С tidy verse

library(dplyr)
wl %>% 
  mutate_at(vars(starts_with("AB")),
     funs(c("", deparse(substitute(.)))[(.=="Y" & !is.na(.)) + 1]))
# A tibble: 4 x 5
#     x multi ABC   ABD   ABE  
#  <int> <chr> <chr> <chr> <chr>
#1     1 Y     ""    ""    ABE  
#2     2 Y     ABC   ""    ABE  
#3     3 Y     ABC   ""    ""   
#4     4 Y     ""    ""    ABE  

Использование base R

nm1 <- grep("^AB", names(wl))
i1 <- wl[,(nm1)] == "Y" & !is.na(wl[,(nm1)])
wl[,(nm1)][i1] <- names(wl)[(nm1)][col(wl[,(nm1)])][i1]
wl
# A tibble: 4 x 5
#      x multi ABC   ABD   ABE  
#  <int> <chr> <chr> <chr> <chr>
#1     1 Y     ""    ""    ABE  
#2     2 Y     ABC   ""    ABE  
#3     3 Y     ABC   ""    ""   
#4     4 Y     ""    ""    ABE  

Или мы можем сделать это быстрее, назначив на месте с помощью set из data.table

library(data.table)
setDT(wl)
for(j in nm1) set(wl, i=which(wl[[j]] == "Y"), j=j, names(wl)[j])
wl
#   x multi ABC ABD ABE
#1: 1     Y         ABE
#2: 2     Y ABC     ABE
#3: 3     Y ABC        
#4: 4     Y         ABE
person akrun    schedule 16.08.2018
comment
Спасибо за предложение! Но мне нужно, чтобы он работал с start_with (AB) или аналогично ему, потому что мой реальный фрейм данных намного больше, чем в моем примере, и я не хочу всегда вручную подсчитывать номера индексов столбцов. - person c.custer; 16.08.2018
comment
@ c.custer. Ладно, поменял на более динамичный - person akrun; 16.08.2018
comment
Отлично, спасибо! И спасибо за предоставление нескольких подходов. Всегда хорошо иметь больше инструментов - person c.custer; 16.08.2018
comment
Ваш пример tidyverse работает, но я, честно говоря, не понимаю, что он делает? Где / как записывается имя столбца? - person c.custer; 16.08.2018
comment
@ c.custer Это шаг deparse(substitute захватывает имя перед оценкой - person akrun; 16.08.2018