为什么行向量的切片不返回行向量?

3

我刚开始学习Julia编程语言,想问一下如何使得对一个行向量进行切片操作后返回仍是行向量?

我已经搜索过了但没有找到答案。

在Matlab中,对一个行向量进行切片操作后会按照预期返回一个行向量。但在Julia中,它会返回一个数组。版本是1.5.3 (2020-11-09)。

>julia
julia> x=[1 2 3 4 5 6]
1×6 Array{Int64,2}:
 1  2  3  4  5  6

julia> x[1:4]
4-element Array{Int64,1}:
 1
 2
 3
 4

在Matlab中
>> x=[1 2 3 4 5 6 7]
>> x(1:4)

ans =

     1     2     3     4

在Julia中,正确的方法是什么?即,对行向量进行切片返回行向量,对列向量进行切片返回列向量。

Matlab的一个例外情况实际上非常迷人! - mbauman
3个回答

4
你手头拥有的不是行向量,而是一个形状为 1x6 的矩阵,也就是一个二维数组。如果你想深入了解 Julia 中如何处理向量,可以阅读这个问题: https://github.com/julialang/julia/issues/4774,它被称为“认真考虑向量转置”。你会发现这个设计经过了很多思考。
Julia 切片会以可预测的方式减少维度:输出的维数等于输入索引的维数之和。在你的情况下,x[1:4],输入索引切片是一维的,因此输出是一维的。如果你想要输出的维数是二维的,你需要使用一维加一维的输入索引:
jl> x[1:1, 1:4]
1×4 Matrix{Int64}:
 1  2  3  4

请注意下面的代码将生成一个一维数组。
jl> x[1, 1:4]
4-element Vector{Int64}:
 1
 2
 3
 4

这是因为第一个索引是标量,即0维的,所以输出结果是0D + 1D = 1D。

即使对于空数组也是如此:

jl> x[1:0, 1:4]
0×4 Matrix{Int64}

即使它的大小为0x4,这仍然是一个矩阵。

您应该知道,在大多数情况下,您应该更喜欢正确的向量:[1, 2, 3, 4, 5, 6]而不是1xN矩阵[1 2 3 4 5 6]


我会优先介绍规则,而不是设计讨论:结果的维度是指数的维度之和。或者更具体地说,结果的轴是索引的连接轴。https://docs.julialang.org/en/v1/manual/arrays/#man-array-indexing - mbauman

1
短而怀疑:
y = x[1:4]'

更长、更有教育意义: 如果您仔细关注输出结果

x = [1 2 3 4 5 6]

你会看到数组的条件性(!)是二:
1×6 Array{Int64,2}:
 1  2  3  4  5  6

也就是说,因为你没有将元素收集到列中,而是强制它成为一行。为了高效地使用Julia,请尝试使用列向量,如下所示:
y = [1,2,3,4,5,6]

或者更多地依赖于Julia的函数并使用。
z = collect(1:6)

朱莉娅使用列优先布局。数组的每一列都是连续的内存区域。这使得对列的操作比对行的操作更快。其他语言可能具有不同的布局。事实上,FORTRAN和MatLab也利用列优先数组。另一方面,C编程语言使用行主布局。
编辑:显然我在写作时迷失了方向。我相信在Julia中正确的方式是全部使用列向量。当然,在某些代数运算中,这种方法可能不可行。但对于大多数程序来说,向量的方向并不重要。因此,在Julia中请使用列向量。

没有特别的原因需要“collect”范围为1到6。 在几乎所有情况下,它的行为类似于普通向量,但更轻巧且更快。 只有在需要改变它时才真正需要“collect”。 - DNF

1
正如@2419所述,x=[1 2 3 4 5 6]实际上是一个一行矩阵,而Julia在列向量方面更具性能优势。
话虽如此,当然也有合法的矩阵用途;-)
这就是长篇故事……当您拥有一个矩阵并对其进行切片以使结果成为单个行时,它会自动转换为列向量:
julia> x = [1 2 3 4; 10 20 30 40; 100 200 300 400]
3×4 Matrix{Int64}:
   1    2    3    4
  10   20   30   40
 100  200  300  400

julia> a = x[2,:]
4-element Vector{Int64}:
 10
 20
 30
 40

然而,如果您将其切成2行或更多行,则它仍然是一个矩阵:
julia> b = x[[2,3],:]
2×4 Matrix{Int64}:
  10   20   30   40
 100  200  300  400

我对这个选择感到有些困惑,但现在就像这样,并且不会改变。请注意,使用第一种情况来检索行向量非常容易:
julia> transpose(a) # or, equivalently, `a'`
1×4 transpose(::Vector{Int64}) with eltype Int64:
 10  20  30  40

重要提示!transpose 是一个矩阵操作,仅适用于数值矩阵(或向量)。 如果您的矩阵包含非数值元素,如字符串,则 transpose 将生成错误,并且您应该使用 permutedims 代替:

julia> x2 = [1 2 "c" 4; 10 20 "cc" 40; 100 200 300 "ddd"]
3×4 Matrix{Any}:
   1    2     "c"    4
  10   20     "cc"  40
 100  200  300        "ddd"

julia> a2 = x2[2,:]
4-element Vector{Any}:
 10
 20
   "cc"
 40

julia> transpose(a2)
1×4 transpose(::Vector{Any}) with eltype Any:
Error showing value of type LinearAlgebra.Transpose{Any, Vector{Any}}:
ERROR: MethodError: no method matching transpose(::String)
# [...]

julia> permutedims(a2)
1×4 Matrix{Any}:
 10  20  "cc"  40

然而,transpose更快:
julia> using BenchmarkTools
julia> @btime transpose(a)
  21.971 ns (1 allocation: 16 bytes)
1×4 transpose(::Vector{Int64}) with eltype Int64:
 10  20  30  40

julia> @btime permutedims(a)
  66.289 ns (2 allocations: 96 bytes)
1×4 Matrix{Int64}:
 10  20  30  40

因此,如果您确定您的矩阵是数值型的,请使用transpose,否则请使用permutedims


有几个小问题:首先,决定它是向量还是矩阵的不是输出行数,而是索引表达式的类型。因此,x [1,1:4]是一个向量,而x [1:1,1:4]是一个矩阵,即使它只有一行。一致性和类型稳定性对于这个设计非常重要。其次,转置既适用于向量也适用于矩阵,而不仅仅是矩阵。 - DNF
是的,你说得对。我不想引入“数组”这个术语,我试图区分的是数值数组和非数值数组(已编辑)。 - Antonello
哦,还有一件事。transpose实际上是一种惰性操作,几乎不需要时间。如果你将变量插入到基准测试表达式中,就可以看到这一点:@btime transpose($a)(注意 $)。通常应该使用插值与BenchmarkTools。 - DNF

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接