- Julia機器學習核心編程:人人可用的高性能科學計算
- 朱紅慶
- 2832字
- 2020-07-28 11:01:34
2.6 數組和矩陣
數組是對象的可索引集合,例如整數、浮點數和布爾值,它們被存儲在多維網格中。Julia中的數組可以包含任意類型的值。在Julia中本身就存在數組這個概念。
在大多數編程語言中,數組的下標都是從0開始的。但是在Julia中,數組的下標是從1開始的。
【范例2-26】數組測試
如下代碼簡單測試了數組的定義和訪問。
01 julia> simple_array = [100,200,300,400,500] #創建一個數組 02 5-element Array{Int64,1}: 03 100 04 200 05 300 06 400 07 500 08 julia> simple_array[2] #訪問數組 09 200 10 julia> simple_array[2:4] 11 3-element Array{Int64,1}: 12 200 13 300 14 400
我們可以通過代碼01行的形式創建一個數組。代碼01行創建了一個名為simple_array的數組,該數組包含5個數。創建完成之后,REPL會立即在屏幕上打印出這個數組的內部數據。我們使用下標來訪問數組中的數據,這和其他編程語言類似,唯一不同的就是Julia的數組下標是從1開始的。代碼08行訪問了數組中的第二個數據200,可以輸入simple_array[2],結果為200。同樣,可以輸出數組中的一段數據,例如代碼10行使用“:”來說明輸出哪些數據,simple_array[2:4]表示獲取數組下標2、3和4的值。
【范例2-27】隨機創建數組
如下代碼展示了如何通過函數隨機創建一個數組。
01 julia> rand_array = rand(1:1000,6) #創建一個數組,使用隨機生成 02 的值 03 6-element Array{Int64,1}: 04 813 05 117 06 261 07 478 08 319 09 787
代碼01行使用rand函數創建了一個數組,該函數接收兩個值,其中第一個值是范圍,用“:”表示;第二個值是一個數。本例創建了一個具有6個元素的數組。
前面我們討論的數組元素的類型是相同的。
【范例2-28】創建具有不同類型元素的數組
如下代碼創建了一個具有不同類型元素的數組,但是一些元素會自動提升它的類型。
01 #數組中的元素類型是不同的 02 julia> another_simple_array = [100,250.20,500,672.42] 03 4-element Array{Float64,1}: 04 100.0 05 250.2 06 500.0 07 672.42
在這段代碼中,我們使用Float和Int數據來創建一個數組。在Julia中創建數組時會將Int類型轉換為Float類型。一般來說,Julia會嘗試使用promote()函數來提升類型。如果不能提升,數組將會變成Any類型。
【范例2-29】Any類型數組
如下代碼創建了一個Any類型數組。
01 julia> [1,2,"abc"] 02 3-element Array{Any,1}: 03 1 04 2 05 "abc"
代碼01行在數組中輸入了Int和字符串類型的元素,我們知道這兩個元素是不能提升類型的,所以該數組為Any類型。
2.6.1 Julia中的列表解析式
通過列表推導創建數組更加容易,接下來我們就創建一個數組,并用2的冪來填充數組。
【范例2-30】使用列表解析式創建數組
01 julia> pow2 = Array(Int64,10) #創建一個大小為10的數組 02 10-element Array{Int64,1}: 03 … 04 #分配2為第一個元素 05 julia> pow2[1] = 2 06 2 07 #使用列表解析式,以2的冪來填充數組 08 julia> [pow2[i] = 2^i for i = 2:length(pow2)]; pow2 09 10-element Array{Int64,1}: 10 2 11 4 12 8 13 16 14 32 15 64 16 128 17 256 18 512 19 1024
使用列表解析式很方便,代碼01行首先創建了一個大小為10的數組,然后代碼05行將該數組的第一個元素賦值為2,最后代碼08行使用列表解析式來填充剩余的空間。
盡管數組的大小是固定的,但Julia提供了可用于改變數組大小的功能——我們可以創建一個空數組并添加元素。
【范例2-31】創建一個空數組并添加元素
01 julia> empty_array = Float64[] #創建一個空數組 02 0-element Array{Float64,1} 03 julia> push!(empty_array,1.0) 04 1-element 05 Array{Float64,1}: 06 1.0 07 julia> push!(empty_array,1.1,1.2) 08 3-element Array{Float64,1}: 09 1.0 10 1.1 11 1.2
在這段代碼中,我們創建了一個空數組empty_array,然后使用push!()函數向其中添加元素。該函數接收一個數組名和一系列值,如代碼07行所示,這些值通過“,”分隔。
我們可以使用append!()函數將一個數組中的所有元素添加到另一個數組中。
【范例2-32】添加數組元素
01 julia> append!(empty_array,[101.1,202.2,303.3]) 02 6-element Array{Float64,1}: 03 1.0 04 1.1 05 1.2 06 101.1 07 202.2 08 303.3
代碼01行傳入一個數組,該數組內的所有元素將被添加到empty_array數組中。
我們還可以創建自定義大小的數組,代碼如下。
【范例2-33】創建自定義大小的數組
01 julia> X = Array{Int64}(4,1) 02 4×1 Array{Int64,2}: 03 140044411365992 04 140044411366424 05 140044411368648 06 140044414849552
以上代碼簡單演示了如何創建一個自定義大小的數組。
我們也可以使用fill!()函數向數組中填充相同的值。
【范例2-34】數組的填充
01 #使用fill!()函數復制值 02 julia> fill!(X,4) 03 4×1 Array{Int64,2}: 04 4 05 4 06 4 07 4 08 julia> X[2] = 10; X #使用下標改變值 09 4×1 Array{Int64,2}: 10 4 11 10 12 4 13 4
代碼02行將之前創建的數組X填充為4,代碼08行將數組中第二個元素的值改變為10。
對整數有效的各種運算符和函數對數組也有效,以下是常用的運算符。
? 一元運算符:?, +, !。
? 二元運算符:+, ?, * , .* , /, ./, \, .\, ^, .^, div, mod。
? 比較運算符:.==, .!=, .<, .<=, .>, .>=。
? 一元布爾或位運算符:~。
? 二元布爾或位運算符:&, |, $。
2.6.2 矩陣運算
本節我們將創建矩陣,并展示它的一些功能和特性。
【范例2-35】矩陣構造
以下代碼構造了一個Julia矩陣。
01 julia> X = [1, 1, 2, 3, 5, 8, 13, 21, 34] #創建一個數組 02 9-element Array{Int64,1}: 03 1 04 1 05 … 06 julia> X = [1 1 2; 3 5 8; 13 21 34] #構造矩陣 07 3×3 Array{Int64,2}: 08 1 1 2 09 3 5 8 10 13 21 34
代碼01行創建了一個含有9個元素的數組X,之后又創建了一個3×3的矩陣X,從代碼06行可以看到矩陣的構造方法。矩陣使用“;”來分隔行,使用空格來分隔每個元素。
我們可以對矩陣進行一些操作。
01 julia> A = [2 4; 8 16; 32 64] #3×2的矩陣 02 3×2 Array{Int64,2}: 03 2 4 04 8 16 05 32 64
代碼01行定義了一個3×2的矩陣A。
我們可以使用reshape()函數來改變矩陣A的形狀。
01 julia> reshape(A,2,3) 02 2×3 Array{Int64,2}: 03 2 32 16 04 8 4 64
也可以使用轉置函數來實現這個操作。
01 julia> transpose(A) 02 2×3 Array{Int64,2}: 03 2 32 16 04 8 4 64
代碼01行使用transpose()函數來實現矩陣轉置。
我們也可以對矩陣進行加法和乘法運算。矩陣加法運算代碼如下:
01 julia> B = [1 1 2; 3 5 8] 02 2×3 Array{Int64,2}: 03 1 1 2 04 3 5 8 05 #矩陣相加 06 julia> transpose(A)+B 07 2×3 Array{Int64,2}: 08 3 9 34 09 7 21 72
矩陣相加操作執行成功。現在,我們來看一看矩陣的乘法運算。
01 julia> transpose(A)*B 02 ERROR: DimensionMismatch("matrix A has dimensions (2,3), 03 matrix B has dimensions (2,3)") 04 …
我們可以預料到這個操作將會報錯,因為矩陣B應該是3×2的。下面轉置矩陣B并再次執行操作。
01 julia> transpose(A)*transpose(B) #置換矩陣B的乘法 02 2×2 Array{Int64,2}: 03 74 302 04 148 604
Julia還提供了一個特殊運算符——就是在普通的運算符前面添加“.”,它可以幫助我們進行元素乘法運算。
01 julia> A.*B #下面對兩個(2×3)矩陣執行元素運算 02 2×3 Array{Int64,2}: 03 2 8 64 04 12 80 512
這里使用A.*B對兩個相同維度的矩陣進行運算。除此之外,矩陣同樣支持如下元素運算。
01 julia> A = [1 2 2 ; 4 5 6] #定義二維矩陣(2×3) 02 2×3 Array{Int64,2}: 03 1 2 2 04 4 5 6 05 julia> B = [0 2 3 ; 4 4 6] 06 2×3 Array{Int64,2}: 07 0 2 3 08 4 4 6 09 julia> A .== B 10 2×3 BitArray{2}: 11 false true false 12 true false true
代碼01行和05行分別定義了兩個矩陣A和B,然后使用“.==”來比較其相對應的元素,返回一個布爾值的矩陣。本例的結果如代碼11行和12行所示。
2.6.3 多維數組操作
上一節我們介紹了Julia的數組、矩陣及其運算。本節我們將介紹Julia中的多維數組。首先使用rand()函數構造一個3×3×3的多維數組,該函數生成隨機值。
【范例2-36】多維數組
以下代碼展示了多維數組的一些用法。
01 julia> multiA = rand(3,3,3) 02 3×3×3 Array{Float64,3}: 03 [:, :, 1] = 04 0.461755 0.594271 0.410017 05 0.168575 0.821246 0.779109 06 0.993498 0.516948 0.59578 07 [:, :, 2] = 08 0.256123 0.19102 0.503787 09 0.628641 0.00406246 0.309143 10 0.939811 0.00935249 0.926403 11 [:, :, 3] = 12 0.263566 0.574306 0.203657 13 0.447232 0.509143 0.123887 14 0.760373 0.790011 0.29599
我們可以使用下標,就像訪問數組和矩陣中的值一樣。
01 julia> multiA[1,3,2] 02 0.5037874039027417
上面的數字在數組中實際上表示為0.503787。我們還可以使用reshape()函數將數組重新“整形”為二維數組。
01 julia> reshape(multiA,9,3) 02 9×3 Array{Float64,2}: 03 0.461755 0.256123 0.263566 04 0.168575 0.628641 0.447232 05 0.993498 0.939811 0.760373 06 0.594271 0.19102 0.574306 07 0.821246 0.00406246 0.509143 08 0.516948 0.00935249 0.790011 09 0.410017 0.503787 0.203657 10 0.779109 0.309143 0.123887 11 0.59578 0.926403 0.29599
2.6.4 稀疏矩陣
前面我們介紹了矩陣和多維數組。在范例中,矩陣和數組中的每一個元素都是用具體的值來填充的。但在很多情況下,可能會有很多0值。如果0值比具有值的數據量多,則建議將它們存儲在高效且專門設計的稀疏矩陣數據結構中。
【范例2-37】創建稀疏矩陣
如下代碼創建了一個稀疏矩陣。
01 julia> sm = spzeros(5,5) 02 5×5 SparseMatrixCSC{Float64,Int64} with 0 stored entries 03 julia> sm[1,1] = 10 04 10 05 julia> sm 06 5×5 SparseMatrixCSC{Float64,Int64} with 1 stored entry: 07 [1, 1] = 10.0