Home > Net >  Is there a method to preserve rownames when using bind_cols?
Is there a method to preserve rownames when using bind_cols?

Time:08-05

Here is a small piece of code creating three named vectors, all with the same three names of the elements:

# Create three similarly named vectors
vec_1 <- c("element_one" = 3, "element_two" = 7, "element_three" = 9)
vec_2 <- c("element_one" = 7, "element_two" = 2, "element_three" = 4)
vec_3 <- c("element_one" = 10, "element_two" = 16, "element_three" = 1)

I am looking to find a simple and elegant solution to the problem of combining multiple similarly named vectors using the bind_cols function, whilst maintaining those names as a new column in the final tibble. Up to this point I have used the base r cbind function to achieve this.

# Combined the three vectors by their names
combined_vec <- cbind(vec_1, vec_2 = vec_2[names(vec_1)], vec_3 = vec_3[names(vec_1)])

# Create a tibble that keeps the vector names as a column
new_combined <- combined_vec %>%
    as.data.frame() %>%
    rownames_to_column() %>%
    tibble()

Is there a way of achieving this more simply, for example, with the bind_cols function? Or can anyone suggest a simpler solution to achieve this result?

CodePudding user response:

You could use enframe() to convert each named vector to a 2-column dataframe and merge them by their names.

library(tidyverse)

mget(ls(pattern = 'vec_')) %>%
  imap(~ enframe(.x, name = "id", value = .y)) %>%
  reduce(left_join, by = "id")

# # A tibble: 3 × 4
#   id            vec_1 vec_2 vec_3
#   <chr>         <dbl> <dbl> <dbl>
# 1 element_one       3     7    10
# 2 element_two       7     2    16
# 3 element_three     9     4     1

CodePudding user response:

We can convert named vectors into dataframes using stack, then use merge with Reduce to merge multiple dataframes:

Reduce(function(...) merge(..., all = TRUE, by = "ind"), 
       lapply(mget(ls(pattern = 'vec_')), stack))
#             ind values.x values.y values
# 1   element_one        3        7     10
# 2 element_three        9        4      1
# 3   element_two        7        2     16

CodePudding user response:

Assuming identically named vectors (I'm not sure how you would resolve the ambiguity with similarly named columns), how about:

library(tibble)
library(magrittr)
library(dplyr)

tibble() %>% 
  bind_rows(list(vec_1, vec_2, vec_3)) %>% 
  add_column(id=names(vec_1), .before=1)
# A tibble: 3 × 4
  id            element_one element_two element_three
  <chr>               <dbl>       <dbl>         <dbl>
1 element_one             3           7             9
2 element_two             7           2             4
3 element_three          10          16             1

Or, a general solution:

doIt <- function(...) {
  x <- list(...)
  tibble() %>% 
    bind_rows(x) %>% 
    add_column(id=names(x[[1]]), .before=1)
}

doIt(vec_1, vec_2, vec_3)
# A tibble: 3 × 4
  id            element_one element_two element_three
  <chr>               <dbl>       <dbl>         <dbl>
1 element_one             3           7             9
2 element_two             7           2             4
3 element_three          10          16             1

If @DarrenTsai is correct, then

tibble(vec_1, vec_2, vec_3) %>% 
  add_column(id=names(vec_1), .before=1) 
# A tibble: 3 × 4
  id            vec_1 vec_2 vec_3
  <chr>         <dbl> <dbl> <dbl>
1 element_one       3     7    10
2 element_two       7     2    16
3 element_three     9     4     1

appears to give the required result.

  •  Tags:  
  • r
  • Related