14
\$\begingroup\$

Sometimes I need to create character vectors like this one:

datanames <- c(
  "europe_co2_min",
  "europe_co2_max",
  "europe_temperature_min",
  "europe_temperature_max",
  "asia_co2_min",
  "asia_co2_max",
  "asia_temperature_min",
  "asia_temperature_max"
)

Notice that this is actually the Cartesian product of the character vectors joined together with a _:

c('europe', 'asia')
c('co2', 'temperature')
c('min', 'max')

I came up with this helper function:

combine <- function(..., prefix='', sep='_') {
  combine.inner <- function(lx, ...) {
    if (length(c(...)) > 0) {
      sapply(sapply(lx, function(x) paste(x, combine.inner(...), sep=sep)), c)
    } else {
      lx
    }
  }
  paste(prefix, combine.inner(...), sep='')
}

It handles arbitrary number of character vector parameters of arbitrary lengths. The prefix parameter is for convenience. It's similar to having a single element vector as the first parameter, except the common separator sep will not be applied after it.

  1. Is there an easier way that exists in R and I missed?
  2. Is there anything smelly about this code?
\$\endgroup\$
13
\$\begingroup\$

Your approach is an example of good R code. However, there is a base function that allows creating a cartesian product of strings, interaction. This function creates a factor, and the levels are equivalent to the cartesian product.

Furthermore, instead of paste(..., sep = "") you can use paste0(...).

If you use these functions, your code will be much shorter:

combine <- function(..., prefix = "", sep = "_") {
  paste0(prefix, levels(interaction(..., sep = sep)))
}

Examples:

s1 <- c('europe', 'asia')
s2 <- c('co2', 'temperature')
s3 <- c('min', 'max')

combine(s1)
# [1] "europe" "asia"  
combine(s1, s2)
# [1] "europe_co2"         "europe_temperature" "asia_co2"           "asia_temperature"  
combine(s1, s2, s3)
# [1] "europe_co2_min"         "europe_co2_max"         "europe_temperature_min"
# [4] "europe_temperature_max" "asia_co2_min"           "asia_co2_max"          
# [7] "asia_temperature_min"   "asia_temperature_max"  
|improve this answer|||||
\$\endgroup\$
3
\$\begingroup\$

You can use expand.grid:

a <- c('europe', 'asia')
b <- c('co2', 'temperature')
c <- c('min', 'max')
expand.grid(a, b, c)

Output:

    Var1        Var2 Var3
1 europe         co2  min
2   asia         co2  min
3 europe temperature  min
4   asia temperature  min
5 europe         co2  max
6   asia         co2  max
7 europe temperature  max
8   asia temperature  max
|improve this answer|||||
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.