Reputation: 419
I am looking for a way to conditionally replace certain values in a subset of columns with that column name staying in the tidyverse. See example below:
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
except it would be the actual column name instead of 'column name'. I've used two different answers on here to get to both mutating on conditional columns (which is how I got to my example) and how to replace certains values with a column name (not used in my current example but the answer is here).
I can combine the two answers to get it to work:
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
I acknowledge the process above is completely functional but out of pure curiosity, is there a way to do this without the second chunk of code? Is there some function I can plug into the values=
portion of the replace(.,.=='Y',values="column name")
step that can capture the column name and perform this entire process within a single mutate_at
function?
Upvotes: 3
Views: 1847
Reputation: 887213
With 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
Using 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
Or we can do this faster by assigning in place with set
from 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
Upvotes: 1
Reputation: 26343
Here is an option with 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
Upvotes: 1
Reputation: 79238
You can use 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
Upvotes: 1
Reputation: 16842
You can do a little tidyeval in your mutate_at
function to get the column name, then an ifelse
(or whatever other logic structure you might want) to replace certain values.
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
Created on 2018-08-16 by the reprex package (v0.2.0).
Upvotes: 2