Raghav K. Chhetri
09/18/2020
Running Julia 1.5.1 on 64-bit Windows 10
julia-1.5.1-win64.exe
from https://julialang.org/downloads/C:\Users\Raghav\AppData\Local\Programs\Julia 1.5.1
This is where all the Julia binaries, libraries etc are stored. You won't need to interact with this folder; only pointing out where the installed files are saved.C:\Users\Raghav\.julia
After you double-click the Julia desktop icon, you are brought to a julia>
prompt
This is the Julia terminal, which is also called a Julia REPL. This terminal allows you to run your Julia commands and programs.
Here are a few basic things you can try out on a Julia terminal:
julia>
This is a Julia-specific terminal or Julia REPL
julia> VERSION
Returns Julia version installed
julia> versioninfo()
Returns Julia version and platform information
julia> pwd()
Present working directory
julia> homedir()
Go to home directory, which for me is "C:\\Users\Raghav"
julia> cd ("D:\\")
Go to D:\\ directory
julia> cd()
Go back to home directory
julia> readdir()
List directory contents
julia> readdir(join=true)
Lists directory contents with full paths
Julia comes with a built-in package manager, which can be invoked by typing ]
in the julia
prompt.
julia> ]
(@v1.5) pkg>
Exit to the julia terminal with Ctrl+C
Let's install a few packages using the built-in package manager: Julia packages
(@v1.5) pkg> add IJulia
Julia kernel for Jupyter
(@v1.5) pkg> add PyPlot
Julia interface to a popular plotting library in Python
(@v1.5) pkg> add DataFrames
Tools to work with tabular data in Julia
(@v1.5) pkg> add SymPy
Python's Symbolic Mathematics library in Julia
(@v1.5) pkg> add Images
Image processing library in Julia
(@v1.5) pkg> add ImageView
Image Display GUI for Julia
(@v1.5) pkg> add Interact
Web-based widgets to make Julia code interactive
(@v1.5) pkg> add FileIO
Framework to detect file formats and dispatch to readers/writers
Invoke installed package into your Julia environment as:
julia> using <Name-of-package>
On Unix-like OS (MacOS and Linux), Shell commands can also be executed directly from the Jupyter notebook using a semicolon before the command. These shell commands are then handled by the default system shell.
Eg., ; pwd
to list present working directory
Note: doesn't work on Windows PC
'Shift' + 'Enter'
b
adds a new cell below a current cell#
#=
and =#
Julia terminal can be accessed directly from the notebook too:
pwd() #Present working directory
"D:\\Dropbox (Personal)\\Tricks.of.the.trade\\Julia"
readdir() #Read the content of this directory
4-element Array{String,1}: ".ipynb_checkpoints" "00_Julia_introduction.html" "00_Julia_introduction.ipynb" "00_Julia_introduction.jl"
readdir(join=true) #Show the full path of the contents
4-element Array{String,1}: "D:\\Dropbox (Personal)\\Tricks.of.the.trade\\Julia\\.ipynb_checkpoints" "D:\\Dropbox (Personal)\\Tricks.of.the.trade\\Julia\\00_Julia_introduction.html" "D:\\Dropbox (Personal)\\Tricks.of.the.trade\\Julia\\00_Julia_introduction.ipynb" "D:\\Dropbox (Personal)\\Tricks.of.the.trade\\Julia\\00_Julia_introduction.jl"
Let's check the current status of the packages that I've installed thus far:
] status
Status `C:\Users\Raghav\.julia\environments\v1.5\Project.toml` [5ae59095] Colors v0.12.4 [a81c6b42] Compose v0.8.2 [a93c6f00] DataFrames v0.21.7 [5789e2e9] FileIO v1.4.3 [c91e804a] Gadfly v1.3.0 [f67ccb44] HDF5 v0.13.6 [47d2ed2b] Hyperscript v0.0.3 [7073ff75] IJulia v1.21.3 [6a3955dd] ImageFiltering v0.6.15 [6218d12a] ImageMagick v1.1.6 [86fae568] ImageView v0.10.9 [916415d5] Images v0.22.4 [c601a237] Interact v0.10.3 [c3e4b0f8] Pluto v0.11.14 [438e738f] PyCall v1.91.4 [d330b81b] PyPlot v2.9.0 [24249f21] SymPy v1.0.28 [10745b16] Statistics
Conventions:
!
... more on this laterUnicode symbols can be inserted in your code using \tab
key after the LaTeX symbol. For instance: \alpha + Tab
below:
Full list of Unicode symbols here
α = 1/137
0.0072992700729927005
typeof(α)
Float64
π
π = 3.1415926535897...
typeof(π)
Irrational{:π}
round(π)
3.0
Inf
Inf
1/Inf
0.0
1 == 1.0
true
isequal(1,1.0)
true
So, although 1
is an integer, and 1.0
is a float, numerically they are equal, and thus both ==
and isequal
return true
1 === 1.0
false
==
checks for numerical equality
===
evaluates to true only when two objects are programmatically indistinguishable and you cannot write code that demonstrates any difference between them. So, only true when both are not only numerically the same but also share the same memory locations. More here
exp(π^π)/(1/α + 1/π)
4.984094478274027e13
z = 1 + 3*im
1 + 3im
typeof(z)
Complex{Int64}
abs(z)
3.1622776601683795
exp(-im*z)
10.852261914197959 - 16.901396535150095im
\div + Tab
100 ÷ 9
11
div(100,9)
11
mod(100,13)
9
mod2pi(7)
0.7168146928204135
mod(7,2*π)
0.7168146928204138
sqrt(α)
0.0854357657716761
cbrt(α)
0.19398127563103507
log(α) #Natural log
-4.919980925828125
log10(α)
-2.1367205671564067
exp(α)
1.0073259746799632
xr = 10*randn(5) # Generate an array of five random numbers (normally-distributed)
5-element Array{Float64,1}: -12.821956993273712 -8.683820552480404 -12.432308497818362 21.983405018630076 -9.21796091049308
xr[1] # First element of the array 'xr'
-12.821956993273712
xr[end] # Last element of the array 'ar'
-9.21796091049308
tmp = xr[2:4] # Slicing an array to grab 2nd to 4th elements
3-element Array{Float64,1}: -8.683820552480404 -12.432308497818362 21.983405018630076
xr[xr .> 0] #Select elements of the array that meet a certain criteria
1-element Array{Float64,1}: 21.983405018630076
exp.(sin.(xr))
5-element Array{Float64,1}: 0.7766132637390659 0.5091591606896105 1.1430051631348843 1.0077735373808017 0.8143656389765686
x= [1, 2, 3]
y= x # point 'y' to 'x'
3-element Array{Int64,1}: 1 2 3
println(x, typeof(x))
println(y, typeof(y))
x === y #evaluates to true only if both are referring to the same object in memory
[1, 2, 3]Array{Int64,1} [1, 2, 3]Array{Int64,1}
true
Now, let's modify elements of x
and see what happens to y
x[1]= 0
x[2]= 0
println(x, typeof(x))
println(y, typeof(y))
x === y
[0, 0, 3]Array{Int64,1} [0, 0, 3]Array{Int64,1}
true
We see that the elements of y
are also updated since both variables are bound to the same object and modifying that object's content impacts both variables
And, it works both ways i.e., modifying elements of y
also updates the elements x
is referencing to:
y[1]= 1
y[3]= -1
println(x, typeof(x))
println(y, typeof(y))
x === y
[1, 0, -1]Array{Int64,1} [1, 0, -1]Array{Int64,1}
true
Next, let's see what happens when we assign an entirely new object to x
. Does y
follow?
x= [-π,0,π]
println(x, typeof(x))
println(y, typeof(y))
x === y
[-3.141592653589793, 0.0, 3.141592653589793]Array{Float64,1} [1, 0, -1]Array{Int64,1}
false
In this case, we see that only x
is updated since an entirely new object was assigned to x
. In other words, x
is now bound to this new object, whereas y
still remains bound to the previous object
Now, if you wish to create a separate copy altogether, we need to use copy
or deepcopy
:
copy(x)
Create a shallow copy of x: the outer structure is copied, but not all internal values. For example, copying an array produces a new array with identically-same elements as the original.
deepcopy(x)
Create a deep copy of x: everything is copied recursively, resulting in a fully independent object. For example, deep-copying an array produces a new array whose elements are deep copies of the original elements. Calling deepcopy on an object should generally have the same effect as serializing and then deserializing it.
a = [1,2,3, [4,5,6]]
b= copy(a)
c= deepcopy(a)
println(a)
println(b)
println(c)
println(b === a)
println(c === a)
Any[1, 2, 3, [4, 5, 6]] Any[1, 2, 3, [4, 5, 6]] Any[1, 2, 3, [4, 5, 6]] false false
a[1:3]= [0,0,0]
println(a)
println(b)
println(c)
Any[0, 0, 0, [4, 5, 6]] Any[1, 2, 3, [4, 5, 6]] Any[1, 2, 3, [4, 5, 6]]
Only a
changed this time and the copies b
and c
were unaffected
Next, let's tinker with the content of the nested array at the 4th index of a
and see what happens:
a[4][1:3]= [0,0,0]
println(a)
println(b)
println(c)
Any[0, 0, 0, [0, 0, 0]] Any[1, 2, 3, [0, 0, 0]] Any[1, 2, 3, [4, 5, 6]]
This time, change to the nested array in a
carried over to the shallow copy b
whereas the deep copy c
remained unaffected
So, we see that the shallow copy b
and the original a
share their contents in the array at the 4th index as copy
didn't recursively look inside the array at this index
Moreover, if we were to replace the 4th index of a
entirely (not tinkering with the contents, a pure replacement), then it only affects a
a[4]= [1,2,3,4,5,6,7,8,9,0]
println(a)
println(b)
println(c)
Any[0, 0, 0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]] Any[1, 2, 3, [0, 0, 0]] Any[1, 2, 3, [4, 5, 6]]
Every binary operation such as +, -, ^, *, /
etc has a vectorized version in Julia, which performs element-by-element operation by default.
a = [1,3,17]
3-element Array{Int64,1}: 1 3 17
a .^ 2
3-element Array{Int64,1}: 1 9 289
a .+ 3 #Add 3 to each element of the array 'a'
3-element Array{Int64,1}: 4 6 20
a .÷ 3 #Divide each element of the array 'a' by 3
# Note that this division is an integer division. \div + tab gives the above symbol
3-element Array{Int64,1}: 0 1 5
a ./ 3
# This now is a full division. Notice that the values are now floating-point whereas earlier it was an integer
3-element Array{Float64,1}: 0.3333333333333333 1.0 5.666666666666667
a .* 3
3-element Array{Int64,1}: 3 9 51
Btw, for multiplication *
and full division/
, both the dotted versions .*
or ./
give the same result as the undotted versions *
and /
when an array is multiplied by a number. See Matrices below for a departure from this.
array = [1 2 3 4 5; 7 8 9 10 0; 5 3 2 7 6] #creating a matrix (two-dimensional array) row-by-row
3×5 Array{Int64,2}: 1 2 3 4 5 7 8 9 10 0 5 3 2 7 6
arr = [[1,7,5] [2,8,3] [3,9,2] [4,10,7] [5,0,6]] #creating a matrix column-by-column
3×5 Array{Int64,2}: 1 2 3 4 5 7 8 9 10 0 5 3 2 7 6
x = [1,8,7] # An array with 3 rows (column-vector)
#=
Alternatives:
x = [1
8
7]
#or
x = [1;8;7]
=#
3-element Array{Int64,1}: 1 8 7
y = [7 6 5] # An array with 3 columns (row-vector)
1×3 Array{Int64,2}: 7 6 5
x * y # Matrix multiplication
3×3 Array{Int64,2}: 7 6 5 56 48 40 49 42 35
x .* y #element-by-element multiplication by broadcasting both matrices
3×3 Array{Int64,2}: 7 6 5 56 48 40 49 42 35
In this case, notice how the dotted multiplication .*
also gave the same result as a regular Matrix multiplication. So, one might incorrectly conclude that .*
is also performing Matrix multiplication. However, under the hood, the former is an element-by-element operation whereas the latter is doing a true Matrix multiplication.
During element-by-element multiplication, this is what happens:
x
is broadcast across additional columns to form this 3x3 array:
1 1 1
8 8 8
7 7 7
and, y
is broadcast across additional rows to form this 3x3 array:
7 6 5
7 6 5
7 6 5
Then, an element-by-element multiplication is performed, which outputs a 3x3 array.
The difference between the two operations becomes clear if we permute the order of multiplication:
y .* x
3×3 Array{Int64,2}: 7 6 5 56 48 40 49 42 35
y * x
1-element Array{Int64,1}: 90
Since the dotted multiplication is broadcasting first and doing an element-by-element multiplication, y .* x
and x .* y
give the same 3x3 array. However, x * y
and y * x
perform Matrix multiplication and thus the resulting matrices are of different sizes.
A = x .+ y # element-by-element addition of two matrices
3×3 Array{Int64,2}: 8 7 6 15 14 13 14 13 12
tmp = x ./ y # element-by-element division of two matrices
3×3 Array{Float64,2}: 0.142857 0.166667 0.2 1.14286 1.33333 1.6 1.0 1.16667 1.4
function f(x,y,z)
r = sqrt(x^2 + y^2 + z^2)
end
f (generic function with 1 method)
f(1,1,1)
1.7320508075688772
f(x)= 4*x.^2 # A one-line function
f (generic function with 2 methods)
f(2)
16
f([1,2,3,4,5])
5-element Array{Int64,1}: 4 16 36 64 100
function g(x,y,z)
r²= x^2 + y^2 + z^2
r = sqrt(r²)
return(round(r,digits=2),r²)
end
g (generic function with 1 method)
g(3,0,0)
(3.0, 9)
points = ((0,0,0), (3,0,0), (3,3,3))
for p in points
(x,y,z)= (p[1],p[2],p[3])
println("At ", p, ", function returns ", g(x,y,z))
end
At (0, 0, 0), function returns (0.0, 0) At (3, 0, 0), function returns (3.0, 9) At (3, 3, 3), function returns (5.2, 27)
using PyPlot #Here we are invoking the PyPlot package to start plotting stuff
foos = 10*randn(300)
plot(foos,"-k")
1-element Array{PyCall.PyObject,1}: PyObject <matplotlib.lines.Line2D object at 0x000000004F905AF0>
x = range(0,step=0.1,stop=6*π)
y = sin.(x)
plot(x, y)
xlabel("X value")
ylabel("Y value")
title("Sinθ")
grid("on")
scatter(x,y,alpha=0.5)
xlabel("X value")
ylabel("Y value")
title("Scatter plot of Sinθ")
grid("on")
x= range(-3*π,stop=3*π,length=201)
y= sinc.(x)
title("sinc function")
xlabel("x axis")
ylabel("y axis")
plot(x, y, linewidth=2.0, linestyle="--", color= "pink");
PyPlot.plt.hist(randn(1000),bins=10); # Histogram
PyPlot.title("Histogram")
grid("on")
f(x) = 4*x.^2
x = [-50,-25,0,25,50]
scatter(x,f(x));
xx = range(-100, stop =100, step=3)
scatter(xx,f(xx))
PyObject <matplotlib.collections.PathCollection object at 0x000000004BD458B0>
Using Python libraries from Julia
using PyCall
@pyimport pylab
@pyimport numpy as np
x = np.linspace(-π,π,1000)
y1 = np.sin(2*x .+ 5);
y2 = np.sin(2*x + np.cos(2*x) .+ 5);
plt = pylab
plt.plot(x, y1,"-b",label="sin(2x + 5)")
plt.plot(x, y2,"--g",label="sin(2x + cos(2x) + 5)")
plt.ylim(-1.25, 1.5)
plt.legend(loc="upper right")
PyObject <matplotlib.legend.Legend object at 0x000000005040DEB0>
Using pylab
interrupts the kernel >> "The kernel appears to have died. It will restart automatically"
However, if PyPlot
is in the system, then that doesn't happen...
To be continued...