Data structures
Contents
Data structures¶
Arrays¶
Arrays are one of the most important data structures for economists. They are equivalent of vectors and matrices in Matlab
or R
.
Arrays are multi-dimensional objects containing numbers (or different objects).
A vector is a one-dimensional array. To get a row vector you use the following convention:
A = [1 2 3]
1×3 Matrix{Int64}:
1 2 3
You get column vectors by using ;
as the separator of elements:
B = [1; 2; 3]
3-element Vector{Int64}:
1
2
3
All elements of arrays must be of the same type. In case of type incompatibility across elements, coercion is performed:
@show c = [1 π 2.3]
@show typeof(c) #It's Float64 despite the first element being an integer.
c = [1 π 2.3] = [1.0 3.141592653589793 2.3]
typeof(c) = Matrix{Float64}
Matrix{Float64} (alias for Array{Float64, 2})
X = [1 2 3;
3 4 5]
2×3 Matrix{Int64}:
1 2 3
3 4 5
It is a good practice to initialize arrays before assigning values to their elements.
To create an empty one-dimensional array with K
elements of type TYPE
you use Array{TYPE}(undef, K)
.
Below an illustrating example:
Array{Float64}(undef, 10)
10-element Vector{Float64}:
3.5e-323
8.4e-323
9.4e-323
1.1e-322
1.2e-322
1.24e-322
1.43e-322
1.5e-322
1.53e-322
5.0e-324
Argument undef
means that nothing particular is assigned to all elements.
Instead, existing values stored previously in the memory are assigned.
To create an array of dimensions \(K\times N\times S\times W\) you use
Array{TYPE}(undef, K, N, S, W)
.
Below an illustrating example:
Array{Int}(undef, 2, 3, 2, 2)
2×3×2×2 Array{Int64, 4}:
[:, :, 1, 1] =
1 3 5
2 4 6
[:, :, 2, 1] =
7 9 11
8 10 12
[:, :, 1, 2] =
13 15 17
14 16 18
[:, :, 2, 2] =
19 21 23
20 22 24
To get an array with all elements constant, you use command fill
:
A = fill(π^2, 4, 3)
4×3 Matrix{Float64}:
9.8696 9.8696 9.8696
9.8696 9.8696 9.8696
9.8696 9.8696 9.8696
9.8696 9.8696 9.8696
To get an object of the same size and data type, function similar
can be useful. Elements of the new objects will contain information previously allocated in the memory.
B = similar(A)
4×3 Matrix{Float64}:
5.0e-324 2.5e-323 4.4e-323
1.0e-323 3.0e-323 5.0e-323
1.5e-323 3.5e-323 5.4e-323
2.0e-323 4.0e-323 6.0e-323
Arrays with zero elements can be constructed with zeros
:
zeros(3,2)
3×2 Matrix{Float64}:
0.0 0.0
0.0 0.0
0.0 0.0
To get an identity square matrix of dimension \(K\) function I
from package LinearAlgebra
can be used:
using LinearAlgebra
I(5)
5×5 Diagonal{Bool, Vector{Bool}}:
1 ⋅ ⋅ ⋅ ⋅
⋅ 1 ⋅ ⋅ ⋅
⋅ ⋅ 1 ⋅ ⋅
⋅ ⋅ ⋅ 1 ⋅
⋅ ⋅ ⋅ ⋅ 1
Simple array operations¶
Except square-bracket notations, accessing arrays in Julia
is almost one-to-one analogy to Matlab
:
A = [1 2 3 4 5;
6 7 5 9 12]
2×5 Matrix{Int64}:
1 2 3 4 5
6 7 5 9 12
size(A)
(2, 5)
A[1,:] #1st row
5-element Vector{Int64}:
1
2
3
4
5
A[:,1] #1st column
2-element Vector{Int64}:
1
6
A[:,[1,3]] #1st and 3rd column
2×2 Matrix{Int64}:
1 3
6 5
A[:,3:end] #from 3rd to the LAST columns
2×3 Matrix{Int64}:
3 4 5
5 9 12
A[1,3:end] #1st row and from 3rd to the last columns
3-element Vector{Int64}:
3
4
5
Note
What can be quite natural for users of such languages as R
, Matlab
, or Fortran
, array indexing in Julia
starts from \(1.\)
This means that you get the fist element of an array X
by calling X[1]
.
This is different from such languages as Python
, C++
, or Java
, where array indexing starts from \(0.\)
For instance, in Python the fist element of an array X
is obtained by running X[0]
.
There are arguments for and against both conventions, which very often leads to heated debates, comparable to the editor war.
Just like with scalars, arithmetic operations on arrays of the same dimensions work in an analogous way:
A = [1 2;3 4];
B = I(2);
A + B
2×2 Matrix{Int64}:
2 2
3 5
A*A
2×2 Matrix{Int64}:
7 10
15 22
A
2×2 Matrix{Int64}:
1 2
3 4
A' #transposition
2×2 adjoint(::Matrix{Int64}) with eltype Int64:
1 3
2 4
A^(-1) #invert matrix
2×2 Matrix{Float64}:
-2.0 1.0
1.5 -0.5
maximum(A) #notice that `max`` works in a bit different way than in Matlab!
4
A^(-1)*A
2×2 Matrix{Float64}:
1.0 0.0
2.22045e-16 1.0
kron(A,I(3))
6×6 Matrix{Int64}:
1 0 0 2 0 0
0 1 0 0 2 0
0 0 1 0 0 2
3 0 0 4 0 0
0 3 0 0 4 0
0 0 3 0 0 4
Nonetheless, if we try to add an object of incompatible dimensions (including scalars), we will get an error message, just like in an example below:
A + 1
MethodError: no method matching +(::Matrix{Int64}, ::Int64)
For element-wise addition, use broadcasting with dot syntax: array .+ scalar
Closest candidates are:
+(::Any, ::Any, ::Any, ::Any...) at operators.jl:560
+(::T, ::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} at int.jl:87
+(::Rational, ::Integer) at rational.jl:288
...
Stacktrace:
[1] top-level scope
@ In[27]:1
[2] eval
@ ./boot.jl:360 [inlined]
[3] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
@ Base ./loading.jl:1094
This can be perceived as a particularly bizzare and excessively formal thing.
That said, at the development stage there were reasons for this.
Furthermore, notice that in Mathematics this type of operations, \(\mathbb{A}+1\), in most cases is also forbidden.
One (extremely inefficient but didactically useful) solution to this problem is to increase the size of the smaller object by using repeat
.
This function constructs an array by repeating the input array a given number of times in each out of the provided dimension.
Thus, to compute \(\mathbb{A}+1,\) we have to increase the size of \(1\) in such a way that the new object fits the size of \(\mathbb{A}\).
As we remember the size of \(\mathbb{A}\) is \(2\times 2.\)
Just to double check, let’s use function size
.
size(A)
(2, 2)
Below you can find repeat
at play:
repeat([1], 3)
3-element Vector{Int64}:
1
1
1
repeat([1], 3, 2)
3×2 Matrix{Int64}:
1 1
1 1
1 1
repeat([1], 4, 3, 2)
4×3×2 Array{Int64, 3}:
[:, :, 1] =
1 1 1
1 1 1
1 1 1
1 1 1
[:, :, 2] =
1 1 1
1 1 1
1 1 1
1 1 1
repeat([1; 2], 2, 3) #input argument: column vector
4×3 Matrix{Int64}:
1 1 1
2 2 2
1 1 1
2 2 2
repeat([1 2], 2, 3) #input argument: row vector
2×6 Matrix{Int64}:
1 2 1 2 1 2
1 2 1 2 1 2
Consequently, we are in the position to compute \(\mathbb{A}+1:\)
A + repeat([1], 2, 2)
2×2 Matrix{Int64}:
2 3
4 5
An addition of two arrays of different sizes can be conducted analogously:
A + repeat([0; 1], 1, 2)
2×2 Matrix{Int64}:
1 2
4 5
Don’t do this in your daily workflow
A much more recommended and efficient way to operate on objects of different sizes is to use broadcasting or mapping. I discuss it in more details in one of the further secions.
Assignment operator (=
) and arrays: easy for Pythonistas, confusing for Matlabians¶
What can be quite confusing at first for people with the Matlab
background is how the assignment operator (=
) works with arrays in Julia
.
In Matlab
if we want to create a matrix V1
that is a copy of another matrix V0
it is enough to write:
V1=V0
Henceforth, there are two separate matrices in the memory, V1
and V0
.
Any changes in either of those will not affect the other one.
In Julia
this works in a very different way.
Exactly like in Python
, running V1=V0
will create a new pointer to the memory allocated for V0
.
Both objects, V1
and V0
, share the same information.
Consequently, any changes in the memory through accessing V1
will affect the data pointed by V0
, and vice versa.
Below you can find an illustration of this language feature.
@show V₀ = [1 2 3];
V₀ = [1 2 3] = [1 2 3]
@show V₁ = V₀;
V₁ = V₀ = [1 2 3]
Running V₁ = V₀
will create a new pointer to the memory associated with V₀
.
Now changes in either of those objects will affect both pointers:
@show V₁[1] = 420;
@show V₁;
@show V₀;
@show V₀[3] = 128;
@show V₁;
@show V₀;
V₁[1] = 420 = 420
V₁ = [420 2 128]
V₀ = [420 2 128]
V₀[3] = 128 = 128
V₁ = [420 2 128]
V₀ = [420 2 128]
This feature of Julia
can be especially confusing in iterative procedures with updating values, such as value function iterations.
In this case, trying to create a copy of one array but making rather a new pointer instead will result in meeting the convergence criterion immediately after the first iteration (terrible sentence).
Obviously, this will not be correct.
A remedy to this issue can be simply addressed by using deepcopy
, which creates a new object in the memory that contains the same information as the copied object.
@show V₀ = [1 2 3];
@show V₁ = deepcopy(V₀);
V₀ = [1 2 3] = [1 2 3]
V₁ = deepcopy(V₀) = [1 2 3]
Now changes in either of those objects will not affect values pointed by the other object:
@show V₁[1] = 420;
@show V₁;
@show V₀;
@show V₀[3] = 128;
@show V₁;
@show V₀;
V₁[1] = 420 = 420
V₁ = [420 2 3]
V₀ = [1 2 3]
V₀[3] = 128 = 128
V₁ = [420 2 3]
V₀ = [1 2 128]
An important thing that should be also stressed is that this feature works only in the presence of assignments without additional operations.
If, for instance, code V₁ = 2*V₀
involves two operations: (1) multiplication *(2,V₀)
; and (2) making pointer V₁
to memory containing results from computing *(2,V₀)
in the past.
This means that memory spaces pointed by V₁
and by V₀
are two different things.
@show V₀ = [1 2 3];
@show V₁ = 2*V₀;
V₀ = [1 2 3] = [1 2 3]
V₁ = 2V₀ = [2 4 6]
@show V₁[1] = 420;
@show V₁;
@show V₀;
@show V₀[3] = 128;
@show V₁;
@show V₀;
V₁[1] = 420 = 420
V₁ = [420 4 6]
V₀ = [1 2 3]
V₀[3] = 128 = 128
V₁ = [420 4 6]
V₀ = [1 2 128]
Dictionaries¶
Dictionaries are very useful objects that are particularly useful in my daily workflow.
They contain different objects and data structures of those types can be very different.
Dictionaries are initialized with Dict
and the list of the elements take arguments in the form "name_of_the_object" => object
.
An illustration below:
my_first_dict = Dict("X" => 2,
"Y" => 1:10)
Dict{String, Any} with 2 entries:
"Y" => 1:10
"X" => 2
Alternatively you can use a bit different notation, which I personally prefer (and I am not sure why): Dict([ ("name_of_the_object1", object1) ])
.
For example, if we want to create an object containing all parameters of our model, dictionaries are good candidates for it:
params = Dict([
("β" , (1/1.04)^(1/12)),
("b" , [.55 0]) ,
("vec_states" , 1:2) ,
("π", [1:10 20:29])
])
Dict{String, Any} with 4 entries:
"b" => [0.55 0.0]
"π" => [1 20; 2 21; … ; 9 28; 10 29]
"vec_states" => 1:2
"β" => 0.996737
Accessing elements of dictionaries has some similarities with arrays. The main difference is that we have to use names in quotates instead of indices:
@show params["β"]
params["π"]
params["β"] = 0.9967369426185624
10×2 Matrix{Int64}:
1 20
2 21
3 22
4 23
5 24
6 25
7 26
8 27
9 28
10 29
We can normally work on objects inside dictionaries.
Suppose we want to increase values of all rows but the last one of the first column of array π
by one we can do it by:
params["π"][1:(end-1),1] .+= 1;
params["π"]
10×2 Matrix{Int64}:
2 20
3 21
4 22
5 23
6 24
7 25
8 26
9 27
10 28
10 29
We can also add new elements to existing dictionaries. It is super simple:
params["Σ"] = 12
12
Now in in params
there is a new object Σ
:
params
Dict{String, Any} with 5 entries:
"Σ" => 12
"b" => [0.55 0.0]
"π" => [2 20; 3 21; … ; 10 28; 10 29]
"vec_states" => 1:2
"β" => 0.996737
In my own workflow, dictionaries are super useful for putting all blocks of models into one object.
I create a dictionary economy
with all parameters, value functions, simulations inside.
Thanks to this during analyses I can explore different parametrizations, counterfactual calibrations and what not in a very organized way.
Get used to assignments in Julia
You have to remember the assignment operator =
with whole dictionaries works in a very similar way as with arrays, i.e. it refers to memory containing information rather than information itself.
equation_copy = equation;
println("Before assignment:")
@show equation["c"];
@show equation_copy["c"];
println("ASSIGNMENT: equation_copy[\"c\"] = 420")
equation_copy["c"] = 420;
println("After assignment:")
@show equation["c"];
@show equation_copy["c"];
Before assignment:
equation["c"] = π
equation_copy["c"] = π
ASSIGNMENT: equation_copy["c"] = 420
After assignment:
equation["c"] = 420
equation_copy["c"] = 420
Too many repetitions of dictionary names? @unpack
should help!¶
Example 1
Suppose that we have a dictionary equation
containing a range of argumnents \(\mathbf{x}=\left(1+\frac{(i-1)}{10}\right)_{i=1}^{100}\) and values of coefficients \((a=1,b=12,c=\pi)\) characterizing a certain quadratic equation \(ax^2 + bx +c.\)
Then, our dictionary will be of the following form:
equation = Dict([
("x" , collect(range(1, step=.1, length=100)) ),
("a", 1),
("b", 12),
("c", π)
])
Dict{String, Any} with 4 entries:
"c" => π
"x" => [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9 … 10.0, 10.1, 10.2…
"b" => 12
"a" => 1
Example 2
Now suppose we want to evaluate the function calibrated with \((a=1,b=12,c=\pi)\) at \(\mathbf{x}.\)
One way to do it is as follows:
equation["y"] = @. equation["a"]*equation["x"]^2 + equation["b"]*equation["x"] + equation["c"]
100-element Vector{Float64}:
16.141592653589793
17.551592653589793
18.981592653589793
20.431592653589796
21.90159265358979
23.391592653589793
24.9015926535898
26.431592653589792
27.981592653589797
29.55159265358979
31.141592653589793
32.751592653589796
34.38159265358979
⋮
216.78159265358983
219.95159265358978
223.14159265358978
226.35159265358976
229.58159265358978
232.8315926535898
236.10159265358982
239.39159265358978
242.70159265358978
246.03159265358974
249.38159265358982
252.7515926535898
The code works but it does not have the nicest look and might be difficult to change.
In this case, macro @unpack
from package Parameters
can be helpful in making the code more legible.
This macro has the following syntax: @unpack object_1, object_2 = dictionary_1
.
@unpack
extracts objects with names object_1
and object_2
from dictionary_1
and makes them available in the current local scope. dictionary_1
might have other objects inside but @unpack
extracts only those explicitly mentioned objects.
Below you can see how it works
@show a #object a is only defined in dictionary equation, so you cannot access it
UndefVarError: a not defined
Stacktrace:
[1] top-level scope
@ show.jl:955
[2] eval
@ ./boot.jl:360 [inlined]
[3] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
@ Base ./loading.jl:1094
using Parameters
@unpack a, b, c, x = equation
@show a #now it works
a = 1
1
Now to evaluate our quadratic function at \(\mathbb{x}\) we can do it by:
equation["y"] = @. a*x^2 + b*x + c
100-element Vector{Float64}:
16.141592653589793
17.551592653589793
18.981592653589793
20.431592653589796
21.90159265358979
23.391592653589793
24.9015926535898
26.431592653589792
27.981592653589797
29.55159265358979
31.141592653589793
32.751592653589796
34.38159265358979
⋮
216.78159265358983
219.95159265358978
223.14159265358978
226.35159265358976
229.58159265358978
232.8315926535898
236.10159265358982
239.39159265358978
242.70159265358978
246.03159265358974
249.38159265358982
252.7515926535898
You have to remember that in @unpack
, the assignment (=
) operator works normally… for Julia
. This means that if object_1
is a scalar while object_2
is an array, working with those objects can be very different.
This topic was discussed in this section.
Below you can find an example illustrating those differences:
@show equation["a"];
@show a;
@show a += 1;
@show equation["a"]; # `a += 1;` didn't change the value of `equation["a"]`
@show a; # `a += 1;` changed the value of `a`
equation["a"] = 1
a = 1
a += 1 = 2
equation["a"] = 1
a = 2
2
@show equation["x"];
@show x;
@show x .+= 1;
@show equation["x"]; # `x .+= 1;` CHANGED the value of `equation["x"]`
@show x; # `x .+= 1;` changed the value of `x`
equation["x"] = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9, 5.0, 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8, 5.9, 6.0, 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 6.7, 6.8, 6.9, 7.0, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8, 7.9, 8.0, 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7, 8.8, 8.9, 9.0, 9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7, 9.8, 9.9, 10.0, 10.1, 10.2, 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9]
x = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9, 5.0, 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8, 5.9, 6.0, 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 6.7, 6.8, 6.9, 7.0, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8, 7.9, 8.0, 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7, 8.8, 8.9, 9.0, 9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7, 9.8, 9.9, 10.0, 10.1, 10.2, 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9]
x .+= 1 = [2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9, 5.0, 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8, 5.9, 6.0, 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 6.7, 6.8, 6.9, 7.0, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8, 7.9, 8.0, 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7, 8.8, 8.9, 9.0, 9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7, 9.8, 9.9, 10.0, 10.1, 10.2, 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9, 11.0, 11.1, 11.2, 11.3, 11.4, 11.5, 11.6, 11.7, 11.8, 11.9]
equation["x"] = [2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9, 5.0, 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8, 5.9, 6.0, 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 6.7, 6.8, 6.9, 7.0, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8, 7.9, 8.0, 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7, 8.8, 8.9, 9.0, 9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7, 9.8, 9.9, 10.0, 10.1, 10.2, 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9, 11.0, 11.1, 11.2, 11.3, 11.4, 11.5, 11.6, 11.7, 11.8, 11.9]
x = [2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9, 5.0, 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8, 5.9, 6.0, 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 6.7, 6.8, 6.9, 7.0, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8, 7.9, 8.0, 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7, 8.8, 8.9, 9.0, 9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7, 9.8, 9.9, 10.0, 10.1, 10.2, 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9, 11.0, 11.1, 11.2, 11.3, 11.4, 11.5, 11.6, 11.7, 11.8, 11.9]
100-element Vector{Float64}:
2.0
2.1
2.2
2.3
2.4
2.5
2.6
2.7
2.8
2.9
3.0
3.1
3.2
⋮
10.8
10.9
11.0
11.1
11.2
11.3
11.4
11.5
11.6
11.7
11.8
11.9
While a+=1
did not have impact on equation["a"]
, x .+= 1
affected equation["x"]
. It is important to remember this feature of the language.
Ranges¶
Ranges are quite intuitive objects.
They collect elements from a certain range of values.
They can be created either by using :
operator (just like in R
and Matlab
) or by using range
function.
For instance, if we want to create a range from 5 through 120, we can use two options:
@show 5:120;
@show range(5, stop=120);
5:120 = 5:120
range(5, stop = 120) = 5:120
Notice that you are not allowed to use range(5,120)
because at least one of length
, stop
, or step
must be specified explicitely.
If we want, we can set the size step. Again, it can be done in two ways:
@show 5:.5:120;
@show range(5, stop=120, step=.5);
5:0.5:120 = 5.0:0.5:120.0
range(5, stop = 120, step = 0.5) = 5.0:0.5:120.0
You can perform normal arithmetic operations:
(1:5)*2
2:2:10
However, be careful about the order of operations.
Arithmetic operations have a higher priority in Julia
.
For Matlab
users that is the same, but it might be problematic for R
users, where the order is different.
In Julia
if you type 1:5+1
, you will give you 1:6
, while in R
you will get 2:6
.
1:5+1
1:6
Another important difference between other languages and Julia
is the fact that arrays and ranges are not compatible with each other.
This means that arithmetic operations will give you an error message, even if objects are of the same size:
1:3 + [1; 2; 3]
MethodError: no method matching +(::Int64, ::Vector{Int64})
For element-wise addition, use broadcasting with dot syntax: scalar .+ array
Closest candidates are:
+(::Any, ::Any, ::Any, ::Any...) at operators.jl:560
+(::T, ::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} at int.jl:87
+(::Union{Int16, Int32, Int64, Int8}, ::BigInt) at gmp.jl:534
...
Stacktrace:
[1] top-level scope
@ In[293]:1
[2] eval
@ ./boot.jl:360 [inlined]
[3] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
@ Base ./loading.jl:1094
You can extract all element from a range and create an array by using collect
:
collect(1:3)
3-element Vector{Int64}:
1
2
3
In this case, arithmetic operations are allowed:
collect(1:3) + [1; 2; 3]
3-element Vector{Int64}:
2
4
6
Sets¶
To be written