Julia 多維數(shù)組

2018-08-12 21:25 更新

多維數(shù)組

數(shù)組是一個存在多維網(wǎng)格中的對象集合。通常,數(shù)組包含的對象的類型為 Any 。對大多數(shù)計算而言,數(shù)組對象一般更具體為 Float64Int32

因為性能的原因,Julia 不希望把程序?qū)懗上蛄炕男问健?/p>

在 Julia 中,通過引用將參數(shù)傳遞給函數(shù)。Julia 的庫函數(shù)不會修改傳遞給它的輸入。用戶寫代碼時,如果要想做類似的功能,要注意先把輸入復制一份兒。

數(shù)組

基礎函數(shù)

函數(shù) 說明
eltype(A) A 中元素的類型
length(A) A 中元素的個數(shù)
ndims(A) A 有幾個維度
nnz(A) A 中非零元素的個數(shù)
size(A) 返回一個元素為 A 的維度的多元組
size(A,n) A 在某個維度上的長度
stride(A,k) 在維度 k 上,鄰接元素(在內(nèi)存中)的線性索引距離
strides(A) 返回多元組,其元素為在每個維度上,鄰接元素(在內(nèi)存中)的線性索引距離

構(gòu)造和初始化

下列函數(shù)中調(diào)用的 dims... 參數(shù),既可以是維度的單多元組,也可以是維度作為可變參數(shù)時的一組值。

函數(shù) 說明
Array(type, dims...) 未初始化的稠密數(shù)組
cell(dims...) 未初始化的元胞數(shù)組(異構(gòu)數(shù)組)
zeros(type, dims...) |指定類型的全 0 數(shù)組. 如果未指明 type, 默認為 Float64
zeros(A) |全 0 數(shù)組, 元素類型和大小同 A.
ones(type, dims...) |指定類型的全 1 數(shù)組. 如果未指明 type, 默認為 Float64
ones(A) |全 1 數(shù)組, 元素類型和大小同 A.
trues(dims...) | 全 trueBool 數(shù)組
falses(dims...) | 全 falseBool 數(shù)組
reshape(A, dims...) 將數(shù)組中的數(shù)據(jù)按照指定維度排列
copy(A) | 復制 A
deepcopy(A) | 復制 A ,并遞歸復制其元素
similar(A, element_type, dims...) 屬性與輸入數(shù)組(稠密、稀疏等)相同的未初始化數(shù)組,但指明了元素類型和維度。
第二、三參數(shù)可省略,省略時默認為 A 的元素類型和維度
reinterpret(type, A) 二進制數(shù)據(jù)與輸入數(shù)組相同的數(shù)組,但指明了元素類型
rand(dims) | 在 [0,1) 上獨立均勻同分布的 Float64 類型的隨機數(shù)組
randn(dims) | Float64 類型的獨立正態(tài)同分布的隨機數(shù)組,均值為 0 ,標準差為 1
eye(n) | n x n 單位矩陣
eye(m, n) | m x n 單位矩陣
linspace(start, stop, n)| 從 startstop 的由 n 個元素構(gòu)成的線性向量
fill!(A, x) | 用值 x 填充數(shù)組 A
fill(x, dims) | 創(chuàng)建指定規(guī)模的數(shù)組, 并使用 x 填充

連接

使用下列函數(shù),可在任意維度連接數(shù)組:

函數(shù) 描述
cat(k, A...) | 在 k 維度上連接輸入 n-d 數(shù)組
vcat(A...) | cat(1, A...) 的簡寫
hcat(A...) |cat(2, A...) 的簡寫

傳遞給這些函數(shù)的標量值被視為一元陣列。

級聯(lián)功能非常常用,所以為它們設計了特殊的語法:

表示 調(diào)用
[A B C ...] |hcat
[A, B, C, ...] |vcat
[A B; C D; ...] |hvcat

hvcat 可以實現(xiàn)一維上的(使用分號間隔)或二維上的(使用空格間隔)的級聯(lián)。

Comprehensions

Comprehensions 用于構(gòu)造數(shù)組。它的語法類似于數(shù)學中的集合標記法:

    A = [ F(x,y,...) for x=rx, y=ry, ... ]

F(x,y,...) 根據(jù)變量 x, y 等來求值。這些變量的值可以是任何迭代對象,但大多數(shù)情況下,都使用類似于 1:n2:(n-1) 的范圍對象,或顯式指明為類似 [1.2, 3.4, 5.7] 的數(shù)組。它的結(jié)果是 N 維稠密數(shù)組。

下例計算在維度 1 上,當前元素及左右鄰居元素的加權平均數(shù):

    julia> const x = rand(8)
    8-element Array{Float64,1}:
     0.843025
     0.869052
     0.365105
     0.699456
     0.977653
     0.994953
     0.41084
     0.809411

    julia> [ 0.25*x[i-1] + 0.5*x[i] + 0.25*x[i+1] for i=2:length(x)-1 ]
    6-element Array{Float64,1}:
     0.736559
     0.57468
     0.685417
     0.912429
     0.8446
     0.656511

注解: 上例中,x 被聲明為常量,因為對于非常量的全局變量,Julia 的類型推斷不怎么樣。

可在 comprehension 之前顯式指明它的類型。如要避免在前例中聲明 x 為常量,但仍要確保結(jié)果類型為 Float64,應這樣寫:

    Float64[ 0.25*x[i-1] + 0.5*x[i] + 0.25*x[i+1] for i=2:length(x)-1 ]

使用花括號來替代方括號,可以將它簡寫為 Any 類型的數(shù)組:

    julia> { i/2 for i = 1:3 }
    3-element Array{Any,1}:
     0.5
     1.0
     1.5

索引

索引 n 維數(shù)組 A 的通用語法為:

    X = A[I_1, I_2, ..., I_n]

其中 I_k 可以是:

  1. 標量
  2. 滿足 :, a:b, 或 a:b:c 格式的 Range 對象
  3. 任意整數(shù)向量,包括空向量 []
  4. 布爾值向量

結(jié)果 X 的維度通常為 (length(I_1), length(I_2), ..., length(I_n)) ,且 X 的索引 (i_1, i_2, ..., i_n) 處的值為 A[I_1[i_1], I_2[i_2], ..., I_n[i_n]] 。綴在后面的標量索引的維度信息被舍棄。如,A[I, 1] 的維度為 (length(I),)。布爾值向量先由 find 函數(shù)進行轉(zhuǎn)換。由布爾值向量索引的維度長度,是向量中 true 值的個數(shù)。

索引語法與調(diào)用 getindex 等價:

    X = getindex(A, I_1, I_2, ..., I_n)

例如:

    julia> x = reshape(1:16, 4, 4)
    4x4 Array{Int64,2}:
     1  5   9  13
     2  6  10  14
     3  7  11  15
     4  8  12  16

    julia> x[2:3, 2:end-1]
    2x2 Array{Int64,2}:
     6  10
     7  11

n:n-1 形式的空范圍有時用來表示相互索引位置在 n-1n之間。例如,在 searchsorted 函數(shù)使用本習慣指出插入點的值不在排序后的數(shù)組中:

    julia> a = [1,2,5,6,7];

    julia> searchsorted(a, 3)
    3:2

賦值

給 n 維數(shù)組 A 賦值的通用語法為:

    A[I_1, I_2, ..., I_n] = X

其中 I_k 可能是:

  1. 標量
  2. 滿足 :, a:b, 或 a:b:c 格式的 Range 對象
  3. 任意整數(shù)向量,包括空向量 []
  4. 布爾值向量

如果 X 是一個數(shù)組,它的維度應為 (length(I_1), length(I_2), ..., length(I_n)) ,且 Ai_1, i_2, ..., i_n 處的值被覆寫為 X[I_1[i_1], I_2[i_2], ..., I_n[i_n]] 。如果 X 不是數(shù)組,它的值被寫進所有 A 被引用的地方。

用于索引的布爾值向量與 getindex 中一樣(先由 find 函數(shù)進行轉(zhuǎn)換)。

索引賦值語法等價于調(diào)用 setindex!

      setindex!(A, X, I_1, I_2, ..., I_n)

例如:

    julia> x = reshape(1:9, 3, 3)
    3x3 Array{Int64,2}:
     1  4  7
     2  5  8
     3  6  9

    julia> x[1:2, 2:3] = -1
    -1

    julia> x
    3x3 Array{Int64,2}:
     1  -1  -1
     2  -1  -1
     3   6   9

向量化的運算符和函數(shù)

數(shù)組支持下列運算符。逐元素進行的運算,應使用帶“點”(逐元素)版本的二元運算符。

  1. 一元: -, +, !
  2. 二元: +, -, *, .*, /, ./, \, .\, ^, .^, div, mod
  3. 比較: .==, .!=, .<, .<=, .>, .>=
  4. 一元布爾值或位運算: ~
  5. 二元布爾值或位運算: &, |, $

一些沒有“點”(逐元素)操作運算符當一個參數(shù)是一個標量時會被使用。這些運算符有 *, /, \ 和按位運算符。

請注意,像 == 操作這樣的比較運算是操作在整個陣列上的,它會給出一個布爾返回值。逐位的比較使用點操作符。

下列內(nèi)置的函數(shù)也都是向量化的, 即函數(shù)是逐元素版本的:

    abs abs2 angle cbrt
    airy airyai airyaiprime airybi airybiprime airyprime
    acos acosh asin asinh atan atan2 atanh
    acsc acsch asec asech acot acoth
    cos  cospi cosh  sin  sinpi sinh  tan  tanh  sinc  cosc
    csc  csch  sec  sech  cot  coth
    acosd asind atand asecd acscd acotd
    cosd  sind  tand  secd  cscd  cotd
    besselh besseli besselj besselj0 besselj1 besselk bessely bessely0 bessely1
    exp  erf  erfc  erfinv erfcinv exp2  expm1
    beta dawson digamma erfcx erfi
    exponent eta zeta gamma
    hankelh1 hankelh2
     ceil  floor  round  trunc
    iceil ifloor iround itrunc
    isfinite isinf isnan
    lbeta lfact lgamma
    log log10 log1p log2
    copysign max min significand
    sqrt hypot

注意 min maxminimum maximum 之間的區(qū)別,前者是對多個數(shù)組操作,找出各數(shù)組對應的的元素中的最大最小,后者是作用在一個數(shù)組上找出該數(shù)組的最大最小值。

Julia 提供了 @vectorize_1arg@vectorize_2arg 兩個宏,分別用來向量化任意的單參數(shù)或兩個參數(shù)的函數(shù)。每個宏都接收兩個參數(shù),即函數(shù)參數(shù)的類型和函數(shù)名。例如:

    julia> square(x) = x^2
    square (generic function with 1 method)

    julia> @vectorize_1arg Number square
    square (generic function with 4 methods)

    julia> methods(square)
    # 4 methods for generic function "square":
    square{T<:Number}(::AbstractArray{T<:Number,1}) at operators.jl:359
    square{T<:Number}(::AbstractArray{T<:Number,2}) at operators.jl:360
    square{T<:Number}(::AbstractArray{T<:Number,N}) at operators.jl:362
    square(x) at none:1

    julia> square([1 2 4; 5 6 7])
    2x3 Array{Int64,2}:
      1   4  16
     25  36  49

Broadcasting

有時要對不同維度的數(shù)組進行逐元素的二元運算,如將向量加到矩陣的每一列。低效的方法是,把向量復制成同維度的矩陣:

    julia> a = rand(2,1); A = rand(2,3);

    julia> repmat(a,1,3)+A
    2x3 Array{Float64,2}:
     1.20813  1.82068  1.25387
     1.56851  1.86401  1.67846

維度很大時,效率會很低。Julia 提供 broadcast 函數(shù),它將數(shù)組參數(shù)的維度進行擴展,使其匹配另一個數(shù)組的對應維度,且不需要額外內(nèi)存,最后再逐元素調(diào)用指定的二元函數(shù):

    julia> broadcast(+, a, A)
    2x3 Array{Float64,2}:
     1.20813  1.82068  1.25387
     1.56851  1.86401  1.67846

    julia> b = rand(1,2)
    1x2 Array{Float64,2}:
     0.867535  0.00457906

    julia> broadcast(+, a, b)
    2x2 Array{Float64,2}:
     1.71056  0.847604
     1.73659  0.873631

逐元素的運算符,如 .+.* 將會在必要時進行 broadcasting 。還提供了 broadcast! 函數(shù),可以明確指明目的,而 broadcast_getindexbroadcast_setindex! 函數(shù)可以在索引前對索引值做 broadcast。

實現(xiàn)

Julia 的基礎數(shù)組類型是抽象類型 AbstractArray{T,N},其中維度為 N,元素類型為 T。AbstractVectorAbstractMatrix 分別是它 1 維和 2 維的別名。

AbstractArray 類型包含任何形似數(shù)組的類型, 而且它的實現(xiàn)和通常的數(shù)組會很不一樣。例如,任何具體的 AbstractArray{T,N} 至少要有 size(A) (返回 Int 多元組),getindex(A,i)getindex(A,i1,...,iN) (返回 T 類型的一個元素), 可變的數(shù)組要能 setindex!。 這些操作都要求在近乎常數(shù)的時間復雜度或 O(1) 復雜度,否則某些數(shù)組函數(shù)就會特別慢。具體的類型也要提供類似于 similar(A,T=eltype(A),dims=size(A)) 的方法用來分配一個拷貝。

DenseArray 是一個抽象的 AbstractArray 類型的亞型,它應該包括在內(nèi)存的常規(guī)偏移上的所有數(shù)組,因此可以被傳遞到外部在此內(nèi)存布局上的 C 和 Fortran 函數(shù)。
亞型應該提供一個方法 stride(A,k),使之返回“跨越”的維度 k:向給出的維度 k1 應該使 getindex(A,i) 中的 i 增加 stride(A,k)。如果提供了一個指針轉(zhuǎn)換方法 convert(Ptr{T},A),那么內(nèi)存布局應該以相同的方式對應于這些擴展。

Array{T,N} 類型是 DenseArray 的特殊實例,它的元素以列序為主序存儲(詳見代碼性能優(yōu)化)。VectorMatrix 是分別是它 1 維和 2 維的別名。

SubArrayAbstractArray 的特殊實例,它通過引用而不是復制來進行索引。使用 sub 函數(shù)來構(gòu)造 SubArray,它的調(diào)用方式與 getindex 相同(使用數(shù)組和一組索引參數(shù))。sub 的結(jié)果與 getindex 的結(jié)果類似,但它的數(shù)據(jù)仍留在原地。subSubArray 對象中保存輸入的索引向量,這個向量將被用來間接索引原數(shù)組。

StridedVectorStridedMatrix 是為了方便而定義的別名。通過給他們傳遞 ArraySubArray 對象,可以使 Julia 大范圍調(diào)用 BLAS 和 LAPACK 函數(shù),提高內(nèi)存申請和復制的效率。

下面的例子計算大數(shù)組中的一個小塊的 QR 分解,無需構(gòu)造臨時變量,直接調(diào)用合適的 LAPACK 函數(shù)。

    julia> a = rand(10,10)
    10x10 Array{Float64,2}:
     0.561255   0.226678   0.203391  0.308912   …  0.750307  0.235023   0.217964
     0.718915   0.537192   0.556946  0.996234      0.666232  0.509423   0.660788
     0.493501   0.0565622  0.118392  0.493498      0.262048  0.940693   0.252965
     0.0470779  0.736979   0.264822  0.228787      0.161441  0.897023   0.567641
     0.343935   0.32327    0.795673  0.452242      0.468819  0.628507   0.511528
     0.935597   0.991511   0.571297  0.74485    …  0.84589   0.178834   0.284413
     0.160706   0.672252   0.133158  0.65554       0.371826  0.770628   0.0531208
     0.306617   0.836126   0.301198  0.0224702     0.39344   0.0370205  0.536062
     0.890947   0.168877   0.32002   0.486136      0.096078  0.172048   0.77672
     0.507762   0.573567   0.220124  0.165816      0.211049  0.433277   0.539476

    julia> b = sub(a, 2:2:8,2:2:4)
    4x2 SubArray{Float64,2,Array{Float64,2},(StepRange{Int64,Int64},StepRange{Int64,Int64})}:
     0.537192  0.996234
     0.736979  0.228787
     0.991511  0.74485
     0.836126  0.0224702

    julia> (q,r) = qr(b);

    julia> q
    4x2 Array{Float64,2}:
     -0.338809   0.78934
     -0.464815  -0.230274
     -0.625349   0.194538
     -0.527347  -0.534856

    julia> r
    2x2 Array{Float64,2}:
     -1.58553  -0.921517
      0.0       0.866567

稀疏矩陣

稀疏矩陣是其元素大部分為 0 的矩陣。

列壓縮(CSC)存儲

Julia 中,稀疏矩陣使用 列壓縮(CSC)格式。Julia 稀疏矩陣的類型為 SparseMatrixCSC{Tv,Ti} ,其中 Tv 是非零元素的類型, Ti 是整數(shù)類型,存儲列指針和行索引:

    type SparseMatrixCSC{Tv,Ti<:Integer} <: AbstractSparseMatrix{Tv,Ti}
        m::Int                  # Number of rows
        n::Int                  # Number of columns
        colptr::Vector{Ti}      # Column i is in colptr[i]:(colptr[i+1]-1)
        rowval::Vector{Ti}      # Row values of nonzeros
        nzval::Vector{Tv}       # Nonzero values
    end

列壓縮存儲便于按列簡單快速地存取稀疏矩陣的元素,但按行存取則較慢。把非零值插入 CSC 結(jié)構(gòu)等運算,都比較慢,這是因為稀疏矩陣中,在所插入元素后面的元素,都要逐一移位。

如果你從其他地方獲得的數(shù)據(jù)是 CSC 格式儲存的,想用 Julia 來讀取,應確保它的序號從 1 開始索引。每一列中的行索引值應該是排好序的。如果你的 SparseMatrixCSC 對象包含未排序的行索引值,對它們進行排序的最快的方法是轉(zhuǎn)置兩次。

有時,在 SparseMatrixCSC 中存儲一些零值,后面的運算比較方便。 Base 中允許這種行為(但是不保證在操作中會一直保留這些零值)。這些被存儲的零被許多函數(shù)認為是非零值。nnz 函數(shù)返回稀疏數(shù)據(jù)結(jié)構(gòu)中存儲的元素數(shù)目,包括被存儲的零。要想得到準確的非零元素的數(shù)目,請使用 countnz 函數(shù),它挨個檢查每個元素的值(因此它的時間復雜度不再是常數(shù),而是與元素數(shù)目成正比)。

構(gòu)造稀疏矩陣

稠密矩陣有 zeroseye 函數(shù),稀疏矩陣對應的函數(shù),在函數(shù)名前加 sp 前綴即可:

    julia> spzeros(3,5)
    3x5 sparse matrix with 0 Float64 entries:

    julia> speye(3,5)
    3x5 sparse matrix with 3 Float64 entries:
            [1, 1]  =  1.0
            [2, 2]  =  1.0
            [3, 3]  =  1.0

sparse 函數(shù)是比較常用的構(gòu)造稀疏矩陣的方法。它輸入行索引 I ,列索引向量 J ,以及非零值向量 V 。 sparse(I,J,V) 構(gòu)造一個滿足 S[I[k], J[k]] = V[k] 的稀疏矩陣:

    julia> I = [1, 4, 3, 5]; J = [4, 7, 18, 9]; V = [1, 2, -5, 3];

    julia> S = sparse(I,J,V)
    5x18 sparse matrix with 4 Int64 entries:
            [1 ,  4]  =  1
            [4 ,  7]  =  2
            [5 ,  9]  =  3
            [3 , 18]  =  -5

sparse 相反的函數(shù)為 findn ,它返回構(gòu)造稀疏矩陣時的輸入:

    julia> findn(S)
    ([1,4,5,3],[4,7,9,18])

    julia> findnz(S)
    ([1,4,5,3],[4,7,9,18],[1,2,3,-5])

另一個構(gòu)造稀疏矩陣的方法是,使用 sparse 函數(shù)將稠密矩陣轉(zhuǎn)換為稀疏矩陣:

    julia> sparse(eye(5))
    5x5 sparse matrix with 5 Float64 entries:
            [1, 1]  =  1.0
            [2, 2]  =  1.0
            [3, 3]  =  1.0
            [4, 4]  =  1.0
            [5, 5]  =  1.0

可以使用 densefull 函數(shù)做逆操作。 issparse 函數(shù)可用來檢查矩陣是否稀疏:

    julia> issparse(speye(5))
    true

稀疏矩陣運算

稠密矩陣的算術運算也可以用在稀疏矩陣上。對稀疏矩陣進行賦值運算,是比較費資源的。大多數(shù)情況下,建議使用 findnz 函數(shù)把稀疏矩陣轉(zhuǎn)換為 (I,J,V) 格式,在非零數(shù)或者稠密向量 (I,J,V) 的結(jié)構(gòu)上做運算,最后再重構(gòu)回稀疏矩陣。

稠密矩陣和稀疏矩陣函數(shù)對應關系

接下來的表格列出了內(nèi)置的稀疏矩陣的函數(shù), 及其對應的稠密矩陣的函數(shù)。通常,稀疏矩陣的函數(shù),要么返回與輸入稀疏矩陣 S 同樣的稀疏度,要么返回 d 稠密度,例如矩陣的每個元素是非零的概率為 d 。

詳見可以標準庫文檔的 stdlib-sparse 章節(jié)。

稀疏矩陣 稠密矩陣 說明
spzeros(m,n) zeros(m,n) 構(gòu)造 m x n 的全 0 矩陣 (spzeros(m,n) 是空矩陣)
spones(S) ones(m,n) 構(gòu)造的全 1 矩陣 與稠密版本的不同, spones 的稀疏 度與 S 相同
speye(n) eye(n) 構(gòu)造 m x n 的單位矩陣
full(S) sparse(A) 轉(zhuǎn)換為稀疏矩陣和稠密矩陣
sprand(m,n,d) rand(m,n) 構(gòu)造 m-by-n 的隨機矩陣(稠密度為 d ) 獨立同分布的非零元素在 [0, 1] 內(nèi)均勻分布
sprandn(m,n,d) randn(m,n) 構(gòu)造 m-by-n 的隨機矩陣(稠密度為 d ) 獨立同分布的非零元素滿足標準正 態(tài)(高斯)分布
sprandn(m,n,d,X) randn(m,n,X) 構(gòu)造 m-by-n 的隨機矩陣(稠密度為 d ) 獨立同分布的非零元素滿足 X 分 布。(需要 Distributions 擴展包)
sprandbool(m,n,d) randbool(m,n) 構(gòu)造 m-by-n 的隨機矩陣(稠密度為 d ) ,非零 Bool元素的概率為 *d* (randbool 中 d =0.5 )
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號