Book Image

Mastering Julia - Second Edition

By : Malcolm Sherrington
Book Image

Mastering Julia - Second Edition

By: Malcolm Sherrington

Overview of this book

Julia is a well-constructed programming language which was designed for fast execution speed by using just-in-time LLVM compilation techniques, thus eliminating the classic problem of performing analysis in one language and translating it for performance in a second. This book is a primer on Julia’s approach to a wide variety of topics such as scientific computing, statistics, machine learning, simulation, graphics, and distributed computing. Starting off with a refresher on installing and running Julia on different platforms, you’ll quickly get to grips with the core concepts and delve into a discussion on how to use Julia with various code editors and interactive development environments (IDEs). As you progress, you’ll see how data works through simple statistics and analytics and discover Julia's speed, its real strength, which makes it particularly useful in highly intensive computing tasks. You’ll also and observe how Julia can cooperate with external processes to enhance graphics and data visualization. Finally, you will explore metaprogramming and learn how it adds great power to the language and establish networking and distributed computing with Julia. By the end of this book, you’ll be confident in using Julia as part of your existing skill set.
Table of Contents (14 chapters)

Multi-dimensional arrays

In fact, Julia views all arrays as a single stream of values and applies size and reshape parameters to compute the appropriate indexing.

Arrays with the number of dimensions greater than 2 (that is, array > 2) can be defined in a straightforward method:

julia> A = rand(4,4,4)
4x4x4 Array{Float64,3}:
[:, :, 1] =
0.522564 0.852847 0.452363 0.444234
0.992522 0.450827 0.885484 0.0693068
0.378972 0.365945 0.757072 0.807745
0.383636 0.383711 0.304271 0.389717
[:, :, 2] =
0.570806 0.912306 0.358262 0.494621
0.810382 0.235757 0.926146 0.915814
0.634989 0.196174 0.773742 0.158593
0.700649 0.843975 0.321075 0.306428
[:, :, 3] =
0.638391 0.606747 0.15706 0.241825
0.492206 0.798426 0.86354 0.715799
0.971428 0.200663 0.00568161 0.0868379
0.936388 0.183021 0.0476718 0.917008
[:, :, 4] =
0.252962 0.432026 0.817504 0.274034
0.164883 0.209135 0.925754 0.876917
0.125772 0.998318 0.593097 0.614772
0.865795 0.204839 0.315774 0.520044

Note the use of slice ':' notation to display the 3D matrix; values are ordered by the third index, then the second, and finally the first.

It is possible to convert this 3D array into a standard matrix containing the same number of values, as follows:

julia> B = reshape(A,8,8)
8x8 Array{Float64,2}:
0.522564 0.452363 0.570806 ... 0.15706 0.252962 0.817504
0.992522 0.885484 0.810382 ... 0.86354 0.164883 0.925754
0.378972 0.757072 0.634989 ... 0.005681 0.125772 0.593097
0.383636 0.304271 0.700649 ... 0.0476718 0.865795 0.31577
0.852847 0.444234 0.912306 ... 0.241825 0.432026 0.274034
0.450827 0.0693068 0.235757 ... 0.715799 0.209135 0.876917
0.365945 0.807745 0.196174 ... 0.086838 0.998318 0.614772
0.383711 0.389717 0.843975 ... 0.917008 0.204839 0.520044

Or, it could appear as a simple vector, like this:

julia> C = reshape(A,64); typeof(C); # => Array{Float64,1}
julia> transpose(C)
1x64 LinearAlgebra.Transpose{Float64,Array{Float64,1}}:
0.522564 0.992522 0.378972 0.383636 ... 0.876917 0.614772 0.520044

Sparse matrices

Normal matrices are sometimes referred to as “dense,” which means that there is an entry for cell [i,j]. In cases where most cell values are, say, 0, this is inefficient, and it is better to implement a scheme of tuples (i,j,x), where x is the value referenced by i and J.

These are termed sparse matrices, and we can create a sparse matrix by executing the following code:

using SparseArrays
S1 = SparseArrays.sparse(I, J, X[, m, n, combine])

S1 will have dimensions m by n and S[I[k], J[k]] = X[k].

If m and n are not given, they default to max(I) and max(J) respectively. The combine() function is used to combine duplicates, and if not provided, duplicates are added by default.

Sparse matrices support much of the same set of operations as dense matrices, but there are a few special functions that can be applied. For example, spzeros() is a counterpart of zeros(), and random number arrays can be generated by sprand() and sprandn():

# The 0.3 means only 30% for the numbers generated will be non-zero
# This will produce different arrays each time it is run
julia> A = sprand(5,5,0.3)
  ⋅        ⋅         ⋅         ⋅        0.16395
 0.21055   ⋅        0.544431   ⋅         ⋅
  ⋅        ⋅         ⋅         ⋅         ⋅
 0.76612   ⋅         ⋅        0.785714  0.993288
  ⋅       0.740757  0.209118   ⋅         ⋅
# So squaring the matrix produces another sparse matrix
julia> A * A
5×5 SparseMatrixCSC{Float64,Int64} with 10 stored entries:
  ⋅        0.121447  0.034285   ⋅         ⋅
  ⋅         ⋅         ⋅         ⋅        0.0345197
  ⋅         ⋅         ⋅         ⋅         ⋅
 0.601951  0.735785  0.207715  0.617346  0.906046
 0.155966   ⋅        0.403291   ⋅         ⋅

Using Matrix() converts the sparse matrix to a dense one, as follows:

julia> convert(Matrix,A);
5×5 Matrix{Float64}:
 0.0      0.0       0.0       0.0       0.16395
 0.21055  0.0       0.544431  0.0       0.0
 0.0      0.0       0.0       0.0       0.0
 0.76612  0.0       0.0       0.785714  0.993288
 0.0      0.740757  0.209118  0.0       0.0

Sparse vectors

Alternatively, we can convert a vector into a sparse array using the sparsevec() function:

julia> sparsevec([1 7 0 3 0])
5-element SparseVector{Int64, Int64} with 3 stored entries:
  [1]  =  1
  [2]  =  2
  [4]  =  4

Another method of construction can make use of a dictionary, as follows:

julia> sparsevec(Dict(1 => "Malcolm", 3 => "[email protected]"))
3-element SparseVector{String, Int64} with 2 stored entries:
  [1]  =  "Malcolm"
  [3]  =  "[email protected]"
julia> sparsevec(Dict("name" => "Malcolm", "email" => "malcolm@
myemail.org"))
ERROR: MethodError: no method matching sparsevec(::Dict{String, 
String})

Note: The key must be an integer; otherwise, an error is raised.

Sparse diagonal matrices

The eyes() function to produce an identity matrix has been deprecated.

Instead, we can use spdiagm() to create a sparse diagonal matrix, and then convert() is required to convert it to a real matrix:

julia> A = spdiagm(ones(Int64,3)) # or spdiagm([1,1,1])
3×3 SparseMatrixCSC{Int64, Int64} with 3 stored entries:
 1   ⋅    ⋅
 ⋅   1   ⋅
 ⋅   ⋅   1
julia> convert(Matrix{Float64},A)
3×3 Matrix{Float64}:
 1.0  0.0  0.0
 0.0  1.0  0.0
 0.0  0.0  1.0

Arrays consist of a collection of homogeneous elements. Later, in Chapters 6 and 7, we will examine more sophisticated structures where the columns can be addressed by name.

These are termed DataFrames and can be thought of as equivalent to data held in a spreadsheet, but we will briefly introduce them here.