dplyr mutate a variable by comparing a variable and vectors of different sizes
Asked Answered
B

5

5

I have the dataframe of the following type

df <- tibble::tribble(~x,
                      c("A", "B"),
                      c("A", "B", "C"),
                      c("A", "B", "C", "D"),
                      c("A", "B"))

and vectors like these

vec1 <- c("A", "B")
vec2 <- c("A", "B", "C")
vec3 <- c("A", "B", "C", "D")

I want to mutate a variable y that shows which row has which vector. I tried the following, but getting the empty y variable with the warning: "longer object length is not a multiple of shorter object length"

df_new <- df %>%
  mutate(y = case_when(x == vec1 ~ "vec1",
                       x == vec2 ~ "vec2",
                       x == vec2 ~ "vec3"))

The desired output is

df_new <- tibble::tribble(~x,                      ~y,
                          c("A", "B"),             "vec1",
                          c("A", "B", "C"),        "vec2",
                          c("A", "B", "C", "D"),   "vec3",
                          c("A", "B"),             "vec1")
Bashaw answered 23/4, 2018 at 20:20 Comment(0)
D
4

A solution using map2_lgl and identical to assess if the vectors are the same.

library(tidyverse)

df_new <- df %>%
  mutate(y = case_when(
    map2_lgl(x, list(vec1), ~identical(.x, .y))  ~"vec1",
    map2_lgl(x, list(vec2), ~identical(.x, .y))  ~"vec2",
    map2_lgl(x, list(vec3), ~identical(.x, .y))  ~"vec3"
  ))
df_new
# # A tibble: 4 x 2
#   x         y    
#   <list>    <chr>
# 1 <chr [2]> vec1 
# 2 <chr [3]> vec2 
# 3 <chr [4]> vec3 
# 4 <chr [2]> vec1 
Dirham answered 23/4, 2018 at 20:27 Comment(2)
It worked, fantastic!! Do we need "df$" in map2_lgl statements?Bashaw
@Bashaw We don't need df$x, just x would work. Thanks for pointing that out. I have updated my answer.Dirham
M
2

Here's an alternative that's more programmatic - you don't need to specify each vector explicitly

Data

df <- tibble::tribble(~x,
                      c("A", "B"),
                      c("A", "B", "C"),
                      c("A", "B", "C", "D"),
                      c("A", "B"))

vec1 <- c("A", "B")
vec2 <- c("A", "B", "C")
vec3 <- c("A", "B", "C", "D")

Solution - takes advantage of ls(...) to return relevant vector names using a pattern

vecs <- ls(pattern="vec")
L <- lapply(vecs, get)
names(L) <- vecs
df %>%
  mutate(y = names(L)[match(x, L)])

# A tibble: 4 x 2
  # x         y    
  # <list>    <chr>
# 1 <chr [2]> vec1 
# 2 <chr [3]> vec2 
# 3 <chr [4]> vec3 
# 4 <chr [2]> vec1
Migration answered 23/4, 2018 at 20:47 Comment(1)
This worked too. I can also replace lapply with map and that would also work.Bashaw
K
1

This would work too:

comp <- list(vec1, vec2, vec3)

df %>% 
  mutate(y = map_chr(df$x, ~ paste0("vec", which(comp %in% list(.)))))

# A tibble: 4 x 2
  x         y    
  <list>    <chr>
1 <chr [2]> vec1 
2 <chr [3]> vec2 
3 <chr [4]> vec3 
4 <chr [2]> vec1 
Kerstinkerwin answered 23/4, 2018 at 21:5 Comment(0)
W
1

An option is to use compare::compareEqual with apply as:

library(dplyr)
library(compare)

df$y <- apply(df,1, function(x){
  dplyr::case_when(
    isTRUE(compareEqual(x[[1]], vec1)) ~ "vec1",
    isTRUE(compareEqual(x[[1]], vec2)) ~ "vec2",
    isTRUE(compareEqual(x[[1]],vec3)) ~ "vec3"
  )})



df
# # A tibble: 4 x 2
# x         y    
# <list>    <chr>
# 1 <chr [2]> vec1 
# 2 <chr [3]> vec2 
# 3 <chr [4]> vec3 
# 4 <chr [2]> vec1 
Willson answered 23/4, 2018 at 21:57 Comment(3)
I tried replacing compareEqual with the base function all.equal and that worked too. Nice to know about the package compare.Bashaw
@Bashaw Yea, you can even use identical. I wanted to show-case use of compare and just simple use of apply with case_when with my answer,Willson
Got it. That was helpful! I learned something new from your code.Bashaw
S
1

Unfortunately you can't do a left_join on exotic formats but we can cheat and join on the dput string:

library(tidyverse)
lkp <- enframe(map_chr(lst(vec1,vec2,vec3),~capture.output(dput(.x))))
df %>%
  mutate(value = map_chr(x,~capture.output(dput(.x)))) %>%
  left_join(lkp) %>%
  select(-value)

# # A tibble: 4 x 2
#           x  name
#      <list> <chr>
# 1 <chr [2]>  vec1
# 2 <chr [3]>  vec2
# 3 <chr [4]>  vec3
# 4 <chr [2]>  vec1
Septicidal answered 23/4, 2018 at 22:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.