In [1]:
# this is a comment i Julia
# in Julia everything is an Expression

In [2]:
2 + 3


Out[2]:
5

In [3]:
# variables don't have to be explicitely typed
a = 2
typeof(a)


Out[3]:
Int64

In [4]:
# Julia supports ASCII strings
str = "Harris"


Out[4]:
"Harris"

In [5]:
typeof(str)


Out[5]:
ASCIIString

In [6]:
# and UTF8 / UTF16 strings
str2 = "öäüß"


Out[6]:
"öäüß"

In [7]:
typeof(str2)


Out[7]:
UTF8String

In [8]:
# both string types are immutable 
# and their individual characters should not be accessed by index
# better use the iterator
for chr in str2
    println(chr)
end


ö
ä
ü
ß

In [9]:
typeof(str2)


Out[9]:
UTF8String

In [10]:
# This is a 'tuple'
a_tuple = (1,"VALUE")


Out[10]:
(1,"VALUE")

In [11]:
typeof(a_tuple)


Out[11]:
Tuple{Int64,ASCIIString}

In [12]:
# arrays are easy to create
array = [0,1,2,3,4]


Out[12]:
5-element Array{Int64,1}:
 0
 1
 2
 3
 4

In [13]:
# and their index starts with 1!
array[1]


Out[13]:
0

In [14]:
# Julia can also generate arrays by following certain conditions
# Here we define a range from 1 to 20 with step 2 (this is an expression, too)
generated_array = 1:2:20


Out[14]:
10-element StepRange{Int64,Int64}:
 1,3,5,7,9,11,13,15,17,19

In [15]:
generated_array[3]


Out[15]:
5

In [16]:
# this is another way to create arrays by using 'comprehensions'
another_array = [arr for arr in 1:10]


Out[16]:
10-element Array{Int64,1}:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10

In [17]:
# Crreating matrices is very easy and can be done by using semicolons as separators between rows
matrix = [1 2 3 4; 
          5 6 7 8; 
          9 10 11 12]


Out[17]:
3x4 Array{Int64,2}:
 1   2   3   4
 5   6   7   8
 9  10  11  12

In [18]:
# Of course, Julia supports Conditionals like if..then..else
if 2 > 3
    println("WAT?")
elseif 2 < 2
    println("WOOT?")
else
    println("Hello, world!")
end


Hello, world!

In [19]:
# ternary operator is also supported
a = 2 > 3 ? true : false


Out[19]:
false

In [20]:
# Julia's for-loop is very flexible and powerful
for x in 1:10
    println("Value is $x") # here we interpolate the string value
end


Value is 1
Value is 2
Value is 3
Value is 4
Value is 5
Value is 6
Value is 7
Value is 8
Value is 9
Value is 10

In [21]:
# and while loops too
a = 1
b = 10
    
while a < b
  a += 1
  println("$a: looping over")
end


2: looping over
3: looping over
4: looping over
5: looping over
6: looping over
7: looping over
8: looping over
9: looping over
10: looping over

In [22]:
# Julia's structures are very easy to access.
# Here we use 'enumerate' to return not only the current value
# but also the index itself
# The returned value we call a 'tuple' and we show it via @show macro
for (i, v) in enumerate(1:10)
    @show (i, v)
end


(i,v) = (1,1)
(i,v) = (2,2)
(i,v) = (3,3)
(i,v) = (4,4)
(i,v) = (5,5)
(i,v) = (6,6)
(i,v) = (7,7)
(i,v) = (8,8)
(i,v) = (9,9)
(i,v) = (10,10)

In [ ]:
# To access the Julia documentation just prepend a question mark before the term your're searching for

In [23]:
?println


search: 
Out[23]:
..  println(x)

Print (using :func:`print`) ``x`` followed by a newline.
println print_with_color print print_joined print_escaped


In [24]:
# functions are first-class objects
# Values are passed-by-reference
function doubleIt(num)
    num * 2
end


Out[24]:
doubleIt (generic function with 1 method)

In [25]:
doubleIt(5)


Out[25]:
10

In [26]:
# you don't have to use the return statement 
# because Julia automatically returns the result of the last expression in a function
# Remember: in Julia everything is an expression 
function doubleItWithRet(num)
    return num * 2
end


Out[26]:
doubleItWithRet (generic function with 1 method)

In [27]:
doubleItWithRet(5)


Out[27]:
10

In [28]:
# a function can also be written this way
# just like a variable definition
# but this applies only to short functions containing one expression
doubleIt(num) = num * 2


Out[28]:
doubleIt (generic function with 1 method)

In [29]:
# you can be more specific and provide the parameter type information to Julia compiler
# Julia can deduce the type but it's often better to give this information in advance to 
# help the compiler generate more efficient code
# Julia uses LLVM to generate native code
doubleIt(num::Float64) = num * 2


Out[29]:
doubleIt (generic function with 2 methods)

In [30]:
# now our "doubleIt" function can consume Int64 and Float64 values (which, of course, are not of the same type)
doubleIt(45.5)


Out[30]:
91.0

In [31]:
# Functions can be returned
function returnAFun()
    aValue = 0
    return ((a) -> a += a) # we return a function that expects a single argument
end


Out[31]:
returnAFun (generic function with 1 method)

In [32]:
returned_func = returnAFun()


Out[32]:
(anonymous function)

In [33]:
value = returned_func(10)
println("Value is $value")


Value is 20

In [34]:
# Julia supports higher-order functions
function consumeAFun(func)
    func(5)
end


Out[34]:
consumeAFun (generic function with 1 method)

In [35]:
# here we define a function (it's quite similar to 'lambdas' in Python, for example) 
toBeConsumed_ = (a) -> a * a


Out[35]:
(anonymous function)

In [36]:
consumeAFun(toBeConsumed_)


Out[36]:
25

In [37]:
# Functions can be anonymous...
# ...and they also can accept arbitrary amounts of arguments
# Just use the "splat" operator ... as a suffix
anonymous_func = function(manyargs...)
    println("Argument Type is: " * string(typeof(manyargs))) # here we use 'string' to convert returned value to a string
    for arg in manyargs
        println("Argument: $arg")
    end
end


Out[37]:
(anonymous function)

In [38]:
anonymous_func(1,2,3,4,5,6)


Argument Type is: Tuple{Int64,Int64,Int64,Int64,Int64,Int64}
Argument: 1
Argument: 2
Argument: 3
Argument: 4
Argument: 5
Argument: 6

In [39]:
# Julia uses "mutiple dispatch" to call the most precise version of a function
# By using methods we can explore the available function definitions.
methods(doubleIt)


Out[39]:
2 methods for generic function doubleIt:
  • doubleIt(num::Float64) at In[29]:5
  • doubleIt(num) at In[28]:4

In [ ]:
# In other languages, especially the object-oriented ones, 
# a 'method' is a functions belonging to a certain instance of a class
# In Julia a 'method' belongs to a function
# Methods in Julia are different 'versions' of the same function 
# which have different signatures

In [40]:
# Julia has no excessive class support like Java, C#, C++ etc.
# Instead it provides a simple type hierarchy without deep nesting

#Here we define an abstract class 
# Unlike in other OO-languages abstract classes don't have fields 
# and concrete types can't further be subtyped

abstract Device

# We subclass from Device by creating a new concrete type 'Computer'
type Computer <: Device
    name::AbstractString
    price::Float64
    peripherals::Array{AbstractString}
end

# To create an immutable type we simply replace 'type' with immutable
immutable iPad <: Device
    name::AbstractString
    price::Float64
end

# We can set some defaults for the type constructor, too
immutable iPhone <: Device
    name::AbstractString
    price::Float64
    iPhone(name="iPhone 6",price=700.00) = new(name,price)
end

In [41]:
iphone = iPhone()


Out[41]:
iPhone("iPhone 6",700.0)

In [42]:
iphone.name


Out[42]:
"iPhone 6"

In [43]:
# because we declared iPhone type to be immutable no changes are allowed
iphone.name = "iPhone 5"


LoadError: type iPhone is immutable
while loading In[43], in expression starting on line 2

 [inlined code] from essentials.jl:114

In [44]:
computer = Computer("DELL",1000.00,["mice","printer"])


Out[44]:
Computer("DELL",1000.0,AbstractString["mice","printer"])

In [45]:
# The type 'Computer' is not immutable so changes are allowed
computer.name = "Lenovo"


Out[45]:
"Lenovo"

In [46]:
# we can easily inspect the generated LLVM code
# the commands with a preceding @ are called 'macros'
@code_llvm doubleIt(45.5)


define double @julia_doubleIt_2202(double) {
top:
  %1 = fmul double %0, 2.000000e+00
  ret double %1
}

In [47]:
# or even the final, machine code
@code_native doubleIt(45.5)


	.text
Filename: In[29]
Source line: 5
	pushq	%rbp
	movq	%rsp, %rbp
Source line: 5
	addsd	%xmm0, %xmm0
	popq	%rbp
	ret

In [48]:
# if you want to know how long it takes to execute a piece of code just prepend the @time macro
@time doubleIt(10)


  0.003504 seconds (456
Out[48]:
20
 allocations: 56.759 KB)

In [49]:
# We can create our own macros, too.
# We define a macro with 'macro' keyword and
# declare the code block which will be expanded later
# by using 'quote'-'end' markers
# Don't forget to prepend $ before the inserted code (variable, expression etc.)
# Without $ Julia wouldn't expand the given code

macro injectIt(func)
    quote
        $func
    end
end

In [50]:
function addition(a::Int64,b::Int64)
    a + b
end

@injectIt addition(2,3)


Out[50]:
5

In [51]:
# Julia is a 'homoiconic' language
# this means that it treats its code and data the same way
# With Julia one can access the AST (abstract syntax tree) directly
# This provides powerful instruments for metaprogramming (e.g. code that generates code)

# Here we define a function that will act as a "function generator".
# We iterate over a loop and generate a new piece of code that will be immediately 
# inserted into our running code. This means that during the execution of "funcgen"
# the same program will get a few more functions "injected". Therefore the
# 'return value' of 'funcgen' is the 'expansion' of our running program: it expands into
# new functions prefixed with 'mult_'. The amount of these new function depends on the 
# argument 'num' we provide to 'funcgen'
#
# The important part here is the macro @eval which reads the following code 

function funcgen(num::Int64)
    for count in 1:num
        println("Generating function no. $count")
        @eval function $(symbol(string("mult_",count)))(par::Int64)
                par * $count
             end
    end
end


Out[51]:
funcgen (generic function with 1 method)

In [52]:
# Here, our function generator creates 10 new mult_-functions. 
funcgen(10)


Generating function no. 1
Generating function no. 2
Generating function no. 3
Generating function no. 4
Generating function no. 5
Generating function no. 6
Generating function no. 7
Generating function no. 8
Generating function no. 9
Generating function no. 10

In [53]:
# we execute one of them to check if the newly generated code is OK
mult_2(5)


Out[53]:
10

In [54]:
# Julia's ecosystem contains many useful packages
# Here we load some of them
using DataFrames # A Julia implementation of DataFrames similar to R or Python
using RDatasets # this package is a port from R
using PyPlot    # this one is from Python


WARNING: using DataFrames.array in module Main conflicts with an existing identifier.
WARNING: Base.String is deprecated, use AbstractString instead.
  likely near C:\Users\Harris\.julia\v0.5\RDatasets\src\dataset.jl:1
WARNING: Base.String is deprecated, use AbstractString instead.
  likely near C:\Users\Harris\.julia\v0.5\RDatasets\src\dataset.jl:1
WARNING: Base.String is deprecated, use AbstractString instead.
  likely near C:\Users\Harris\.julia\v0.5\RDatasets\src\datasets.jl:1

In [ ]:
# to update local package list use:
# Pkg.update()
# to initialize a new local package repository use:
# Pkg.init() # this is automatically done when calling Pkg.update() for the first time
# to add a new package use:
# Pkg.add(PACKAGE_NAME_IN_DOUBLE_QUOTES)
# to build a new package from source use:
# Pkg.build(PACKAGE_NAME_IN_DOUBLE_QUOTES)

In [55]:
# This is how we load a dataset from RDatasets package
# RDatasets brings many predefined datasets
iris = dataset("datasets", "iris")


Out[55]:
SepalLengthSepalWidthPetalLengthPetalWidthSpecies
15.13.51.40.2setosa
24.93.01.40.2setosa
34.73.21.30.2setosa
44.63.11.50.2setosa
55.03.61.40.2setosa
65.43.91.70.4setosa
74.63.41.40.3setosa
85.03.41.50.2setosa
94.42.91.40.2setosa
104.93.11.50.1setosa
115.43.71.50.2setosa
124.83.41.60.2setosa
134.83.01.40.1setosa
144.33.01.10.1setosa
155.84.01.20.2setosa
165.74.41.50.4setosa
175.43.91.30.4setosa
185.13.51.40.3setosa
195.73.81.70.3setosa
205.13.81.50.3setosa
215.43.41.70.2setosa
225.13.71.50.4setosa
234.63.61.00.2setosa
245.13.31.70.5setosa
254.83.41.90.2setosa
265.03.01.60.2setosa
275.03.41.60.4setosa
285.23.51.50.2setosa
295.23.41.40.2setosa
304.73.21.60.2setosa
&vellip&vellip&vellip&vellip&vellip&vellip

In [56]:
# We get the complete list by calling 'datasets'
RDatasets.datasets()


Out[56]:
PackageDatasetTitleRowsColumns
1COUNTaffairsaffairs60118
2COUNTazdrg112azdrg11217984
3COUNTazproazpro35896
4COUNTbadhealthbadhealth11273
5COUNTfasttrakgfasttrakg159
6COUNTlbwlbw18910
7COUNTlbwgrplbwgrp67
8COUNTloomisloomis41011
9COUNTmdvismdvis222713
10COUNTmedparmedpar149510
11COUNTrwmrwm273264
12COUNTrwm5yrrwm5yr1960917
13COUNTshipsships407
14COUNTtitanictitanic13164
15COUNTtitanicgrptitanicgrp125
16EcdatAccidentShip Accidents405
17EcdatAirlineCost for U.S. Airlines906
18EcdatAirqAir Quality for Californian Metropolitan Areas306
19EcdatBenefitsUnemployement of Blue Collar Workers487718
20EcdatBidsBids Received By U.S. Firms12612
21EcdatBudgetFoodBudget Share of Food for Spanish Households239726
22EcdatBudgetItalyBudget Shares for Italian Households172911
23EcdatBudgetUKBudget Shares of British Households151910
24EcdatBwagesWages in Belgium14724
25EcdatCPSch3Earnings from the Current Population Survey111303
26EcdatCapmStock Market Data5165
27EcdatCarStated Preferences for Car Choice465470
28EcdatCaschoolThe California Test Score Data Set42017
29EcdatCatsupChoice of Brand for Catsup279814
30EcdatCigarCigarette Consumption13809
&vellip&vellip&vellip&vellip&vellip&vellip

In [57]:
# With RDatasets we can work almost like in R environment
RDatasets.head(iris)


Out[57]:
SepalLengthSepalWidthPetalLengthPetalWidthSpecies
15.13.51.40.2setosa
24.93.01.40.2setosa
34.73.21.30.2setosa
44.63.11.50.2setosa
55.03.61.40.2setosa
65.43.91.70.4setosa

In [58]:
# Here we group the species by their name 
# We use the 5-th row from the original dataset and count them by 'nrow'
by(iris, 5, nrow)


Out[58]:
Speciesx1
1setosa50
2versicolor50
3virginica50

In [59]:
# Here's another well-known dataset: titanic
titanic = dataset("datasets","titanic")


Out[59]:
ClassSexAgeSurvivedFreq
11stMaleChildNo0
22ndMaleChildNo0
33rdMaleChildNo35
4CrewMaleChildNo0
51stFemaleChildNo0
62ndFemaleChildNo0
73rdFemaleChildNo17
8CrewFemaleChildNo0
91stMaleAdultNo118
102ndMaleAdultNo154
113rdMaleAdultNo387
12CrewMaleAdultNo670
131stFemaleAdultNo4
142ndFemaleAdultNo13
153rdFemaleAdultNo89
16CrewFemaleAdultNo3
171stMaleChildYes5
182ndMaleChildYes11
193rdMaleChildYes13
20CrewMaleChildYes0
211stFemaleChildYes1
222ndFemaleChildYes13
233rdFemaleChildYes14
24CrewFemaleChildYes0
251stMaleAdultYes57
262ndMaleAdultYes14
273rdMaleAdultYes75
28CrewMaleAdultYes192
291stFemaleAdultYes140
302ndFemaleAdultYes80
&vellip&vellip&vellip&vellip&vellip&vellip

In [60]:
by(titanic,3,nrow)


Out[60]:
Agex1
1Adult16
2Child16

In [61]:
groupby(titanic,[1,2])


Out[61]:
DataFrames.GroupedDataFrame  8 groups with keys: [1,2]
First Group:
4x5 DataFrames.SubDataFrame{Array{Int64,1}}
| Row | Class | Sex      | Age     | Survived | Freq |
|-----|-------|----------|---------|----------|------|
| 1   | "1st" | "Female" | "Child" | "No"     | 0    |
| 2   | "1st" | "Female" | "Adult" | "No"     | 4    |
| 3   | "1st" | "Female" | "Child" | "Yes"    | 1    |
| 4   | "1st" | "Female" | "Adult" | "Yes"    | 140  |
⋮
Last Group:
4x5 DataFrames.SubDataFrame{Array{Int64,1}}
| Row | Class  | Sex    | Age     | Survived | Freq |
|-----|--------|--------|---------|----------|------|
| 1   | "Crew" | "Male" | "Child" | "No"     | 0    |
| 2   | "Crew" | "Male" | "Adult" | "No"     | 670  |
| 3   | "Crew" | "Male" | "Child" | "Yes"    | 0    |
| 4   | "Crew" | "Male" | "Adult" | "Yes"    | 192  |

In [62]:
# DataArrays are similar to standard arrays 
# Additionally they accept NA values
da = @data [1,2,3,4,5,6,7,NA,8,9,10]


Out[62]:
11-element DataArrays.DataArray{Int64,1}:
  1  
  2  
  3  
  4  
  5  
  6  
  7  
   NA
  8  
  9  
 10  

In [63]:
# DataFrames represent tabular structures and behave like their counterparts in R and Python
df = DataFrame(ID = 1:4, 
                FirstName = ["John","Mary","Christopher","Max"],
                LastName = ["Doe","Clarkson","Wishbone","Reynolds"], 
                EMail = ["john.doe@email.com","mary.clarkson@email.com","chris.wishbone@email.com","max.reynolds@email.com"])


Out[63]:
IDFirstNameLastNameEMail
11JohnDoejohn.doe@email.com
22MaryClarksonmary.clarkson@email.com
33ChristopherWishbonechris.wishbone@email.com
44MaxReynoldsmax.reynolds@email.com

In [64]:
# Like in R and Python we can easily get out some useful information about the data structure
df.columns


Out[64]:
4-element Array{Any,1}:
 [1,2,3,4]                                                                                                      
 ASCIIString["John","Mary","Christopher","Max"]                                                                 
 ASCIIString["Doe","Clarkson","Wishbone","Reynolds"]                                                            
 ASCIIString["john.doe@email.com","mary.clarkson@email.com","chris.wishbone@email.com","max.reynolds@email.com"]

In [65]:
describe(df)


ID
Min      1.0
1st Qu.  1.75
Median   2.5
Mean     2.5
3rd Qu.  3.25
Max      4.0
NAs      0
NA%      0.0%

FirstName
Length  4

In [66]:
# Just like with any other "standard" Julia type we can use 'show' to list the internals
show(df)


Type    ASCIIString
NAs     0
NA%     0.0%
Unique  4

LastName
Length  4
Type    ASCIIString
NAs     0
NA%     0.0%
Unique  4

EMail
Length  4
Type    ASCIIString
NAs     0
NA%     0.0%
Unique  4

4x4 DataFrames.DataFrame
| Row | ID | FirstName     | LastName   | EMail                      |
|-----|----|---------------|------------|----------------------------|
| 1   | 1  | "John"        | "Doe"      | "john.doe@email.com"       |
| 2   | 2  | "Mary"        | "Clarkson" | "mary.clarkson@email.com"  |
| 3   | 3  | "Christopher" | "Wishbone" | "chris.wishbone@email.com" |
| 4   | 4  | "Max"         | "Reynolds" | "max.reynolds@email.com"   |

In [67]:
# Gadfly is Julia's own implementation for handling graphics
using Gadfly

In [68]:
X = [1,7,23,5,0]
Y = [11,62,3,14,15]
Labels = ["one", "two", "three", "four", "five"]

Gadfly.plot(x=X, y=Y, label=Labels,Geom.point, Geom.label)


Out[68]:
x -30 -25 -20 -15 -10 -5 0 5 10 15 20 25 30 35 40 45 50 55 -25 -24 -23 -22 -21 -20 -19 -18 -17 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 -25 0 25 50 -26 -24 -22 -20 -18 -16 -14 -12 -10 -8 -6 -4 -2 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 one two three four five -100 -80 -60 -40 -20 0 20 40 60 80 100 120 140 160 180 -80 -75 -70 -65 -60 -55 -50 -45 -40 -35 -30 -25 -20 -15 -10 -5 0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100 105 110 115 120 125 130 135 140 145 150 155 160 -100 0 100 200 -80 -75 -70 -65 -60 -55 -50 -45 -40 -35 -30 -25 -20 -15 -10 -5 0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100 105 110 115 120 125 130 135 140 145 150 155 160 y

In [69]:
#PyPlot is a wrapper for Python's matplotlib

x = iris[1][1:100]
y = iris[3][1:100]
p = PyPlot.scatter(x,y, c=collect(x))
xlabel("Sepal Length")
ylabel("Petal Width")
title("Iris")
grid("on")



In [ ]: