Vectors are a fundamental data structure for nearly every computer language and it is a crucial for scientific computing as that it is an efficient way of handling large amount of the same datatype. Vectors are collections with a single dimension, and Julia has a Matrix type as well for 2 dimensional collections. These all fit into a general array type.
collect(range) takes a Range object (the pair or triples of numbers separated by colons) and creates an 1D array with the numbers from the range. For example, collect(1:10) returns
If the elements of an array form a functional pattern, we can use what is called a comprehension to construct it in a compact manner. For example, a 1D array [1, -1, 1, -1, 1, -1, 1, -1] can be made:
There are a number of operations on array. For each + and - adds and subtracts two arrays of the same size in an element by element manner. For example if
Section6.5Element by Element Operations (Broadcasting)
Many methods exist to simplify a vectorized method that returns the operation applied element by element to the matrix. To do this, most operations have a . variety and is also called broadcasting. For example, if we want multiply A and B in an element by element manner then
Note that this is not matrix multiplication, which is A*B and we will discuss this in Chapter 40. Many other operations can be done in similar way. For example, to take the square root of every number in the matrix A, we can enter sqrt.(A) which returns:
returns (5, 7, 9). And if we want to add 10 to every element of a matrix, we can use this syntax as well. [1, 2, 3, 4] .+ 10 returns [11, 12, 13, 14]. Note that having broadcasting simplifies a lot of operations with arrays. Without this, typically a for loop would be needed to be written, which is not difficult, but more more complicated than a single operation.
There is also a macro that can apply broadcasting. Let’s say that we are applying the function \(f(x) = x \sin(x) + e^{x^2}\) on an array x = collect(-3:3). To write this operation element by element: one must do x .* sin.(x)+exp.(x.^2) and also that isn’t too difficult, there is a bit of an easier way using the @. macro. If we add this in front of a non-broadcasting expression, it will apply all functions an operations in the right way. You should notice that @. x*sin(x) + exp(x^2) returns the same array.
We will use both forms of broadcasting in the rest of this book. Often, it broadcasting is relatively simple, we’ll use the non-macro version, but for more complicated ones, we’ll use the macro.
results in [1 2 3 4 5 6 8]. The sort function returns a new array that is the sorted version of the original. If you want a sorted version of A in place, use the sort! function instead.
where C is the array from 1 to 9 and before sorting, the mod is taken. The by is a keyword argument that takes a function. in this case, we have used an anonymous function, which is a function without a name and is often used inside of other functions. We will explore these in more detail in Chapter 7. The result of the sort function is [3,6,9,1,4,7,2,5,8], where the mod of the first 3 are 0, the next three are 1 and the last three are 2.
There is a computer science data structure called a stack which acts like a stack of things (paper, dishes, Pokémon cards) and there are two operations on it: 1) put something on top of the stack or 2) take something off the top of the stack. Many languages including Julia don’t have a separate data structure but use an array like one with two operations, push and pop. In Julia these have ! at the end to indicate that the operations change the array. If A=collect(1:5) then
Subsection6.9.2Adding and Removing Elements in the Middle of an Array
The functions push! and pop! add and remove an element from the end of a Vector or 1D array. If we want to add or remove elements in the middle there is are the functions insert! and deleteat!. They both act on a position in the array and you can see how they work with the following examples. If A=collect(1:2:11), then
Subsection6.9.3It Splices, It Dices!! --- okay, it doesn’t dice
Although you can get a long ways with push!, pop!, insert! and deleteat!, the splice! function is a Swiss Army knife for arrays. It can take an array, pull elements out and put elements in. We will walk through the options:
removes and returns the 3rd and 4th elements which are [5, 7]. Also, the array A is the original array without these elements or [1, 3, 9, 11, 13]. If we let A=collect(1:2:11), then
We saw the function insert! above, which will insert a single element in a 1D array. The splice! function will insert multiple elements. For example, if we have
then the function returns Int64[]. This is an empty array of type Int64. The result is empty because nothing was removed in the process. The array A is now [1, 3, 11, 12, 13, 14, 5, 7, 9, 11]. A few things to note about this. The second argument is a bit strange in that you insert 3:2 which is a range in which the last element is smaller than the first. This will let Julia know that you don’t want to remove any elements, unlike either a single number or a range with the first element smaller than the last.
We say splice! is the Swiss army knife, because it does even more!! (Now, how much would you pay?). This last section shows that we can both remove and insert elements into an array at the same time.
returns 5 and replaces the 3rd position with the number 4. The result is [1, 3, 4, 7, 9, 11]. If A=collect(1:2:11), then replacing the 3rd element with [-1,-2,-3] by
returns all elements that satisfy that the mod 2 is 0 (or even numbers) or [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]. The first argument is a function and we use an anonymous function for this.
In addition, if you want to filter on an array with the results in the place of the original array use filter! instead. For small arrays, one can use either, however, for a large array, to use filter would require a new array be made and we will see in Chapter 9 that creating arrays is a slow down and often a bottleneck for code. In such a case filter! would be more efficient.
There are a couple of related functions that involve arrays, that of joining an array to get a string and splitting a string to get an array. We show a few examples here.
which outputs "1, 2, 3, 4, 5, 6", which is the 6 elements of the vector stringified and concatenated separated by ", ". The join function has another option to separate the last one differently. For example if
and notice that this is an array elements of type Substring{String}. This type is used as an efficiency to show where in the original string the substring occurs. If it is desirable to extract the integer representations of the strings, then use the parse function explained in Section 3.7. The integers can be extracted with
where the map function from Chapter 7 is used. This returns the integer array [1, 2, 3, 4, 5, 6]. See the Julia documentation on split for more information and options.
It is also desirable to join two or more arrays. We saw we can append one onto another with the append! function, but recall that this method updates the first array, often an underisable result. We can combine the two arrays
in a way to make a vector of length 8 with vcat(x,y) which returns the vector [1, 2, 3, 4, 5, 6, 7, 8]. Note: vcat is short for vertical concatenation and when applied to vectors (which are understood to be column vectors), one gets a new vector.
Although array comprehensions are incredibly powerful, often if you want to repeat elements of an array to generate another array, the task can be tricky.
and we can use [1+ (i-1) ÷ 4 for i=1:8] to produce the array [1, 1, 1, 1, 2, 2, 2, 2] and also, [1+i % 4 for i=0:7] to generate [1, 2, 3, 4, 1, 2, 3, 4].
However, if other numbers are repeated it may not be easy to generate these. Instead, we will use the repeat function which in short repeats the numbers in an array some number of times. First, if we execute repeat(1:2,4), then the results are the same as the first array above or [1, 2, 1, 2, 1, 2, 1, 2, 1, 2]. To generate the second one above, we can use the inner option as in repeat(1:2, inner = 4). The 3rd example above can be generated with repeat(1:4,2).
An array is one example of a Collection in Julia. We will cover these in more details in Chapter 22 and it is helpful to have knowledge of the more abstract idea when handling arrays.
Another type that allows one to combine two or more elements of data is called a tuple. For example, if we want two integers that are combined, we can make:
For example, this could be a way to store a geometric point in the plane. Determining the data type with typeof((1,2)) returns Tuple{Int64, Int64}, which is another example of a composite type. The type is a Tuple but there are Int64 types for the components of the tuple.
Tuples can consists of different types for different parts. For example, t = (5,7.8, "Hello") is a valid tuple and its type is Tuple{Int64, Float64, String}.
You can access the elements of the tuple using bracket notation. Using the value of t above, we can get the first element of t with t[1]. Note, this is similar to array notation, which we will see in Chapter 6. Note: the first element of things like tuples and array is 1, not 0.
Named tuples can be helpful to build complex datatypes, where there are multiple fields are associated with the same piece of data. Another example might be if you have data associated with a person, such as
where there are a mix of strings and integers in the same tuple. This may be all that you need for an example like this, however, we will see more complex data types in Chapter 12, which creates a new data type or struct. Such a type will allow more flexibility. For example if we define a point using a tuple, it is difficult to do operations on the point.