John
John

Reputation: 403

In R Creating flexible functions that works with different types of input

I wonder how to think about making R functions flexible to different types of input such as vectors, lists, dataframes and tibbles. Please see examples below. And specifically wonder:

Are there better approaches to this? Are there different aspects that are important to think about when addressing this problem?

# Example data
x <- c(1, 2)
y <- c(1, 2)

# Example function
add_numbers <- function(x, y){
  z <- x+y
  z
}
add_numbers(x, y)

# They function does not work with lists as input!
x_list <- list(c(1, 2))
y_list <- list(c(1, 2))
add_numbers(x_list, y_list)

# The 2 examples below works, but provide different names for column in output.
x_tb <- tibble::as_tibble_col(c(1, 2))
y_tb <- tibble::as_tibble_col(c(1, 2))
z_tb <- add_numbers(x_tb, y_tb)
z_tb$value # Note the column name is different in output

x_df <- as.data.frame(c(1, 2))
y_df <- as.data.frame(c(1, 2))
z_df <- add_numbers(x_df, y_df)
z_df$`c(1, 2)` # Note the column name is different in output


# Potential solution for a more flexibl function
add_numbers_flexible <- function(x, y) {
  
  # If vec make tibble
  if(is.vector(x)  && length(x)>1) {
    x <- tibble::as_tibble_col(x)
  }
  if(is.vector(y)  && length(y)>1) {
    y <- tibble::as_tibble_col(y)
  }
  
  # Sort out if input is list
  if(is.list(x)){
    x <- tibble::as_tibble_col(x[[1]])
  }
  if(is.list(y)){
    y <- tibble::as_tibble_col(y[[1]])
  }
  
  # Sort out if input is data.frame
  if(is.data.frame(x)){
    colnames(x) <- "value"
  }
  if(is.data.frame(y)){
    colnames(y) <- "value"
  }
  
  z <- x+y
  z
}

# These now works, providing similar output
add_numbers_flexible(x, y)
add_numbers_flexible(x_list, y_list)
add_numbers_flexible(x_df, y_df)
add_numbers_flexible(x_tb, y_tb)

Upvotes: 1

Views: 136

Answers (1)

csgroen
csgroen

Reputation: 2541

For this particular example, unlisting each object before adding would work for all example use cases.

x <- c(1, 2); y <- c(1, 2)
x_list <- list(c(1, 2)); y_list <- list(c(1, 2))
x_tb <- tibble::as_tibble_col(c(1, 2)); y_tb <- tibble::as_tibble_col(c(1, 2))
x_df <- as.data.frame(c(1, 2)); y_df <- as.data.frame(c(1, 2))

add_numbers_flex2 <- function(x, y) {
    x2 <- unlist(x)
    y2 <- unlist(y)
    return(x2 + y2)
}

add_numbers_flex2(x,y)
#> [1] 2 4
add_numbers_flex2(x_list, y_list)
#> [1] 2 4
add_numbers_flex2(x_tb, y_tb)
#> value1 value2 
#>      2      4
add_numbers_flex2(x_df, y_df)
#> c(1, 2)1 c(1, 2)2 
#>        2        4

In fact, it would even work by mixing types, though I'm not sure weather that's desirable:

add_numbers_flex2(x_df, y)
#> c(1, 2)1 c(1, 2)2 
#>        2        4

Upvotes: 1

Related Questions