# 6.2. Array Functions¶

## 6.2.1. Introduction to Array functions¶

We have seen before how many for-loops over arrays can be avoided using e.g. the dot-operator, which evaluates functions and operators element-wise. A number of other helper functions are available to extend this so-called vectorization technique, and allow for shorter code that is easier to understand and which might execute faster.

For example, a very common operation is to sum all the elements of an array. While this is easy to do with a for-loop, the function sum makes it even easier:

x = rand(1:100, 5)
println("Sum of ", x, " = ", sum(x))

Sum of [48, 88, 90, 40, 29] = 295


The sum function can also take an additional dimension argument dims, to specify which dimension it should add along. For example, it can add all the elements in each column (along dimension 1, producing a vector of sums):

A = rand(-100:100, 3, 5)

3×5 Matrix{Int64}:
83  -22  -59  -13  95
-64  -74    5   70  81
57  -11   43    1  22

sum(A, dims=1)

1×5 Matrix{Int64}:
76  -107  -11  58  198


or it can add all the elements in each row (along dimension 2):

sum(A, dims=2)

3×1 Matrix{Int64}:
84
18
112


Note that while these functions reduce a 2D array to a 1D array, the output is still a 2D array (a row of a column vector). The syntax [:] after any array will convert it to a 1D array, which might be needed for example when using the array as indices.

sum(A, dims=1)[:]

5-element Vector{Int64}:
76
-107
-11
58
198


Some other examples of these so-called array reduction functions are shown below.

x = rand(1:100, 5)
display(x)
prod(x)                         # Product of all elements

5-element Vector{Int64}:
77
58
68
93
1

28242984

display(A)
display(maximum(A, dims=1))     # Largest element in each column
display(minimum(A, dims=1))     # Smallest element in each column

3×5 Matrix{Int64}:
83  -22  -59  -13  95
-64  -74    5   70  81
57  -11   43    1  22

1×5 Matrix{Int64}:
83  -11  43  70  95

1×5 Matrix{Int64}:
-64  -74  -59  -13  22


The cumulative sum and product functions work in a similar way, but produce arrays of the same size as the original array:

x = 1:5
display(cumsum(x))                   # Cumulative sum, that is, entry n is the sum of x_1,...,x_n
display(cumprod(x))                  # Cumulative product

5-element Vector{Int64}:
1
3
6
10
15

5-element Vector{Int64}:
1
2
6
24
120

A = reshape(1:6, 2, 3)

2×3 reshape(::UnitRange{Int64}, 2, 3) with eltype Int64:
1  3  5
2  4  6

cumsum(A, dims=1)                    # Cumulative sum along dimension 1 (that is, column-wise)

2×3 Matrix{Int64}:
1  3   5
3  7  11

cumprod(A, dims=2)                   # Cumulative product along dimension 2

2×3 Matrix{Int64}:
1  3  15
2  8  48


## 6.2.2. Example: Taylor polynomial using array functions¶

In part 1, we used for-loops to evaluate the Taylor polynomial for $$\cos x$$ of a given degree $$2n$$:

function taylor_cos(x,n)
term = 1
y = 1
for k = 1:n
term *= -x^2 / ((2k-1) * 2k)
y += term
end
y
end


We can rewrite this in a vectorized form by using the cumprod and the sum functions. Note that each term in the sum is built up by multiplying the previous term by the expression -x^2 / ((2k-1) * 2k). This means we can create a vector with these values, and create all the terms using the cumprod function. Finally, the Taylor polynomial is obtained by adding all the terms using the sum function, and manually adding the first term 1 which is a special case:

function taylor_cos(x,n)
factors = [ -x^2 / ((2k-1) * 2k) for k = 1:n ]
y = 1 + sum(cumprod(factors))
end

taylor_cos (generic function with 1 method)

println(taylor_cos(10, 50)) # Taylor approximation
println(cos(10))            # true value

-0.8390715290752269
-0.8390715290764524


Since we now know how to plot functions, let us make the typical calculus plot that compares Taylor polynomial approximations of different degrees. Note how we can generate a 2D array yy_taylor of 6 different Taylor polynomials, for all values of x, using an array comprehension.

xx = -10:0.01:10
yy = cos.(xx)
yy_taylor = [ taylor_cos(x,n) for x in xx, n in 0:5 ]

using PyPlot
plot(xx, yy, linewidth=2, color="k")
plot(xx, yy_taylor)
grid(true)
axis([-10,10,-1.2,1.2]); ## 6.2.3. Array functions for indices and booleans¶

Another useful set of array functions are based on indices and boolean variables. For example, the in function (or the $$\in$$ symbol) will loop through an array and return true if a given number appears anywhere in the array:

x = 1:2:1000                 # Odd numbers
println(503 in x)            # True, 503 is in the list
println(1000 ∈ x)            # False, 1000 is not in the list

true
false


The all function returns true if all elements in a boolean array are true:

println(all(x .< 500))
println(all(x .> 0))

false
true


The any function returns true if any element in a boolean array is true:

println(any(x .== 503))
println(any(x .== 1000))

true
false


We can also find the index of e.g. the first element that is true in a boolean array:

idx = findfirst(x .== 503)    # Index in x with the value 503

252


or more generally, a vector with the indices to all the elements that are true:

ind = findall(@. x % 97 == 0)
println("Indices in x with numbers that are multiples of 97: ", ind)

Indices in x with numbers that are multiples of 97: [49, 146, 243, 340, 437]


Finally, the count function returns the number of times true appears in a boolean array:

count(@. x % 97 == 0)

5