4  Data Structure

Types of data structures covered:

  1. Tuples: unmutable, ordered or named, subset with integer tup[1] or dot notation tup.key

  2. Ranges: start:step or start:step:stop

  3. Dictionaries: mutable, named only, subset with name only dct["key"].

  4. Arrays: mutable, ordered only, can mixed type of elements, subset with arr[1]

tuples and arrays are both ordered sequences of elements (so we can index into them). Dictionaries and arrays are both mutable.

Julia is 1 indexing.

4.1 Tuples

We can create a tuple by enclosing an ordered collection of elements in ( ).

Tuple can be named or unnamed.

(name1 = item1, name2 = item2, ...)

fav_anims = ("penguins", "cats", "sugargliders")
#> ("penguins", "cats", "sugargliders")
fav_anims_named = (bird = "penguins", mammal = "cats", marsupial = "sugargliders")
#> (bird = "penguins", mammal = "cats", marsupial = "sugargliders")

# Indexing
fav_anims[1]
#> "penguins"
fav_anims_named[2]
#> "cats"

# Dot Notation
fav_anims_named.bird
#> "penguins"
typeof(fav_anims)
#> Tuple{String, String, String}
typeof(fav_anims_named)
#> NamedTuple{(:bird, :mammal, :marsupial), Tuple{String, String, String}}

Subset first & Last value

fav_anims[begin]
#> "penguins"
fav_anims[end]
#> "sugargliders"

Tuples is unmutable. This will get Error.

fav_anims[1] = "Otters" # Error

4.1.1 Tuple construction from variables

specifying first a semicolon before the values.

i = 1
#> 1
f = 3.14
#> 3.14
s = "Julia"
#> "Julia"

my_quick_namedtuple = (; i, f, s)
#> (i = 1, f = 3.14, s = "Julia")

4.2 Ranges

Range object

1:10
#> 1:10
typeof(1:10)
#> UnitRange{Int64}

# Other types
typeof(1.0:10.0)
#> StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}

Gather values by

for Loop

[i for i in 1:2:10]
#> 5-element Vector{Int64}:
#>  1
#>  3
#>  5
#>  7
#>  9

collect()

collect(1:2:10)
#> 5-element Vector{Int64}:
#>  1
#>  3
#>  5
#>  7
#>  9

4.3 Pairs

my_pair = "Julia" => 42
#> "Julia" => 42
typeof(my_pair)
#> Pair{String, Int64}

pair subset

my_pair.first
#> "Julia"
my_pair.second
#> 42
first(my_pair)
#> "Julia"
last(my_pair)
#> 42

4.4 Dictionary

Dict Constuction

Dict(key1 => value1, key2 => value2, ...)

myphonebook = Dict("Jenny" => "867-5309", "Ghostbusters" => "555-2368")
#> Dict{String, String} with 2 entries:
#>   "Jenny"        => "867-5309"
#>   "Ghostbusters" => "555-2368"

or construct by tuples:

Dict( [("A", 1), ("B", 2)] )
#> Dict{String, Int64} with 2 entries:
#>   "B" => 2
#>   "A" => 1

Obtain keys by:

keys(myphonebook)
#> KeySet for a Dict{String, String} with 2 entries. Keys:
#>   "Jenny"
#>   "Ghostbusters"

Subset with Key

myphonebook["Jenny"]
#> "867-5309"

Add another item

myphonebook["Kramer"] = "555-FILK"
#> "555-FILK"
myphonebook
#> Dict{String, String} with 3 entries:
#>   "Jenny"        => "867-5309"
#>   "Kramer"       => "555-FILK"
#>   "Ghostbusters" => "555-2368"

Remove Item (modify in-place)

pop!(myphonebook, "Kramer")
#> "555-FILK"

4.4.1 Example: tuple and dict conversion

function separate2(str::AbstractString) 
    x = split(str, r"[^[:alnum:].]+"; limit = 2)
    (x[1], x[2])
end
#> separate2 (generic function with 1 method)

separate2("asdf, dfs")
#> ("asdf", "dfs")
["name: Mark", "age: 20"] .|> separate2
#> 2-element Vector{Tuple{SubString{String}, SubString{String}}}:
#>  ("name", "Mark")
#>  ("age", "20")

Separate to dictionary

function separate2(vec::Vector) 
    tups = separate2.(vec)
    Dict(tups)
end
#> separate2 (generic function with 2 methods)

separate2(["name: Mark", "age: 20"])
#> Dict{SubString{String}, SubString{String}} with 2 entries:
#>   "name" => "Mark"
#>   "age"  => "20"

4.5 Arrays

4.5.1 Introduction

[item1, item2, ...]

similar to R vector

myfriends = ["Ted", "Robyn", "Barney", "Lily", "Marshall"]
#> 5-element Vector{String}:
#>  "Ted"
#>  "Robyn"
#>  "Barney"
#>  "Lily"
#>  "Marshall"

Array{String,1} means this is a one dimensional vector. An Array{String,2} would be a 2d matrix.

fibonacci = [1, 1, 2, 3, 5, 8, 13]
#> 7-element Vector{Int64}:
#>   1
#>   1
#>   2
#>   3
#>   5
#>   8
#>  13
mixture = [1, 1, 2, 3, "Ted", "Robyn"]
#> 6-element Vector{Any}:
#>  1
#>  1
#>  2
#>  3
#>   "Ted"
#>   "Robyn"
mixture[1]
#> 1

4.5.2 Formal Array Contruction

  • Vector{T}: one-dimensional array. Alias for Array{T, 1}.
  • Matrix{T}: two-dimensional array. Alias for Array{T, 2}.

Create vector (1d array) with type Float64. It has undefined 5 element

my_V = Vector{Float64}(undef, 3)
#> 3-element Vector{Float64}:
#>  5.0e-324
#>  5.0e-324
#>  0.0
length(my_V)
#> 3

Create 2 x 3 Matrix with element undefined.

my_M = Matrix{Int}(undef, 2, 3)
#> 2×3 Matrix{Int64}:
#>           0  5046895632  5047313056
#>  5047313056           0  5046895632
length(my_M) # Number of elements
#> 6

ndims(my_M)  # Dims
#> 2

size(my_M) # Size (shape) of Matrix
#> (2, 3)

Then fill array with

fill!(my_V, 4)
#> 3-element Vector{Float64}:
#>  4.0
#>  4.0
#>  4.0
my_V
#> 3-element Vector{Float64}:
#>  4.0
#>  4.0
#>  4.0

push! adds an element to the end of an array and pop! removes the last element of an array.

push!(my_V, 21)
#> 4-element Vector{Float64}:
#>   4.0
#>   4.0
#>   4.0
#>  21.0
pop!(my_V)
#> 21.0

zeros array

zeros(3)
#> 3-element Vector{Float64}:
#>  0.0
#>  0.0
#>  0.0

ones array

ones(2)
#> 2-element Vector{Float64}:
#>  1.0
#>  1.0
ones(Int, 2, 3)
#> 2×3 Matrix{Int64}:
#>  1  1  1
#>  1  1  1

4.5.3 Array literals

favorites = [
    ["koobideh", "chocolate", "eggs"],
    ["penguins", "cats", "sugargliders"]
    ]
#> 2-element Vector{Vector{String}}:
#>  ["koobideh", "chocolate", "eggs"]
#>  ["penguins", "cats", "sugargliders"]

Array literals also accept a type specification

Float64[1 2; 3 4]
#> 2×2 Matrix{Float64}:
#>  1.0  2.0
#>  3.0  4.0

Mix and match

[ones(Int, 2, 2) zeros(Int, 2, 2)]
#> 2×4 Matrix{Int64}:
#>  1  1  0  0
#>  1  1  0  0
[zeros(Int, 2, 2)
 ones(Int, 2, 2)]
#> 4×2 Matrix{Int64}:
#>  0  0
#>  0  0
#>  1  1
#>  1  1
rand(4, 3)
#> 4×3 Matrix{Float64}:
#>  0.470263  0.300487  0.958228
#>  0.213954  0.497404  0.299014
#>  0.573203  0.842071  0.534293
#>  0.911019  0.560313  0.323428

4.5.4 Array Indexing and Slicing

V = [1, 2, 3, 4, 5]
#> 5-element Vector{Int64}:
#>  1
#>  2
#>  3
#>  4
#>  5

M = [[1 2 3];
     [4 5 6];
     [7 8 9]]
#> 3×3 Matrix{Int64}:
#>  1  2  3
#>  4  5  6
#>  7  8  9
V[1:3]
#> 3-element Vector{Int64}:
#>  1
#>  2
#>  3

# First and Last Value
V[begin]
#> 1
V[end]
#> 5

Can’t use negative index, however.

V[-1]
# Error
M[2, 1]
#> 4

M[2:3, 1]
#> 2-element Vector{Int64}:
#>  4
#>  7

select columns

M[2, :]
#> 3-element Vector{Int64}:
#>  4
#>  5
#>  6

4.5.5 Array Manipulation

M = [[1 2 3];
     [4 5 6];
     [7 8 9]]
#> 3×3 Matrix{Int64}:
#>  1  2  3
#>  4  5  6
#>  7  8  9

Assign single element

M[1, 2] = 10
#> 10
M
#> 3×3 Matrix{Int64}:
#>  1  10  3
#>  4   5  6
#>  7   8  9

Assign column (or row) vector

M[2, :] = zeros(3)
#> 3-element Vector{Float64}:
#>  0.0
#>  0.0
#>  0.0
M
#> 3×3 Matrix{Int64}:
#>  1  10  3
#>  0   0  0
#>  7   8  9

Reshape

Reshape: Vector -> N-Dims Array

six_vector = collect(1:6)
#> 6-element Vector{Int64}:
#>  1
#>  2
#>  3
#>  4
#>  5
#>  6
three_two_matrix = reshape(six_vector, (3, 2))
#> 3×2 Matrix{Int64}:
#>  1  4
#>  2  5
#>  3  6
three_two_matrix
#> 3×2 Matrix{Int64}:
#>  1  4
#>  2  5
#>  3  6

Reshape: N-Dims Array -> Vector

vec(three_two_matrix)
#> 6-element Vector{Int64}:
#>  1
#>  2
#>  3
#>  4
#>  5
#>  6

4.5.6 Apply Fun over Array

M = [[1 2 3];
     [4 5 6];
     [7 8 9]]
#> 3×3 Matrix{Int64}:
#>  1  2  3
#>  4  5  6
#>  7  8  9

Dot syntax

M .+ 10
#> 3×3 Matrix{Int64}:
#>  11  12  13
#>  14  15  16
#>  17  18  19

map function

map(x -> 2x, M)
#> 3×3 Matrix{Int64}:
#>   2   4   6
#>   8  10  12
#>  14  16  18

or

(x -> 2x).(M)
#> 3×3 Matrix{Int64}:
#>   2   4   6
#>   8  10  12
#>  14  16  18

mapslices()

Apply a function over all elements in a specific array dimension.

M2 = [1 2 3;
      4 5 6]
#> 2×3 Matrix{Int64}:
#>  1  2  3
#>  4  5  6

Cols Sum

mapslices(sum, M2; dims = 1)
#> 1×3 Matrix{Int64}:
#>  5  7  9

Row Sum

mapslices(sum, M2; dims = 2)
#> 2×1 Matrix{Int64}:
#>   6
#>  15

4.5.7 Copy Array

The correct way is

somemorenumbers = copy(fibonacci)
#> 7-element Vector{Int64}:
#>   1
#>   1
#>   2
#>   3
#>   5
#>   8
#>  13

which will bound to different memory ID.

4.6 Array Comprehension

[x^2 for x in 1:3]
#> 3-element Vector{Int64}:
#>  1
#>  4
#>  9

Multiple inputs also support.

[string(x, "-", y) for x in 1:3 for y in 1:2]
#> 6-element Vector{String}:
#>  "1-1"
#>  "1-2"
#>  "2-1"
#>  "2-2"
#>  "3-1"
#>  "3-2"

Add conditionals:

[x^2 for x in 1:10 if isodd(x)]
#> 5-element Vector{Int64}:
#>   1
#>   9
#>  25
#>  49
#>  81

4.7 Array Manipulation

Concatenate array

  • cat(): concatenate input arrays along a specific dimension dims
cat(ones(2), zeros(2), dims=1)
#> 4-element Vector{Float64}:
#>  1.0
#>  1.0
#>  0.0
#>  0.0
  • vcat(): vertical concatenation
vcat(ones(2), zeros(2))
#> 4-element Vector{Float64}:
#>  1.0
#>  1.0
#>  0.0
#>  0.0
  • hcat(): horizontal concatenation
hcat(ones(2), zeros(2))
#> 2×2 Matrix{Float64}:
#>  1.0  0.0
#>  1.0  0.0

4.8 Exercise

a_ray = [1, 2, 3]
#> 3-element Vector{Int64}:
#>  1
#>  2
#>  3

# Add 4
push!(a_ray, 4)
#> 4-element Vector{Int64}:
#>  1
#>  2
#>  3
#>  4
# Remove 4
pop!(a_ray)
#> 4

a_ray
#> 3-element Vector{Int64}:
#>  1
#>  2
#>  3