【问题标题】:Conditional assignment syntax for array of vectors向量数组的条件赋值语法
【发布时间】:2019-08-05 15:09:57
【问题描述】:

我是 Julia 新手,更多来自 Matlab 和 Python。

我无法理解向量数组的条件赋值的 Julia 语法。对我来说,这似乎与数字数组的条件赋值不一致。

对于数字数组,我可以执行以下操作:

a = [1,2,3]
b = [0,1,1]
a[b.>0] .= 5

这会将 a 的两个元素替换为新值 5。

我的实际工作示例是使用 Array{Int64,1} 类型元素的数组:

a = [[1,0,0], [0,1,0], [0,0,1]]
b = [0,1,1]
a[b.>0] .= Int64[0,0,5]

这不起作用,输出是: ERROR: DimensionMismatch("array could not be broadcast to match destination")

但是,这条线有效:

a[b.>0] .= [Int64[0,0,5]]

我无法理解这一点,因为在后一种情况下,按元素赋值 (.=) 对我来说更没有意义,因为左右两个数组的大小不同。

谁能解释一下?

提前致谢。

【问题讨论】:

    标签: julia


    【解决方案1】:

    操作:

    x .= y
    

    尝试遍历xy 并执行分配。一个简单的例子是:

    julia> x = [1,2,3,4]
    4-element Array{Int64,1}:
     1
     2
     3
     4
    
    julia> y = [12,14]
    2-element Array{Int64,1}:
     12
     14
    
    julia> x[[2,4]] .= y
    2-element view(::Array{Int64,1}, [2, 4]) with eltype Int64:
     12
     14
    
    julia> x
    4-element Array{Int64,1}:
      1
     12
      3
     14
    

    我们看到左侧和右侧有 2 个元素,因此可以执行就地分配。

    然后 Julia 有一个特殊规则,如果右侧的容器长度为 1,它可以扩展以匹配左侧的大小(这也适用于大于 1 的维度,但让我们关注一个简​​单的案例)。

    例如你有:

    julia> x = [1,2,3,4]
    4-element Array{Int64,1}:
     1
     2
     3
     4
    
    julia> x[[2,4]] .= 11
    2-element view(::Array{Int64,1}, [2, 4]) with eltype Int64:
     11
     11
    
    julia> x
    4-element Array{Int64,1}:
      1
     11
      3
     11
    
    julia> length(11)
    1
    
    julia> x[[2,4]] .= [11]
    2-element view(::Array{Int64,1}, [2, 4]) with eltype Int64:
     11
     11
    
    julia> x
    4-element Array{Int64,1}:
      1
     11
      3
     11
    
    julia> length([1])
    1
    

    这里需要注意的一个关键点是 [1]1 在这种情况下的行为完全相同,因为数字被认为是在广播中保存该数字的 1 元素容器。

    现在来看你的例子:

    a = [[1,0,0], [0,1,0], [0,0,1]]
    b = [0,1,1]
    a[b.>0] .= Int64[0,0,5]
    

    失败是因为:

    julia> length(a[b.>0])
    2
    
    julia> length(Int64[0,0,5])
    3
    

    我们发现尺寸不匹配。

    但是在:

    a[b.>0] .= [Int64[0,0,5]]
    

    你有:

    julia> length([Int64[0,0,5]])
    1
    

    所以长度为 1 的容器会被扩展。

    但是请注意,您很可能不想进行a[b.>0] .= [Int64[0,0,5]] 赋值,因为a 将拥有相同的数组Int64[0,0,5]。例如

    julia> a = [[1,0,0], [0,1,0], [0,0,1]]
    3-element Array{Array{Int64,1},1}:
     [1, 0, 0]
     [0, 1, 0]
     [0, 0, 1]
    
    julia> b = [0,1,1]
    3-element Array{Int64,1}:
     0
     1
     1
    
    julia> a[b.>0] .= [Int64[0,0,5]]
    2-element view(::Array{Array{Int64,1},1}, [2, 3]) with eltype Array{Int64,1}:
     [0, 0, 5]
     [0, 0, 5]
    
    julia> a
    3-element Array{Array{Int64,1},1}:
     [1, 0, 0]
     [0, 0, 5]
     [0, 0, 5]
    
    julia> a[2][1] = 100
    100
    
    julia> a
    3-element Array{Array{Int64,1},1}:
     [1, 0, 0]
     [100, 0, 5]
     [100, 0, 5]
    

    在大多数情况下,这不是您想要的。例如,一个更安全的方法是像这样的for 循环:

    julia> a = [[1,0,0], [0,1,0], [0,0,1]]
    3-element Array{Array{Int64,1},1}:
     [1, 0, 0]
     [0, 1, 0]
     [0, 0, 1]
    
    julia> b = [0,1,1]
    3-element Array{Int64,1}:
     0
     1
     1
    
    julia> for i in axes(b, 1)
               b[i] > 0 && (a[i] = Int64[0,0,5])
           end
    
    julia> a
    3-element Array{Array{Int64,1},1}:
     [1, 0, 0]
     [0, 0, 5]
     [0, 0, 5]
    
    julia> a[2][1] = 100
    100
    
    julia> a
    3-element Array{Array{Int64,1},1}:
     [1, 0, 0]
     [100, 0, 5]
     [0, 0, 5]
    

    您可以看到a 的每个条目都是一个不同的对象。

    【讨论】:

      【解决方案2】:

      逻辑索引只选择条件为真的那些元素。在您的a[b.>0] 的情况下,它选择了两个元素:

      julia> a[b.>0]
      2-element Array{Int64,1}:
       2
       3
      

      您正在尝试将三个元素分配到这两个位置:

      julia> a[b.>0] .= [10,20,30]
      ERROR: DimensionMismatch("array could not be broadcast to match destination")
      

      您可以做的是使用相同的条件逻辑对您分配的值数组进行子集,以选择应该分配哪两个元素:

      julia> a[b.>0] .= [10,20,30][b.>0]
      2-element view(::Array{Int64,1}, [2, 3]) with eltype Int64:
       20
       30
      
      julia> a
      3-element Array{Int64,1}:
        1
       20
       30
      

      语法a[b.>0] .= [Int64[0,0,5]] 仅在aAny 数组时才有效,它意味着完全不同的东西。它将值数组本身广播到所有选定的位置——也就是说,它将整个数组作为重复元素放入a

      julia> a = Any[1,2,3]
      3-element Array{Any,1}:
       1
       2
       3
      
      julia> a[b.>0] .= [Int64[0,0,5]]
      2-element view(::Array{Any,1}, [2, 3]) with eltype Any:
       [0, 0, 5]
       [0, 0, 5]
      
      julia> a
      3-element Array{Any,1}:
       1
        [0, 0, 5]
        [0, 0, 5]
      

      【讨论】:

      • 一个很好的答案(我正在并行写我的,然后注意到你的:))。我有一个小评论 a 不必有 Any 类型,它的类型允许 Vector{Int64} 元素就足够了。
      • 是的,非常正确。我在那里过度简化了文本——Any 数组是最有可能同时保存IntVector{Int} 的数组,因为它是它们唯一常见的超类型,但Union 当然也可以解决问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-06
      • 1970-01-01
      • 2021-06-17
      • 1970-01-01
      • 2015-01-26
      • 1970-01-01
      相关资源
      最近更新 更多