【问题标题】:Broadcasting, multiple n*m returns instead of n*m element [Julia 1.0]广播,多个 n*m 返回而不是 n*m 元素 [Julia 1.0]
【发布时间】:2018-09-02 06:47:51
【问题描述】:

假设我想编写一个需要任意长度的数组作为参数输入的函数: 例如,

f = function(x,y) 
  z = x * y
  outputs = DataFrame(x = x, y = y, z = z)
  return(outputs)
end

返回 f.([1,2],[1,2]) 是两个 1x3 DataFrame 的 2 元素数组。但是,在这种情况下,我想要一个 2x3 DataFrame。

我可以通过在嵌套 for 循环之前定义 z 来实现这一点:

f = function(x,y)
  z=fill(0,length(x))
  for i in 1:length(x)
   z[i] = x[i] * y[i]
  end
  outputs = DataFrame(x = x, y = y, z = z)
  return(outputs)
end

在这里,f([1,2],[1, 2]) 得到了我想要的。但是,问题是我必须定义所有函数内变量两次并添加一个 for 循环,同时记住包含迭代变量 i。有什么我想念的吗?我的问题是,我如何获得我想要的 nm 元素而不是 nm 数组... 我试图关注这个Julia blog postthis Julia discussion post 也专门解决了这个问题,但我认为解决方案对于 1.0 来说已经过时了。

---- 编辑

使用 for 循环可以像使用点来表示元素操作一样工作。 我关心的更大的问题是一致性。 假设我有两个功能。一个函数 (f1) 返回一维输出,而另一个 (f2) 具有二维输出。

 function f1(x, y)
  z = x .* y
  DataFrame(x = x, y = y, z = z)
 end
 function f2(x, y)
  z = x * y
  return(z)
 end

x = [1,2]y = [1,2] 的正确调用是 f1([1,2], [1,2])f2.([1,2], [1,2])。 我在这里所说的不一致的是(从不知道内部功能代码的用户的角度来看),要获得zxy 的长度的输出, .f2 一起使用,但不是f1。我能看到的唯一解决方法是在f2 中定义z = .x * y(或者为每个索引循环使用一个)。在这种情况下,f1f2 都可以在没有点的情况下调用。这是一个合适的解决方案吗?需要明确的是,我的目标是f1f2 被用户以相同的方式调用,无论xy 是单元素数组还是多元素数组。如果xy 是单个元素并且. 如果每个变量都有多个元素,我的偏好是让用户调用两个函数而不使用点。这似乎不可能。因此,我必须学会忍受的部分是必须在我的函数中编写许多.[i](如果我想要“一致性”)。对吗?

或者,我可以添加文档,明确说明返回一个变量的函数需要在参数长度>1 时使用. 调用,而对于任何返回数据帧的函数都不需要使用. 调用原因。 [原谅对技术语言的任何滥用;我的背景是生态学]

【问题讨论】:

  • 这在 Julia 中称为 broadcasting,而不是循环融合。你可以编辑你的标题,让读者更清楚。
  • @Justin 你可以为此使用多个调度吗? docs.julialang.org/en/v1/manual/methods/index.html
  • 我最终使用了多次调度。我不知道这个功能。非常感谢。

标签: arrays dataframe scope julia


【解决方案1】:

这是你想要的吗?

julia> function f(x, y)
           z = x .* y
           DataFrame(x = x, y = y, z = z)
       end
f (generic function with 1 method)

julia> f([1,2], [1,2])
2×3 DataFrame
│ Row │ x │ y │ z │
├─────┼───┼───┼───┤
│ 1   │ 1 │ 1 │ 1 │
│ 2   │ 2 │ 2 │ 4 │

你也可以简称为f(x, y) = DataFrame(x = x, y = y, z = x .* y)

您编写函数定义的方式表明您了解 R。在 Julia 中,与 R 不同,标量和数组是完全分离的类型(例如 Float64Vector{Float64}),必须区别对待;但通常,只需在正确的位置添加足够的广播即可(并且通过在任何函数调用之后或任何运算符之前放置 . 来进行广播)。

为确保不要混淆这些内容,您可以在参数中添加类型:f(x::Vector{Float64}, y::Vector{Float64}) 或任何适合您的类型。

【讨论】:

  • 感谢您提出另一个可行的选择。由于我最关心其他人能否使用我的功能,我将在原始答案中详细说明。
【解决方案2】:

如果 x 和 y 是单个元素并且使用 .if 每个变量有多个元素,我的偏好是让用户调用两个函数而不使用点。

您需要一个专门处理参数类型的函数。编译后执行时最优雅和最快的方法是使用@generated 宏。

using DataFrames
@generated function f(a,b)
    if a<:Array && b<:Array
        code = quote
            DataFrame(x = a, y = b, z = a .* b)
        end
    else
        code = quote
            DataFrame(x = a, y = b, z = a * b)
        end
    end
    code
end

现在让我们测试一下。请注意函数行为如何取决于参数的类型(Float64 vs Int)。每个参数可以是Array 或标量。

julia> f(3,4)                    
1×3 DataFrame                    
│ Row │ x │ y │ z  │             
├─────┼───┼───┼────┤             
│ 1   │ 3 │ 4 │ 12 │             

julia> f(3,4.0)                  
1×3 DataFrame                    
│ Row │ x │ y   │ z    │         
├─────┼───┼─────┼──────┤         
│ 1   │ 3 │ 4.0 │ 12.0 │         

julia> f(3.0,[1,2,3])            
3×3 DataFrame                    
│ Row │ x   │ y │ z   │          
├─────┼─────┼───┼─────┤          
│ 1   │ 3.0 │ 1 │ 3.0 │          
│ 2   │ 3.0 │ 2 │ 6.0 │          
│ 3   │ 3.0 │ 3 │ 9.0 │    

julia> f([1,2,3],4)
3×3 DataFrame
│ Row │ x │ y │ z  │
├─────┼───┼───┼────┤
│ 1   │ 1 │ 4 │ 4  │
│ 2   │ 2 │ 4 │ 8  │
│ 3   │ 3 │ 4 │ 12 │      

julia> f([6,7,8],[1,2,3])        
3×3 DataFrame                    
│ Row │ x │ y │ z  │             
├─────┼───┼───┼────┤             
│ 1   │ 6 │ 1 │ 6  │             
│ 2   │ 7 │ 2 │ 14 │             
│ 3   │ 8 │ 3 │ 24 │             

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-12
    相关资源
    最近更新 更多