17
\$\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\$

4 Answers 4

17
\$\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"  
\$\endgroup\$
0
6
\$\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
\$\endgroup\$
6
\$\begingroup\$

Well, it can be still useful now-a-days.

a=c('europe', 'asia')
b=c('co2', 'temperature')
c=c('min', 'max')

apply(expand.grid(a,b,c), 1, function(x) paste0(x, collapse="_"))

Output:

[1] "europe_co2_min"         "asia_co2_min"           "europe_temperature_min"
[4] "asia_temperature_min"   "europe_co2_max"         "asia_co2_max"          
[7] "europe_temperature_max" "asia_temperature_max"
\$\endgroup\$
0
\$\begingroup\$

Maybe someone will still be served. I modified one of the codes they provided here, to create a tablet, although I'm not sure it's the best way to do it. Perhaps someone with more experience can tell us.

a <- c("a","b","o")
comb <- apply(expand.grid(a,a), 1, function(x) paste0(x, collapse=","))
tb <- matrix(comb,ncol = length(a),nrow=length(a))
colnames(tb) <- a
row.names(tb) <- a

Returns:

      a    b    o
a    a,a  a,b  a,c 
b    b,a  b,b  b,o 
o    o,a  o,b  o,o
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Hi Samuel, all content on Code Review must be in English. I have used Google Translate to convert your answer to English. \$\endgroup\$
    – Peilonrayz
    Commented Oct 23, 2021 at 0:35
  • \$\begingroup\$ Welcome to Code Review! You have presented an alternative solution, but haven't reviewed the code. Please edit to show what aspects of the question code prompted you to write this version, and in what ways it's an improvement over the original. It may be worth (re-)reading How to Answer. \$\endgroup\$ Commented Oct 23, 2021 at 7:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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