【问题标题】:Reproduce the `expand.grid` function from R in Julia在 Julia 中从 R 中重现 `expand.grid` 函数
【发布时间】:2015-04-24 03:19:15
【问题描述】:

expand.gridR 中一个非常方便的函数,用于计算多个列表的所有可能组合。以下是它的工作原理:

> x = c(1,2,3)
> y = c("a","b")
> z = c(10,12)
> d = expand.grid(x,y,z)
> d
   Var1 Var2 Var3
1     1    a   10
2     2    a   10
3     3    a   10
4     1    b   10
5     2    b   10
6     3    b   10
7     1    a   12
8     2    a   12
9     3    a   12
10    1    b   12
11    2    b   12
12    3    b   12

如何在 Julia 中重现此功能?

【问题讨论】:

  • 我考虑过构建n 嵌套循环,其中n 是变量的数量(在我上面的示例中为3:x,y,z)。每个循环都会遍历每个变量中唯一值的数量,但我很困惑,因为我无法提前知道我需要多少个循环。所以我正在考虑构建一个调用自身的函数,但是 1)我不知道这在 Julia 中是否可行,并且 2)这听起来像一个愚蠢复杂的解决方案,可能不会很有效。感谢您的评论。
  • 你见过 R 的 expand.grid 是怎么做的吗?它遍历 args 的数量,在每次迭代中添加一个列,这是源列的扭曲子集,具有各种嵌套的代表等等......
  • 检查here:“repeat 可用于扩展网格,如 R 的 expand.grid() 函数”和 here:“是否存在与 R 中的 expand.grid 类似的函数"(前两个谷歌点击...)
  • @Henrik,第一个链接不是很通用(虽然确实有效),第二个链接肯定很好,虽然确实依赖于一个包。
  • @IainDunning 感谢您的评论。我的评论旨在作为对 OP 的一个小插曲,他们很容易找到一些 Julia 代码来提出问题(你尝试过什么),然后解释为什么它不能满足他们的需求,例如'我希望更一般的','但是,这个解决方案依赖于一个包'(即正是你的评论)。干杯。

标签: r combinations julia


【解决方案1】:

感谢@Henrik 的评论:

x = [1,2,3]
y = ["a","b"]
z = [10,12]
d = collect(Iterators.product(x,y,z))

这是另一个使用列表理解的解决方案

reshape([ [x,y,z]  for x=x, y=y, z=z ],length(x)*length(y)*length(z))

【讨论】:

  • 应该注意第一个需要一个包,第二个是针对有限列表集的特殊情况(尽管对于任何特定情况都可以)
  • 确实如此!我添加了using Iterators。谢谢!
  • 从 Julia 1.1 开始,上面的 cmets 现在已经过时了,因为迭代器现在内置在带有 Iterators 命名空间的主 Julia 中。您只需编写 Iterators.product.
【解决方案2】:

这是我的完全(?)通用解决方案,使用递归、可变参数和 splatting:

function expandgrid(args...)
    if length(args) == 0
        return Any[]
    elseif length(args) == 1
        return args[1]
    else
        rest = expandgrid(args[2:end]...)
        ret  = Any[]
        for i in args[1]
            for r in rest
                push!(ret, vcat(i,r))
            end
        end
        return ret
    end
end

eg = expandgrid([1,2,3], ["a","b"], [10,12])
@assert length(eg) == 3*2*2
@show eg

这给出了一个数组数组,但如果这是你想要的,你可以将它简单地组合成一个矩阵。

【讨论】:

    【解决方案3】:

    我知道这是一个相当老的问题,但在找到这篇文章之前几天,我也几乎将 expand.grid 函数从 R 逐行转换为 Julia... 它对某人来说仍然很有趣,因为它返回一个DataFrame,这可能更方便。 这是link to the Gist,下面是代码以防万一:

    using DataFrames
    
    """
    Create a Data Frame from All Combinations of Factor Variables (see R's base::expand.grid)
    # Arguments
    ... Array, Dict, or Tuple containing at least one value
    # Return
    A DataFrame containing one row for each combination of the supplied argument. The first factors vary fastest.
    # Examples
    ```julia
    expand_grid([1,2],["owl","cat"])
    expand_grid((1,2),("owl","cat"))
    expand_grid((1,2)) # -> Returns a DataFrame with 2 rows of 1 and 2.
    ```
    """
    function expand_grid(args...)
        nargs= length(args)
    
        if nargs == 0
          error("expand_grid need at least one argument")
        end
    
        iArgs= 1:nargs
        nmc= "Var" .* string.(iArgs)
        nm= nmc
        d= map(length, args)
        orep= prod(d)
        rep_fac= [1]
        # cargs = []
    
        if orep == 0
            error("One or more argument(s) have a length of 0")
        end
    
        cargs= Array{Any}(undef,orep,nargs)
    
        for i in iArgs
            x= args[i]
            nx= length(x)
            orep= Int(orep/nx)
            mapped_nx= vcat(map((x,y) -> repeat([x],y), collect(1:nx), repeat(rep_fac,nx))...)
            cargs[:,i] .= x[repeat(mapped_nx,orep)]
            rep_fac= rep_fac * nx
        end
    
        convert(DataFrame,cargs)
    end
    

    【讨论】:

      【解决方案4】:

      我知道这是一个老问题,但万一有人仍在寻找像 R expand.grid 函数一样工作的解决方案(即传递任何类型的命名变量列表并返回变量名称为的数据框列名、原始变量类型的每一列以及不同变量的所有可能组合),这是我的 Julia 新手尝试:

      using DataFrames
      
      function expand_grid(; iters...)
          var_names = collect(keys(iters))
          var_itr = [1:length(x) for x in iters.data]
          var_ix = vcat([collect(x)' for x in Iterators.product(var_itr...)]...)
          out = DataFrame()
          for i = 1:length(var_names)
              out[:,var_names[i]] = collect(iters[i])[var_ix[:,i]]
          end
          return out
      end
      
      expand_grid(a=1:2, b=1.0:5.0, c=["one", "two", "three", "four"])
      

      很可能有一种更有效或更清洁的方法来做到这一点,但这是我能想到的最好的方法,它可以满足我对 R 函数的期望。

      【讨论】:

        猜你喜欢
        • 2012-08-21
        • 2015-07-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多