Skip to article frontmatterSkip to article content

Julia Foundations

University of Central Florida
Valorum Data

Computational Analysis of Social Complexity

Fall 2025, Spencer Lyon

Prerequisites

  • Laptop or personal computer with internet connection

Outcomes

  • Understand the main benefits and features of Julia
  • See how to define variables, functions, and types in Julia
  • Install commonly used packages for Graphs, DataFrames, Plotting and more

References

What is Julia?

  • Julia is a relatively new programming language (first public release in 2012, 1.0 release in 2018)
  • General purpose, but specializes in numerical computation
  • Leverages advanced compiler technology to generate very efficient code
  • It can be as clear to read and write as Python, and as quick to evaluate as C!

Core Types

  • We’ll start by learning about the core datatypes built in to Julia
  • Along the way we’ll pick up some of the key syntax elements
  • We will move quickly, so some prior programming experience would be helpful

Numbers

  • Let’s start with numbers
  • To work with a number, just type it!
42
42
  • We can also do basic arithmetic in the way you would expect
10 * 3
30
1 + 2
3
  • So far we’ve worked with integers (whole numbers)
  • Julia can also work with numbers containing a decimal
  • In Julia these are called floating point numbers
1.234 ^ 2.2  # use `^` for exponentiation, not `**` like in python
1.5881567008330448
553.34 / 12.9
42.89457364341085
  • We can mix and match integers and floats
25 / 2.5
10.0
25 / 2  # dividing integers returns a float (notice the `.`)
12.5
  • notice we used # to define a comment

Text Data

  • Not all data is numerical
  • Some is textual
  • To represent text in Julia we use a String
  • To define a String we use quotation marks (") as below
"My name is Spencer"
"My name is Spencer"
"1"  # an integer in a string
"1"
  • You cannot use single quotes for strings as in other languages (like Python or Javascript)
  • Go ahead... try it by removing the # and excuting the cell below
# 'hello'
"""
This

is

also

a

string
"""
"This\n\nis\n\nalso\n\na\n\nstring\n"

Arrays

  • When doing numerical work, we often need to deal with multiple pieces of data at the same time
  • In Julia the default way of doing this is to use an array
  • Arrays are defined with [ and ] as below
[1, 2, 3.14]  # a 3 element array
3-element Vector{Float64}: 1.0 2.0 3.14
[1 2 3]  # a 1x3 matrix
1×3 Matrix{Int64}: 1 2 3
[1 2; 3 4]  # a 2x2 matrix
2×2 Matrix{Int64}: 1 2 3 4
[1 2
 3 4]  # another way to write a 2x2 matrix
2×2 Matrix{Int64}: 1 2 3 4
[1 "hello"; 2 "world"]  # a 2x2 matrix with int and string
2×2 Matrix{Any}: 1 "hello" 2 "world"

Accessing array items

  • We can use [N] to access the Nth element
  • We can also use [i:j] to access items i through j
  • Finally we can use [[n1, n2]] to access the n1th and n2th elements
[100, 101, 102, 103][2]
101
[100, 101, 102, 103][2:4]
3-element Vector{Int64}: 101 102 103
[100, 101, 102, 103][[1, 3]]
2-element Vector{Int64}: 100 102
  • Note that unlike Python, Julia starts counting at 1
  • Also note that end can be used to refer to the last element, end-1 to second to last, and so on
[1, 2, 3, 4][end]
4
[1, 2, 3, 4][end-2]
2

Tuples

  • There is another data type for holding “lists” of data called a tuple
  • Tuples are create using parenthesis instead of square brackets as follows
(1, 2, 3, "hello")
(1, 2, 3, "hello")
("hello", 5)
("hello", 5)
("hello", 5)[2]
5
  • The main differences between tuples and arrays are
    1. Tuples are meant to hold immutable or non-changing data
    2. Tuples aren’t usually meant for computation or linear algebra

Dictionary

  • Very often in programming we want to be able to associate a key or name to a specific value
  • One data type for doing that is a Dict
  • Dicts are created with the somewhat inconvenient syntax Dict(name => value, ...) where the ... means we can repeat the pattern multiple times
  • They keys and values can be of any type
Dict("x" => 1, 2 => "y", ["w", "z"] => [1, 2, 3])
Dict{Any, Any} with 3 entries: 2 => "y" ["w", "z"] => [1, 2, 3] "x" => 1
# use `[name]` to access element with `name`
Dict("x" => 1, 2 => "y", ["w", "z"] => [1, 2, 3])[2]
"y"
Dict("x" => 1, "y" =>2)
Dict{String, Int64} with 2 entries: "x" => 1 "y" => 2
  • Dictionaries are often used for passing around groups of parameters
  • We’ll see examples later on

Named Tuples

  • The final “collection” we’ll talk about is the named tuple

  • It is a hybrid between a tuple and a dictionary

  • To create them we use the synax (name = value, ...)

  • They names or keys need to be just names (not numbers or arrays). The values can be anything

(x = 1, y = 2, z="hello")
(x = 1, y = 2, z = "hello")
(x = 1, y = 2, z="hello").z # use `.name` to access item
"hello"
  • Named tuples are a newer feature of Julia
  • They are often used for the same purpsoes as dictionaries because the syntax is much cleaner

Variables

  • Often when programming, we need to refer to the same piece of data more than once
  • To do this we use a variable
  • Variables are defined using an =, as in name = value
x = 1
1
y = 42
42
x + y  # 'use' or 'refer to' x and y
43
m1 = [1 0; 0 1]
2×2 Matrix{Int64}: 1 0 0 1
m2 = [1 2; 3 4]
2×2 Matrix{Int64}: 1 2 3 4
m1 * m2  # matrix multiplication
2×2 Matrix{Int64}: 1 2 3 4
m2 * m2  # again -- but with something besides identity matrix!
2×2 Matrix{Int64}: 7 10 15 22
d = Dict("X" => 1, "Y" => 2)
Dict{String, Int64} with 2 entries: "Y" => 2 "X" => 1
d["X"]
1

Functions

  • Most Julia programs do more than basic arithmetic operations on data
  • To apply an operation to a piece of data, we call a function
  • To call a function we use the function_name(data1, data2)
  • A very handy function is the typeof function
typeof(1)
Int64
typeof(2.0)
Float64
typeof([1,2,3])
Vector{Int64} (alias for Array{Int64, 1})
typeof([1 2; 3 4.0])
Matrix{Float64} (alias for Array{Float64, 2})
  • Many standard operations are built in to Julia as functions
sum([1, 2, 3])  # compute sum of array of numbers
6
inv([1 2; 3 4])  # matrix inverse
2×2 Matrix{Float64}: -2.0 1.0 1.5 -0.5
size([1 2; 3 4])  # number of (rows, columns)  in matrix
(2, 2)
length([1, 2, 3])  # number of elements in array
3
length([1 2; 3 4])  # returns total number of elements in a Matrix
4
rand(2, 2, 2)  # a 2x2x2 array of random numbers, sampled from uniform[0,1] dist
2×2×2 Array{Float64, 3}: [:, :, 1] = 0.362076 0.860015 0.297667 0.572139 [:, :, 2] = 0.775214 0.145788 0.815195 0.820176
  • Julia has 1000s of functions
  • We’ll learn more as we go along...
  • Just watch for the pattern with parentisis: name(args)

Defining Functions

  • Functions are used to execute a predefined set of operations
  • Defining our own funcitons allows us to break programs into small, easily written an understood components
  • We define functions using the syntax
function name(arg1, arg2)
    # steps
end
function mean(x)
    total = sum(x)
    N = length(x)
    total / N
end
mean (generic function with 1 method)
mean([1, 2, 3])
2.0
# mean of 1000 random samples from U[0,1] -- should be ~ 0.5
mean(rand(1000))
0.48944092617754337
  • If a function only contains one line of code, you can also use a shorthand notation:
function_name(arg1, arg2) = # step
add_two(x) = x + 2
add_two (generic function with 1 method)
add_two(40)
42

Getting help for functions

  • Given that there are so many functions, sometimes it is hard to remember exactly what a function does
  • Thankfully we can get help from Julia
  • If we type ?function_name, Julia will present us with documentation about the function
?map
ParseError:

# Error @ /Users/sglyon/Teaching/UCF/CAP-6318/book-myst/week01/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_Y145sZmlsZQ==.jl:1:1

?map

╙ ── not a unary operator



Stacktrace:

 [1] top-level scope

   @ ~/Teaching/UCF/CAP-6318/book-myst/week01/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_Y145sZmlsZQ==.jl:1
?extrema

Control Flow

  • Julia has the basic elements of control flow:
    • if-else statements
    • for loops
if 1 > 2 # no parenthesis needed
    println("what???")
else     # else is optional
    return mean([1, 2, 3])
    print("phew")
end      # all "blocks" terminate with word `end`
2.0
for i in 1:5 # range of numbers 1 to 5
    println(i, " ", i^2)
end
1 1
2 4
3 9
4 16
5 25
  • We will see many more examples as we go forward

Packages

  • Julia comes ready to go with many powerful functions and data types
  • However, there is a very active community of Julia programmers who are experts in different subfields of science and engineering
  • This has led to the development of vibrant and exciting ecosystem of packages or toolboxes for performing specific tasks
  • We can access these routines by using Julia packages

Loading packages

  • By default Julia ships with a “standard library”
  • These are packages that come bundled with Julia itself and are pre-installed
  • To load a package and all of its types/functions use the using keyword
  • For example, we can load the Dates package and start using it
using Dates
t1 = Dates.now()
2024-08-19T20:39:10.885
Dates.format(t1, "yyyy-mm-dd")
"2024-08-19"
t2 = Dates.now()
2024-08-19T20:39:11.197
t2 > t1
true
t3 = DateTime(1776, 7, 4)
1776-07-04T00:00:00
"America is $(t1 - t3) ($(floor(t1 - t3, Dates.Day))) old"
"America is 7830160750885 milliseconds (90626 days) old"

Installing Packages

  • In addition to the standard library, we can also use packages created by other Julia users
  • To use a 3rd party package, we first need to install it
  • There are two ways to do this

1

]add PackageName

2

using Pkg  # a standard library package
Pkg.add("PackageName")

Let’s try them both

]add Plots
   Resolving package versions...
  No Changes to `~/.julia/environments/v1.10/Project.toml`
  No Changes to `~/.julia/environments/v1.10/Manifest.toml`
using Pkg
Pkg.add("DataFrames")
   Resolving package versions...
  No Changes to `~/.julia/environments/v1.10/Project.toml`
  No Changes to `~/.julia/environments/v1.10/Manifest.toml`
  • After installing packages, we can load and use them just as we did the standard library packages
using Plots  # Python: from Plots import *
plot([sin, cos], -2pi, 2pi)
Loading...
using DataFrames
df = DataFrame(c1=1:10, c2=(1:10).^2)
Loading...

Package Composability

  • One unique feature sof Julia is that most of the language itself, in addition to packages, are written in Julia
  • For other languages like Python or R the “built in” part of the language is often written in another language like C
  • This difference has a large impact for Julia users
    • Built in code and user code (including packages) are given the same “treatment”
    • Anything the language creators can do, so can you
  • A practical implication of this is that packages can operate on built in types (like we saw in our examples above) as well as types from other packages
  • Let’s see what this looks like by plotting a DataFrame
# install "StatsPlots", which links Plots and DataFrames
Pkg.add("StatsPlots")

using StatsPlots
   Resolving package versions...
  No Changes to `~/.julia/environments/v1.10/Project.toml`
  No Changes to `~/.julia/environments/v1.10/Manifest.toml`
@df df scatter(:c1, :c2)
Loading...
Pkg.add("RDatasets") # common datasets from R programming language
using RDatasets
school = RDatasets.dataset("mlmRev","Hsb82")
@df school density(:MAch, group = :Sx)
Loading...
@df school density(:MAch, group = (:Sx, :Sector))
Loading...