【问题标题】:Cartesian product data frame笛卡尔积数据框
【发布时间】:2010-11-29 23:41:00
【问题描述】:

我有三个或更多表示为 R 向量的自变量,如下所示:

A <- c(1,2,3)
B <- factor(c('x','y'))
C <- c(0.1,0.5)

我想取所有这些的笛卡尔积并将结果放入数据框中,如下所示:

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

我可以通过手动写出对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))

但必须有更优雅的方式来做到这一点,是吗? product in itertools 完成了部分工作,但我找不到任何方法来吸收迭代器的输出并将其放入数据框中。有什么建议吗?

附言这个计算的下一步看起来像

d$D <- f(d$A, d$B, d$C)

因此,如果您知道同时执行这两个步骤的方法,那也会很有帮助。

【问题讨论】:

  • 如果你指定函数 f 的作用会很有用。
  • f 是几种不同的数学计算之一的占位符,但就这个问题而言,我认为你需要知道的是它们都采用N个适当类型的向量并产生一个向量;所有输入的长度必须相同,输出也是该长度。
  • 我建议更改此问题的标题...“数据表”现在在 R 中的含义有所不同。
  • @random_forest_fanatic 我将其更改为“数据框”。如果这不是你的意思,请澄清。 (我不知道你在说什么,但我的意思总是一个数据框架,而且标题确实是我的草率。)
  • @Frank 我挑剔的原因是因为我找到了这个问题,因为我正在搜索标题所说的内容:如何用 R 中的 data.table 做笛卡尔积。这个问题没有与该主题有关,因此我建议对其进行更改以避免将来出现混淆/误导。

标签: r dataframe


【解决方案1】:

您可以使用expand.grid(A, B, C)


编辑:使用do.call 来实现第二部分的替代方法是plyr 包中的函数mdply

library(plyr)

d = expand.grid(x = A, y = B, z = C)
d = mdply(d, f)

使用一个简单的函数'paste'来说明它的用法,你可以试试

d = mdply(d, 'paste', sep = '+');

【讨论】:

  • 啊哈!我知道必须有一个标准库例程来执行此操作,但找不到它的名称。不过,如果有人对第二部分有答案,我将保留这个问题。
  • 如果 f 是一个自定义函数,那么你可以修改它以接受一个数据框作为参数,并让函数处理拆分成分量向量
  • 一直盯着 plyr 文档,但没有意识到这是 mdply 的用途。谢谢。
【解决方案2】:

有一个操作数据框的函数,在这种情况下很有帮助。

它可以产生各种连接(用SQL术语来说),而笛卡尔积是一个特例。

您必须先将变量转换为数据帧,因为它以数据帧作为参数。

这样就可以了:

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);

唯一需要注意的是行没有按照您描述的那样排序。 您可以根据需要手动对其进行排序。

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, ...)

“如果 by 或两者 by.x 和 by.y 的长度为 0(长度为零的向量或 NULL),则结果 r 是 x 和 y 的笛卡尔积”

查看此网址了解详情:http://stat.ethz.ch/R-manual/R-patched/library/base/html/merge.html

【讨论】:

    【解决方案3】:

    使用库tidyr 可以使用tidyr::crossing(顺序与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 
    

    下一步是使用tidyverse,尤其是purrr::pmap* 系列:

    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
    

    【讨论】:

      【解决方案4】:

      考虑使用出色的 data.table 库来提高表现力和速度。它使用相当简单的统一语法处理许多 plyr 用例(关系分组依据),以及变换、子集和关系连接。

      library(data.table)
      d <- CJ(x=A, y=B, z=C)  # Cross join
      d[, w:=f(x,y,z)]  # Mutates the data.table
      

      或一行

      d <- CJ(x=A, y=B, z=C)[, w:=f(x,y,z)]
      

      【讨论】:

        【解决方案5】:

        使用 Ramnath 对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)
        

        请注意,do.call 按“原样”在d 上工作,因为data.framelist。但是do.call 期望d 的列名与f 的参数名相匹配。

        【讨论】:

        • @Zack:谢谢;我已经更新了我的回复。这不是单行的,但使用 do.call 评估 f 仍然比输入每个参数更容易。
        【解决方案6】:

        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') 
        

        【讨论】:

          【解决方案7】:

          我永远记不起那个标准函数expand.grid。所以这是另一个版本。

          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='_'))
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-06-16
            • 1970-01-01
            相关资源
            最近更新 更多