MITB Banner

Watch More

Basics Of Julia Programming Language For Data Scientists

Basics of Julia

Julia is a relatively new, fast, high-level dynamic programming language. Although it is a general-purpose language and can be used to write all kinds of applications, much of its package ecosystem and features are designed for high-level numerical computing. Julia draws from various languages, from the more low-level systems programming languages like C to high-level dynamic typing languages such as Python, R and MATLAB. And this is reflected in its optional typing nature, its syntax and its features. 

Register for our free hands-on workshop on oneAPI AI Analytics Toolkit

Julia doesn’t have classes; it works around this by supporting the quick creation of custom types and methods for these types. However, these functions are not limited to the types they are created for and can have many versions, a feature called multiple dispatching. It supports direct calls to C functions without any wrapper API, for example, the struct keyword used to define custom types. And instead of defining scope based on indentation like Python, Julia uses the keyword end, much akin to MATLAB. 

It would be ridiculous to summarize all its features and idiosyncrasies; you can refer to the wiki or docs welcome page for a more comprehensive description of Julia. With that, let’s begin our journey of learning the basics of Julia and then using it to create machine learning solutions. But before we do that, you’ll need to install Julia and set up a development environment. Although Julia comes with its own REPL, much like Python IDLE, we will be using Jupyter notebooks. Here’s how to set up Julia for running it in Jupyter notebooks:

  1. Download and install Julia from its website.
  2. Open the Julia command line.
  3. Run the command using Pkg (you’ll learn more about this later).
  4. Install the Julia kernel for Jupyter using  Pkg.add("IJulia").
  5. Now when you create a Jupiter notebook and you will have an option to select Julia.

Basics of Julia

IO, Variables and Arithmetics 

Input & Output

We’ll start how every programming tutorial starts with the trusty “Hello World” statement. For printing things to the terminal Julia provides the println() method.

 println("Hello World")
 Hello World  

To take in input from the command line you can use the (Java-inspired?) readline() and readlines() methods.

Operators

All arithmetic operators behave similar to their Python counterparts, except for the exponent operator, **, which is replaced by ^ in Julia.  

 println(6 + 5)
 println(3 - 8)
 println(5 * 7)
 println(666 / 11)
 println(666 % 11)
 println(2 ^ 10)

 11
 -5
 35
 60.54545454545455
 6
 1024 
Variables

Variable names in Julia must start with an underscore, an alphabet, or a subset of Unicode code points greater than 00A0 . The following characters may also include ! and digits. Weirdly enough, Julia also supports using emojis and operators like + as an identifier; keep in mind that the behaviour of the operators does change after being used as an identifier. This happens because the + symbol is originally an identifier that refers to a sum function. 

 best_num = 73
 println(typeof(best_num))
 π = 3.1415
 println(typeof(π))
 ????  = "Like a boss"
 println(typeof( ???? ))
 # doesn't work in Jupyter notebooks, try in Julia  CLI
 # + = "Do you know the way?"
 # print(+)

 Int64
 Float64
 String 

You can find out more about Julia’s variable naming and styling conventions here.

Comments & Strings

Like Python, single-line comments can be made in Julia using the # symbol, for multi-line comments, it uses the #=  =# syntax.

 # One line comment

 #= Multiple
 line 
 comment=# 

There are two ways of defining a string in Julia; we’ve already used single quotes, “ ”, the other is is triple quotes, “”” “””. There are a few functional differences between strings enclosed in single and triple quotes. The biggest being that you can use quotes inside triple-quote strings without needing to escape it.

 "This is a string."
 """Look, Ma! No "errors"! """ 

Strings are indexable and sliceable, but the indexing starts at one. Yes, Julia uses 1-based indexing, not 0-based like Python. Wars are fought over lesser issues. Additionally, Julia provides keywords begin and end that can be used as shorthand for the first and last indices.

 println(s[end])
 println(s[begin])
 println(s[begin + 2])
 println(s[2:5])

 .
 T
 i
 his  

Another thing to note is the ‘ ‘ define a character, not a string.

 typeof('a')

 Char 

In Julia, the $ symbol is used to insert variables into a string and to evaluate

expressions within a string. 

 name = "Aditya"
 num_articles = 40
 num_papers = 18
 println("Hello, my name is $name.")
 println("I have written $num_articles articles and covered $num_papers research papers.")

 Hello, my name is Aditya.
 I have written 40 articles and covered 18 research papers. 

There are three ways of concatenating strings in Julia. The first is to use string interpolation. 

 s1 = "How many cats "
 s2 = "is too many cats?"
 ???? = 3
 println("$s1$s2")

 "How many cats is too many cats?" 

Another is to use the string() method that can also be used to convert other non-string inputs to strings.

 string("I don't know, but ", ????, " is too few.")
 "I don't know, but 3 is too few." 

Or use the * operator. 

s1*s2

You can learn more about Strings in Julia here.

Data Structures 

Julia offers three data structures, Tuple, Arrays and Dictionaries. Functionally all of these act just like their Python counterparts; just the syntax is a bit different.

Tuples

Much like Python, tuples are immutable and also have a dictionary-like variant. To define a tuple an ordered collection of elements enclosed in ( )

 animals = ("penguins", "cats", " kangaroo")
 animals[1]

 "penguins" 

Introduced in Julia 1.0, NamedTuples act just like tuples except that each element has a name associated with it.  They use a special syntax using = inside a tuple.

 animals = (bird = "ducks", mammal = "cats", marsupial = "kangaroo")
 animals.bird # animals[1] 

Dictionaries

Dictionaries are used to store sets of data related to one another and can be created using the Dict() function and the => syntax.

 contacts  = Dict("Sheldon" => "732-1371", "Ghostbusters" => "555-2368")
 contacts["Elliot"] = "127-0001"
 contacts 

 Dict{String, String} with 2 entries:
   "Sheldon"      => "732-1371"
   "Ghostbusters" => "555-2368" 

The pop!() method can be used to delete elements from a dictionary.

 pop!(contacts , "Elliot")  #we'll learn about the ! later  
 contacts 

 Dict{String, String} with 2 entries:
   "Sheldon"      => "732-1371"
   "Ghostbusters" => "555-2368" 

Arrays

Arrays in Julia are ordered mutable collections that can store mixed types. They can be indexed, sliced and edited using the push!() & pop!() methods. 

 HIMYM = ["Ted", "Barney", "Lily", "Marshall", "Robin"]
 fibonacci = [1, 1, 2, 3, 5, 8, 13]
 mix = [7, 3, "Ted", "Robyn"]
 HIMYM[2] = "The Lengend Himself"
 println(HIMYM)

 ["Ted", "The Lengend Himself", "Lily", "Marshall", "Robin"]

 push!(fibonacci, 21)
 push!(fibonacci, 34)
 pop!(fibonacci)
 println(fibonacci)

 9-element Vector{Int64}:
   1
   1
   2
   3
   5
   8
  13
  21

The concept of shallow and deep copies persists in Julia as well, and to copy an Array, you can use the familiar copy() method. 

 numbers = fibonacci
 numbers[1] = 404
 println(fibonacci)

 [404, 1, 2, 3, 5, 8, 13, 21]
 fibonacci[1] = 1
 numbers = copy(fibonacci)
 numbers[1] = 404
 println(fibonacci)

 [1, 1, 2, 3, 5, 8, 13, 21] 

You can read more about data structures in Julia here.

Looping & Conditionals

while loops

 HIMYM = ["Ted", "Barney", "Lily", "Marshall", "Robin"]
 i = 1
 while i <= length(HIMYM)
     friend = myfriends[i]
     println("Have you met $friend?")
     global i += 1
 end

 Have you met Ted?
 Have you met Robin?
 Have you met Barney?
 Have you met Lily?
 Have you met Marshall? 

for loops

The above example can be recreated using for loops as

 for friend in HIMYM
     println("Have you met $friend?")
 end 

The real power of for loops comes to light when working with multidimensional data. Let’s create additional tables to illustrate this.

 m, n = 5, 5
 A = fill(0, (m, n))
 for j in 1:n
     for i in 1:m
         A[i, j] = i + j
     end
 end
 println(A) 

Here’s a relatively more compact Julia syntax for writing nested for loops.

 B = fill(0, (m, n))
 for j in 1:n, i in 1:m
     B[i, j] = i + j
 end
 println(B) 

And finally, the most “Julia” way using an array comprehension.

C = [i + j for i in 1:m, j in 1:n]

You can learn more about loops in Julia here.

Conditionals 

Not only does Julia offer the if-else syntax it also supports the much-missed ternary operator syntax for writing conditional statements. 

 x = 7
 y = 3

 if x > y
     x
 else
     y
 end 

is equivalent to 

(x > y) ? x : y

You can learn more about control flow statements here.

Functions

Julia supports three ways of declaring functions. The first used the keywords function and end:

 function sayhi(name)
     println("Hi $name, it's great to see you!")
 end
 function f(x)
     x^2
 end 

These functions can also be written as one line functions:

 sayhi2(name) = println("Hi $name, it's great to see you!")
 f2(x) = x^2 

or as “anonymous” functions 

 sayhi3 = name -> println("Hi $name, it's great to see you!")
 f3 = x -> x^2 

Duck-typing in Julia

In Julia, functions try to make do with whatever type of argument is passed to them.  For example, hello will work an integer, and f will work on a matrix or string.

 println(hello(55595472))
 A = rand(3, 3)
 println(f(A))
 println(f("hi"))

 Hello 55595472, it's great to see you!
 [1.098291594301114 0.5733286686511377 1.2973169349506042; 1.5089880137440685 1.0767547143728742 1.7354001978884117; 0.6092825261800483 0.5512628958029917 0.958821963007823]
 "hihi" 

Mutating and Non-Mutating Functions

By convention, functions followed by a ! alter their arguments and functions without ! don’t. This is not some hard-set rule for all functions; it’s just a better and easier way of differentiating mutating functions.

 v = [3, 5, 2]
 sort(v)
 println(v)

 [3, 5, 2] 

sort(v) returns a sorted array, but v remains unchanged. On the other hand, sort!(v) modifies the contents of v, which are now sorted within the array v

 sort!(v)
 println(v)

 [2, 3, 5] 

Higher-Order Functions

Higher-order functions are functions that take another function as an argument. Let’s go through two of the higher-order functions offered by Julia: map and broadcast.

map() applies the function to each element of the data structure passed to it.

 println(map(x -> x^3, [1, 2, 3]))

 [1, 8, 27] 

broadcast() is a more generalized version of map(); it can do everything map() can do and more. Both functions apply a function across a data structure. The difference between the two is how they treat multidimensional arrays. 

 println(map(+, 9, [2,2,2]))
 println(broadcast(+, 9, [2,2,2]))

 [11]
 [11, 11, 11] 

map() combines the columns first which are then used as arguments to the given function; on the other hand, broadcast() does an element-wise application of the given function. Furthermore, broadcast() also has a more concise syntax using the . operator that allows us to write complex elementwise expressions more naturally.  

 A = [i + 3*j for j in 0:2, i in 1:3]
 println(A .+ 2 .* f.(A) ./ A)
 # instead of
 println(broadcast(x -> x + 2 * f(x) / x, A))


 [3.0 6.0 9.0; 12.0 15.0 18.0; 21.0 24.0 27.0]
 [3.0 6.0 9.0; 12.0 15.0 18.0; 21.0 24.0 27.0] 

You can read more about functions in Julia here.

Working with Packages in Julia

Julia has over 2000 packages, which sounds like a lot but compared to Python’s whopping 1,37,000, the package ecosystem still has some growing to do. That being said Julia has first-class function calls to other languages; these provide foreign function interfaces. You can easily call into Python or R, for example, with PyCall or Rcall.  This enables developers to use their trusted favourite package/library from other languages. To see all available packages, check out:

To use a package in a Julia installation, you’ll need to use the package manager, Pkg, to add it explicitly. You have already installed the package “IJulia” at the beginning of the article. Now let’s briefly see one of the best Julia packages I have encountered in my limited experience: the Plots.jl package that enables developers to switch between backends while using the same API seamlessly.

We’ll illustrate this by using GR and Plotly backends, but first, let’s add the package and “import” it using the “using” command.  

 using Pkg
 Pkg.add("Plots")
 using Plots 

and create some dummy data to plot.

 price = [14.4, 14.5, 14.8, 15.2, 15.5, 15.8]
 quantity = [45000, 20000, 15000, 5000, 400, 17]
 Pkg.add("GR")
 gr() #switching backend
 plot(quantity, price, label="line")  
 scatter!(quantity, price, label="points")  

Notice how we use scatter!() and not scatter(); if we were to use the non-mutating variant, it would create a new plot with only the scatter plot.

 Pkg.add("PlotlyJS")
 plotlyjs() #switching backend 

You can get the Jupyter notebook with the above code here. I couldn’t get Julia to work on Colab; let me know if you have any luck.

Last Epoch

This article gave a brief overview of the basics syntax and features of Julia. Among the ones it covered, some things are extremely useful and intuitive. Like the use of ! symbol to indicate mutating functions or the compact . symbol syntax of the broadcast() method. On the other hand, there are also some icky choices like the use of * for concatenating strings or the use of the cumbersome => in dictionaries. (This all comes from a very Python-biased perspective.) That being said, Julia does have a lot of promise in its ability to interact with other languages seamlessly and run even devectorized code as fast as C. Furthermore, it’s still early in its development process, it isn’t even a decade old. It will evolve over the next decade or so. As for us, over the next few weeks, we’ll cover more Julia Packages and use them to create various machine learning and deep learning models.

Access all our open Survey & Awards Nomination forms in one place >>

Picture of Aditya Singh

Aditya Singh

A machine learning enthusiast with a knack for finding patterns. In my free time, I like to delve into the world of non-fiction books and video essays.

Download our Mobile App

CORPORATE TRAINING PROGRAMS ON GENERATIVE AI

Generative AI Skilling for Enterprises

Our customized corporate training program on Generative AI provides a unique opportunity to empower, retain, and advance your talent.

3 Ways to Join our Community

Telegram group

Discover special offers, top stories, upcoming events, and more.

Discord Server

Stay Connected with a larger ecosystem of data science and ML Professionals

Subscribe to our Daily newsletter

Get our daily awesome stories & videos in your inbox
Recent Stories