Arrays

This notebook illustrates how to create abd reshuffle arrays. Other notebooks focus on matrix algebra and other functions applied to matrices.

Load Packages and Extra Functions


In [1]:
using Dates, Printf, DelimitedFiles 

include("printmat.jl")   #a function for prettier matrix printing


Out[1]:
printwhere (generic function with 1 method)

Scalars, Vectors and Multi-dimensional Arrays

are different things, even if they happen to "look" similar. For instance, a 1x1 array is not a scalar and an nx1 array is not a vector. This is discussed in some detail further down.

However, we first present some common features of all arrays (vectors or multi-dimensional arrays).

Creating Arrays

The typical ways of getting an array are

  • hard coding the contents
  • reading in data from a file
  • as a result from computations
  • allocating the array and then changing the elements
  • (often not so smart) growing the array by adding rows (or columns,..)
  • by list comprehension

The next few cells give simple examples.

1. Hard Coding the Contents or Reading from a File


In [2]:
z = [11 12;                       #typing in your matrix
     21 22]
println("A matrix that we typed in:")
printmat(z)

x = readdlm("Data/MyData.csv",',',skipstart=1)  #read matrix from file
println("First four lines of x from csv file:")
printmat(x[1:4,:])


A matrix that we typed in:
        11        12
        21        22

First four lines of x from csv file:
197901.000     4.180     0.770    10.960
197902.000    -3.410     0.730    -2.090
197903.000     5.750     0.810    11.710
197904.000     0.050     0.800     3.270

2a. Allocating an Array and Then Changing the Elements: Fill

An easy way to create an array is to use the fill() function.

A = fill(0,(10,2))           #10x2, integers (0)
B = fill(0.0,10)             #vector with 10 elements, floats (0.0)
C = fill(NaN,(10,2))         #10x2, floats (NaN)
D = fill("",3)               #vector with 3 elements, strings ("")
E = fill(Date(1),3)          #vector with 3 elements, dates (0001-01-01)

In [3]:
x = fill(0.0,(3,2))     #creates a 3x2 matrix filled with 0.0
println("so far, x is filled with 0.0. For instance, x[1,1] is $(x[1,1])")

for i = 1:size(x,1), j = 1:size(x,2)
    x[i,j] = i/j
end

println("\nx after some computations")
printmat(x)


so far, x is filled with 0.0. For instance, x[1,1] is 0.0

x after some computations
     1.000     0.500
     2.000     1.000
     3.000     1.500

2b. Allocating an Array and Then Changing the Elements: A More General Approach (extra)

You can also create an array by

A = Array{Int}(undef,10,2)       #10x2, integers
F = Array{Any}(undef,3)          #vector with 3 elements, can include anything

The undef signals that the matrix is yet not initialized. This is more cumbersome than fill(), but sometimes more flexible.


In [4]:
F    = Array{Any}(undef,3)
F[1] = [1;2;3;4]             #F[1] contains a vector
F[2] = "Sultans of Swing"    #F[2] a string
F[3] = 1978                  #F[3] an integer

printmat(F)


[1, 2, 3, 4]
Sultans of Swing
      1978

3. Growing an Array

Growing a matrix is done by [A;B] and/or [A B] (or by vcat, hcat and cat). This is somewhat slow, so do not use it for appending to a matrix in a long loop. Instead, pre-allocate the matrix and then fill it (see above).

However, growing a vector is not that slow. It can be done by

push!(old vector,new element 1,new element 2)

If you instead want to append all elements of a vector, then do

append!(old vector,vector to append)

In [5]:
A = [11 12;
     21 22]
B = [1 2;
     0 10]

z = [A;B]                #same as vcat(A,B)
println("\n","stacking A and B vertically")
printmat(z)

z2 = [A B]                 #same as hcat(A,B)
println("\n","stacking A and B horizontally")
printmat(z2)


stacking A and B vertically
        11        12
        21        22
         1         2
         0        10


stacking A and B horizontally
        11        12         1         2
        21        22         0        10


In [6]:
B = Float64[]                 #empty vector, to include floats
for i = 1:3
    x_i = 2.0 + 10^i
    push!(B,x_i)              #adding an element at the end
end 
println("a vector with 3 elements:")
printmat(B)


a vector with 3 elements:
    12.000
   102.000
  1002.000

4. List Comprehension and map (extra)

List comprehension sounds fancy, but it is just a simple way to create arrays from repeated calculations. Similar to a "for loop."

You can achieve the same thing with map (for instance, by map(i->collect(1:i),1:3)).


In [7]:
A = [collect(1:i) for i=1:3]         #this creates a vector of vectors

println("A[1] is vector with 1 element, A[2] a vector with 2 elements,...")
printmat(A)

B = map(i->collect(1:i),1:3)
printmat(B)


A[1] is vector with 1 element, A[2] a vector with 2 elements,...
       [1]
    [1, 2]
 [1, 2, 3]

       [1]
    [1, 2]
 [1, 2, 3]

Using Parts of a Matrix

The most common way to use parts of an array is by indexing. For instance, to use the second column of A, do A[:,2].

Notice that A[1,:] gives a (column) vector (yes, it does), while A[1:1,:] gives a 1xk matrix. (It looks like a row vector, but is really a matrix with just one row.)

Also notice that z = A[1,:] creates an independent copy, so changing z will not change A.

In case you do not need an independent copy, then y = view(A,1,:) creates a view of the first row of A. This saves memory and is often faster. Notice, however, that changing y will now change A.


In [8]:
A = [11 12;
    21 22]
println("A:")
printmat(A)

println("\nsecond column of A:")
printmat(A[:,2])

println("\n","first row of A (as a vector): ")
printmat(A[1,:])                          #notice 1 makes it a vector

println("\n","first row of A: ")
printmat(A[1:1,:])                        #use 1:1 to keep it as a 1x2 matrix

println("\n","view of first row of A: ")
y = view(A,1,:)
printmat(y)


A:
        11        12
        21        22


second column of A:
        12
        22


first row of A (as a vector): 
        11
        12


first row of A: 
        11        12


view of first row of A: 
        11
        12

Splitting up an Array (extra)

Sometimes you want to assign separate names to the columns (or rows) of a matrix. The next cell shows an example.


In [9]:
println("A simple way...which works well when you want to create a few variables")
x1 = A[:,1]
x2 = A[:,2]
printmat(x2)

println("Another, prettier way")
(z1,z2) = [A[:,i] for i = 1:2]
printmat(z2)


A simple way...which works well when you want to create a few variables
        12
        22

Another, prettier way
        12
        22

Arrays vs. Vectors vs. Scalars

Matrices, vectors and scalars are different things, even if they contain the same number of elements. In particular,

(a) an $n\times1$ matrix is not the same thing as an $n$ vector

(b) a $1\times1$ matrix or a 1-element vector are not the same thing as a scalar.

As you will see further on, vectors are often more convenient than $n\times1$ matrices.

To convert a 1-element vector or 1x1 matrix C to a scalar, just do myScalar = C[].


In [10]:
A = ones(3,1)         #this is a 3x1 matrix
B = ones(3)           #a vector with 3 elements

println("The sizes of matrix A and vector B: $(size(A)) $(size(B))")

println("\nTesting if A==B: ",isequal(A,B))

println("\nThe nx1 matrix A and n-element vector B can often be used together, for instance, as in A+B, whose size is ",size(A+B))
printmat(A+B)


The sizes of matrix A and vector B: (3, 1) (3,)

Testing if A==B: false

The nx1 matrix A and n-element vector B can often be used together, for instance, as in A+B, whose size is (3, 1)
     2.000
     2.000
     2.000


In [11]:
C = ones(1,1)                   #a 1x1 matrix
c = 1                           #a scalar

println("\nc/C would give an error since C is a (1x1) matrix")

println("\nInstead, do c/C[]: ",c/C[])

if length(C) == 1 && !isa(C,Number)
    C = C[]
end
println("\nAfter conversion of C, do c/C: ",c/C)


c/C would give an error since C is a (1x1) matrix

Instead, do c/C[]: 1.0

After conversion of C, do c/C: 1.0

Vectors: x'x and x'A*x Create Scalars (if x is a vector)

If $x$ is a vector and $A$ a matrix, then $x'x$ and $x'A*x$ are scalars. This is what a linear algebra text book would teach you, so vectors are very useful.

This is not true if x is an array of size $n\times1$. In that case the result is a 1x1 array.

Recommendation: use vectors (instead if $nx1$ arrays) when you can.


In [12]:
x = [1;2]                               #this is a vector
A = [11 12;
     21 22]
println("\nx'x and x'A*x when x is a 2 element vector: ",x'x," ",x'A*x)


x   = zeros(Int,2,1)                   #this is a 2x1 matrix (array) 
x[1] = 1
x[2] = 2
println("\nx'x and x'A*x when x is a 2x1 array: ",x'x," ",x'A*x)


x'x and x'A*x when x is a 2 element vector: 5 165

x'x and x'A*x when x is a 2x1 array: [5] [165]

An Array of Arrays (extra)

If x1 and x2 are two arrays, then y=[x1,x2] is a vector (of arrays) where y[1] = x1 and y[2] = x2. (If you instead want to stack x1 and x2 into a single matrix, use [x1 x2], [x1;x2] or one of the cat functions discussed above.)

In this case y[1] is actually a view of x1 so changing elements of one changes the other.


In [13]:
x1 = ones(3,2)
x2 = [1;2]
y = [x1,x2]               #a vector of arrays

println(size(y))
printmat(y[1])
printmat(y[2])


(2,)
     1.000     1.000
     1.000     1.000
     1.000     1.000

         1
         2

Arrays are Different...

Vectors and matrices (arrays) can take lots of memory space, so Julia is designed to avoid unnecessary copies of arrays. In short, notice the following

  • B = A creates two names of the same array (changing one changes the other)
  • B = reshape(A,n,m), B = vec(A), and B = A' and create another view of the same array (changing one changes the other)
  • When an you input an array to a function, then this array is shared between the function and the calling program (scope). Changing elements of the array (inside the function) will then change the array outside the function. The next cell provides some details.

In [14]:
function f1(A)
    A[1] = A[1]/2          #changing ELEMENTS of A, affects outside value
    #A = A/2               #this would NOT affect the outside value
  return A                 
end

x  = [1.0 2.0]
printlnPs("original x:            ",x)

y1 = f1(x)
printlnPs("x after calling f1(x): ",x)


original x:                 1.000     2.000
x after calling f1(x):      0.500     2.000

In [ ]: