Paste variable names in a function R

Viewed 53

I need to transform a dataframe in the following way:

year1 <- rep(2001, 5)
b <- c("","","120","","131")
d <- c(letters[1:5])
year2 <- c("","","2002","","2004")

dat <- data.frame(cbind(a,d,year1,b,year2))
dat 

    a d year1   b year2
1 249 a  2001          
2 234 b  2001          
3 273 c  2001 120  2002
4 201 d  2001          
5 238 e  2001 131  2004

This is the required output below. Basically, I need to move the values from b and year 2 under a and year 1 and repeat all other information for this record.

    a d year1    b year2 id
1 249 a  2001             1
2 234 b  2001             2
3 273 c  2001  120  2002  3
4 201 d  2001             4
5 238 e  2001  131  2004  5
6 120 c  2002 <NA>  <NA>  3
7 131 e  2004 <NA>  <NA>  5 

I have written a function because I need to do the same on multiple data set. Here is my problem: the column names might vary. How can I improve the function below, so that I can change the names of the columns in the inputs. I have marked the lines where I need help.

Here is my approach below:

fix_records <- function(dat=dat, event1=c("a","year1"), event2 = c("b","year2")) {
dat$id <- 1:nrow(dat)
dat1 <- dat[ , -which(names(dat) %in% event1)] 
dat2 <- dat1[which(dat1$year2!=""),] # NEED HELP HERE: how to pass year2 in a generic way? 
names(dat2) <- c("d", "a", "year1", "id") # Is there a way to link this line to the arguments? 
dat3 <- dplyr::bind_rows(dat, dat2)
return(dat3)
}

2 Answers

You could work with data tables instead of data frames.

library(data.table)

a <- c("249", "234", "273", "201", "238")
year1 <- rep("2001", 5)
b <- c("","","120","","131")
d <- c(letters[1:5])
year2 <- c("","","2002","","2004")
dat <- data.table(a, d, year1, b, year2)

fix_records <- function(dat, event1 = c("a", "year1"), event2 = c("b", "year2")) {
  col_name <- setdiff(names(dat), c(event1, event2))
  dat$id <- 1:nrow(dat)
  dat2 <- dat[get(event2[2]) != "", ]
  dat2 <- dat2[, (event1) := NULL]
  names(dat2) <- c(col_name, event1, "id")
  dat3 <- dplyr::bind_rows(dat, dat2)
  return(dat3)
}

You could use dplyr:

library(dplyr)

fix_records <- function(df, event1 = c("a", "year1"), event2 = c("b", "year2")) {
  
  df <- df %>%
    mutate(id = row_number()) 
  
  df %>% 
    filter(!!sym(event2[2]) != "") %>% 
    select(
      id,
      d,
      !!sym(event1[1]) := !!sym(event2[1]),
      !!sym(event1[2]) := !!sym(event2[2])
      ) %>% 
    bind_rows(df, .)
}

fix_records(df)

#>     a d year1    b year2 id
#> 1 249 a  2001             1
#> 2 234 b  2001             2
#> 3 273 c  2001  120  2002  3
#> 4 201 d  2001             4
#> 5 238 e  2001  131  2004  5
#> 6 120 c  2002 <NA>  <NA>  3
#> 7 131 e  2004 <NA>  <NA>  5
Related