Computational Analysis of Social Complexity
Fall 2026, 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
Packages and Software Engineering sections of QuantEcon julia lectures
Julia documentation
Documentation for packages: Graphs, DataFrames, Plots
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!
4242We can also do basic arithmetic in the way you would expect
10 * 3301 + 23So 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 python1.5881567008330448553.34 / 12.942.89457364341085We can mix and match integers and floats
25 / 2.510.025 / 2 # dividing integers returns a float (notice the `.`)12.5notice we used
#to define a comment
Text Data¶
Not all data is numerical
Some is textual
To represent text in Julia we use a
StringTo 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 array3-element Vector{Float64}:
1.0
2.0
3.14[1 2 3] # a 1x3 matrix1×3 Matrix{Int64}:
1 2 3[1 2; 3 4] # a 2x2 matrix2×2 Matrix{Int64}:
1 2
3 4[1 2
3 4] # another way to write a 2x2 matrix2×2 Matrix{Int64}:
1 2
3 4[1 "hello"; 2 "world"] # a 2x2 matrix with int and string2×2 Matrix{Any}:
1 "hello"
2 "world"Accessing array items¶
We can use
[N]to access the Nth elementWe can also use
[i:j]to access itemsithroughjFinally 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
102Note that unlike Python, Julia starts counting at 1
Also note that
endcan be used to refer to the last element,end-1to second to last, and so on
[1, 2, 3, 4][end]4[1, 2, 3, 4][end-2]2Tuples¶
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]5The main differences between tuples and arrays are
Tuples are meant to hold immutable or non-changing data
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 timesThey 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" => 2Dictionaries 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 inname = value
x = 11y = 4242x + y # 'use' or 'refer to' x and y43m1 = [1 0; 0 1]2×2 Matrix{Int64}:
1 0
0 1m2 = [1 2; 3 4]2×2 Matrix{Int64}:
1 2
3 4m1 * m2 # matrix multiplication2×2 Matrix{Int64}:
1 2
3 4m2 * m2 # again -- but with something besides identity matrix!2×2 Matrix{Int64}:
7 10
15 22d = Dict("X" => 1, "Y" => 2)Dict{String, Int64} with 2 entries:
"Y" => 2
"X" => 1d["X"]1Functions¶
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
typeoffunction
typeof(1)Int64typeof(2.0)Float64typeof([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 numbers6inv([1 2; 3 4]) # matrix inverse2×2 Matrix{Float64}:
-2.0 1.0
1.5 -0.5size([1 2; 3 4]) # number of (rows, columns) in matrix(2, 2)length([1, 2, 3]) # number of elements in array3length([1 2; 3 4]) # returns total number of elements in a Matrix4rand(2, 2, 2) # a 2x2x2 array of random numbers, sampled from uniform[0,1] dist2×2×2 Array{Float64, 3}:
[:, :, 1] =
0.362076 0.860015
0.297667 0.572139
[:, :, 2] =
0.775214 0.145788
0.815195 0.820176Julia 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
endfunction mean(x)
total = sum(x)
N = length(x)
total / N
endmean (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.48944092617754337If a function only contains one line of code, you can also use a shorthand notation:
function_name(arg1, arg2) = # stepadd_two(x) = x + 2add_two (generic function with 1 method)add_two(40)42Getting 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
?mapParseError:
# 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?extremaControl Flow¶
Julia has the basic elements of control flow:
if-elsestatementsforloops
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.0for i in 1:5 # range of numbers 1 to 5
println(i, " ", i^2)
end1 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
usingkeywordFor example, we can load the
Datespackage and start using it
using Datest1 = Dates.now()2024-08-19T20:39:10.885Dates.format(t1, "yyyy-mm-dd")"2024-08-19"t2 = Dates.now()2024-08-19T20:39:11.197t2 > t1truet3 = 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 PackageName2
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)using DataFrames
df = DataFrame(c1=1:10, c2=(1:10).^2)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)Pkg.add("RDatasets") # common datasets from R programming language
using RDatasets
school = RDatasets.dataset("mlmRev","Hsb82")
@df school density(:MAch, group = :Sx) Resolving package versions...
No Changes to `~/.julia/environments/v1.10/Project.toml`
No Changes to `~/.julia/environments/v1.10/Manifest.toml`
@df school density(:MAch, group = (:Sx, :Sector))