Michel
Michel

Reputation: 23

expand.grid with separate variable for each column

I would like to achieve the following data.frame in R:

    i1   i2   i3
1   A1   A2   A3
2   No   A2   A3
3   A1   No   A3
4   No   No   A3
5   A1   A2   No
6   No   A2   No
7   A1   No   No
8   No   No   No

In each column the variable can either be the concatenated string "A" and the column number or "No". The data.frame should contain all possible combinations.

My idea was to use expand.grid, but I don't know how to create the list dynamically. Or is there a better approach?

expand.grid(list(c("A1", "No"), c("A2", "No"), c("A3", "No")))

Upvotes: 2

Views: 1014

Answers (3)

hvollmeier
hvollmeier

Reputation: 2986

Another option, only using the most fundamental functions in R, is to use the indices:

df <- data.frame(V1 = c('A','A','A', 'A',rep('No',4)), V2 = c('A','A','No','No','A','A','No','No'), V3 = c('A','No','A','No','A','No','A','No'), stringsAsFactors = FALSE)

to get the row and col indices of the elements we need to change:

rindex <- which(df != 'No') %% nrow(df)
cindex <- ceiling(which(df != 'No')/nrow(df))

the solution is basically a one-liner:

df[matrix(c(rindex,cindex),ncol=2)] <- paste0(df[matrix(c(rindex,cindex),ncol=2)],cindex)

> df

  V1 V2 V3
1 A1 A2 A3
2 A1 A2 No
3 A1 No A3
4 A1 No No
5 No A2 A3
6 No A2 No
7 No No A3
8 No No No

Upvotes: 0

David Arenburg
David Arenburg

Reputation: 92282

I guess you could create your own helper function, something like that

MyList <- function(n) expand.grid(lapply(paste0("A", seq_len(n)), c, "No"))

Then simply pass it the number of elements (e.g., 3)

MyList(3)
#   Var1 Var2 Var3
# 1   A1   A2   A3
# 2   No   A2   A3
# 3   A1   No   A3
# 4   No   No   A3
# 5   A1   A2   No
# 6   No   A2   No
# 7   A1   No   No
# 8   No   No   No

Alternatively, you could also try data.tables CJ equivalent which should much more efficient than expand.grid for a big n

library(data.table)
DTCJ <- function(n) do.call(CJ, lapply(paste0("A", seq_len(n)), c, "No"))
DTCJ(3) # will return a sorted cross join
#    V1 V2 V3
# 1: A1 A2 A3
# 2: A1 A2 No
# 3: A1 No A3
# 4: A1 No No
# 5: No A2 A3
# 6: No A2 No
# 7: No No A3
# 8: No No No

Upvotes: 6

akrun
akrun

Reputation: 886948

Another option is using Map with expand.grid

 n <- 3
 expand.grid(Map(c, paste0('A', seq_len(n)), 'NO'))

Or

 expand.grid(as.data.frame(rbind(paste0('A', seq_len(n)),'NO')))

Upvotes: 2

Related Questions