J'ai trois variables indépendantes ou plus représentées comme des vecteurs R, comme ceci:
A <- c(1,2,3)
B <- factor(c('x','y'))
C <- c(0.1,0.5)
et je veux prendre le produit cartésien de tous et mettre le résultat dans un bloc de données, comme ceci:
A B C
1 x 0.1
1 x 0.5
1 y 0.1
1 y 0.5
2 x 0.1
2 x 0.5
2 y 0.1
2 y 0.5
3 x 0.1
3 x 0.5
3 y 0.1
3 y 0.5
Je peux le faire en écrivant manuellement des appels à rep
:
d <- data.frame(A = rep(A, times=length(B)*length(C)),
B = rep(B, times=length(A), each=length(C)),
C = rep(C, each=length(A)*length(B))
mais il doit y avoir une manière plus élégante de le faire, non? product
in itertools
fait partie du travail, mais je ne trouve aucun moyen d'absorber la sortie d'un itérateur et de la mettre dans un bloc de données. Aucune suggestion?
p.s. La prochaine étape de ce calcul ressemble à
d$D <- f(d$A, d$B, d$C)
donc si vous connaissez un moyen de faire les deux étapes à la fois, ce serait également utile.
vous pouvez utiliser expand.grid(A, B, C)
EDIT: une alternative à l'utilisation de do.call pour réaliser la deuxième partie, est la fonction mdply. voici le code
d = expand.grid(x = A, y = B, z = C)
d = mdply(d, f)
pour illustrer son utilisation en utilisant une fonction triviale 'coller', vous pouvez essayer
d = mdply(d, 'paste', sep = '+');
Il y a une fonction qui manipule la trame de données, ce qui est utile dans ce cas.
Il peut produire diverses jointures (dans la terminologie SQL), tandis que le produit cartésien est un cas particulier.
Vous devez d'abord convertir les variables en trames de données, car elles prennent les trames de données comme paramètres.
donc quelque chose comme ça fera:
A.B=merge(data.frame(A=A), data.frame(B=B),by=NULL);
A.B.C=merge(A.B, data.frame(C=C),by=NULL);
La seule chose à prendre en compte est que les lignes ne sont pas triées comme vous l'avez décrit. Vous pouvez les trier manuellement comme vous le souhaitez.
merge(x, y, by = intersect(names(x), names(y)),
by.x = by, by.y = by, all = FALSE, all.x = all, all.y = all,
sort = TRUE, suffixes = c(".x",".y"),
incomparables = NULL, ...)
"Si par ou les deux by.x et by.y sont de longueur 0 (un vecteur de longueur zéro ou NULL), le résultat, r, est le produit cartésien de x et y"
voir cette URL pour plus de détails: http://stat.ethz.ch/R-manual/R-patched/library/base/html/merge.html
Voici une façon de faire les deux, en utilisant la suggestion de Ramnath de expand.grid
:
f <- function(x,y,z) paste(x,y,z,sep="+")
d <- expand.grid(x=A, y=B, z=C)
d$D <- do.call(f, d)
Notez que do.call
fonctionne sur d
"tel quel" car un data.frame
est un list
. Mais do.call
attend que les noms de colonne de d
correspondent aux noms d'arguments de f
.
Pensez à utiliser la merveilleuse bibliothèque data.table pour l'expressivité et la vitesse. Il gère de nombreux cas d'utilisation plyr (groupement relationnel par), ainsi que la transformation, le sous-ensemble et la jointure relationnelle en utilisant une syntaxe uniforme assez simple.
library(data.table)
d <- CJ(x=A, y=B, z=C) # Cross join
d[, w:=f(x,y,z)] # Mutates the data.table
ou en une seule ligne
d <- CJ(x=A, y=B, z=C)[, w:=f(x,y,z)]
Avec la bibliothèque tidyr
on peut utiliser tidyr::crossing
(l'ordre sera comme dans OP):
library(tidyr)
crossing(A,B,C)
# A tibble: 12 x 3
# A B C
# <dbl> <fct> <dbl>
# 1 1 x 0.1
# 2 1 x 0.5
# 3 1 y 0.1
# 4 1 y 0.5
# 5 2 x 0.1
# 6 2 x 0.5
# 7 2 y 0.1
# 8 2 y 0.5
# 9 3 x 0.1
# 10 3 x 0.5
# 11 3 y 0.1
# 12 3 y 0.5
L'étape suivante serait d'utiliser tidyverse
et surtout le purrr::pmap*
famille:
library(tidyverse)
crossing(A,B,C) %>% mutate(D = pmap_chr(.,paste,sep="_"))
# A tibble: 12 x 4
# A B C D
# <dbl> <fct> <dbl> <chr>
# 1 1 x 0.1 1_1_0.1
# 2 1 x 0.5 1_1_0.5
# 3 1 y 0.1 1_2_0.1
# 4 1 y 0.5 1_2_0.5
# 5 2 x 0.1 2_1_0.1
# 6 2 x 0.5 2_1_0.5
# 7 2 y 0.1 2_2_0.1
# 8 2 y 0.5 2_2_0.5
# 9 3 x 0.1 3_1_0.1
# 10 3 x 0.5 3_1_0.5
# 11 3 y 0.1 3_2_0.1
# 12 3 y 0.5 3_2_0.5
Utilisation de la jointure croisée dans sqldf
:
library(sqldf)
A <- data.frame(c1 = c(1,2,3))
B <- data.frame(c2 = factor(c('x','y')))
C <- data.frame(c3 = c(0.1,0.5))
result <- sqldf('SELECT * FROM (A CROSS JOIN B) CROSS JOIN C')
Je ne me souviens jamais de cette fonction standard expand.grid
. Voici donc une autre version.
crossproduct <- function(...,FUN='data.frame') {
args <- list(...)
n1 <- names(args)
n2 <- sapply(match.call()[1+1:length(args)], as.character)
nn <- if (is.null(n1)) n2 else ifelse(n1!='',n1,n2)
dims <- sapply(args,length)
dimtot <- prod(dims)
reps <- rev(cumprod(c(1,rev(dims))))[-1]
cols <- lapply(1:length(dims), function(j)
args[[j]][1+((1:dimtot-1) %/% reps[j]) %% dims[j]])
names(cols) <- nn
do.call(match.fun(FUN),cols)
}
A <- c(1,2,3)
B <- factor(c('x','y'))
C <- c(.1,.5)
crossproduct(A,B,C)
crossproduct(A,B,C, FUN=function(...) paste(...,sep='_'))