Julia Exercises

Author

Haden Bunn

1 Julia Exercises

This document contains exercises that are intended to reinforce the concepts presented in Module 1. Each exercise should take <5 minutes to complete and there may be more than one way to approach each problem.

1.1 Exercise 1

  • Create a variable x and assign it a value of 1.5 as a String.
# Answer here
  • Convert the value of x to a numeric type that will preserve the decimal value, then assign the result to a variable y.
# Answer here
  • Create another variable z and assign it the numeric value 1.5.
# Answer here
  • Is the value of y equal to z?
# Answer here
  • Is the value of y identical to z?
# Answer here

1.2 Exercise 2

  • How many operations are included in the logical expression: false || true && true && false || true.
# Answer here
  • Predict the return value of the expression if it were entered into the REPL.
# Answer here
  • Write out the individual operations in the order they would be evaluated.
# Answer here
  • Modify the original expression to obtain the opposite result (e.g., false -> true) by adding ≤3 characters.
# Answer here

1.3 Exercise 3

  • What are the 4 basic (primitive) data types in Julia?
# Answer here
  • Write two expressions that confirm 1.0 is both a Number and Float64 by returning a boolean value of true.
# Answer here
  • Explain why 1.0 is both a Number and a Float64.
# Answer here
  • Explain the difference between Missing and missing.
# Answer here
  • Write an expression that evaluates whether nothing and missing are equivalent.
# Answer here

1.4 Exercise 4

  • The expression below creates a string of 1000 characters randomly sampled with replacement from the range “A-Z”.
s = join(rand('A':'Z', 1000))
  • Find the index of the first 'A' in s. If the search returns nothing, choose a different character until the function returns a valid index. If changed, be sure to use the new character instead of 'A' and 'a' (e.g., 'B', 'b') for the remaining prompts in this exercise.
# Answer here
  • Modify the search so that it is case-insensitive, then test it using a lowercase 'a'.
# Answer here
  • Find the index for the last 'A' in s.
# Answer here
  • Write an expression that returns the characters in s that occur between the first and last indices identified above.
# Answer here
  • Write an expression that checks whether s contains a string "AA".
# Answer here

1.5 Exercise 5

  • Which of Julia’s four basic data structures (Tuple, NamedTuple, Dictionary, and Array) are mutable? Which are indexable?
# Answer here
  • Create a Dictionary, d1 with Integer keys 1, 2, 3, corresponding to values: "my string", 99, and a tuple ("x", "y", "z").
# Answer here
  • Unpack the second and third elements of the tuple in d1 into two variables, a and b, then use those variables to create a NamedTuple, ntp.
# Answer here
  • Change the value of ntp.b to 5; make sure the update is reflected in ntp.
# Answer here
  • Create another Dictionary, d2, from ntp where each key is of type Symbol and each value is of type Any.
# Answer here
  • Write an expression that checks if key c exists in d2 where c::Symbol; the expression should return a boolean value.
# Answer here
  • Add a key, c, to d2, assign it a tuple containing a single value, 1, then verify the type of value stored in c using the appropriate function.
# Answer here

1.6 Exercise 6

  • Create a 3x3 matrix, X with values ranging from 100 to 900.
# Answer here
  • There are at least 4 methods of indexing into X that will return a scalar value of 100; list 4 of them below.
# Answer here
  • Retrieve all values in the first row of X as both a column vector and a row vector.
# Answer here
  • Replace the values in the second row of X with the values 2, 5, 8 (if you get an error here, read it carefully and apply the suggested fix).
# Answer here
  • Modify the elements of X so that the expression below evaluates true.
# Answer here
zeros(Int, 3, 3) == X # check after modifying X
  • Consider the 4x2 matrix below, recreate this matrix by vertically concatenating 4 separate arrays.
# 5  10
# 15 20
# 25 30
# 35 40

# Answer here
  • Recreate the same 4x2 matrix above by horizontally concatenating 2 Ranges.
# Answer here

1.7 Exercise 7

  • Create a variable, x and assign it a value missing. Write a simple if statement that will print the value of x to the REPL if it is missing.
# Answer here
  • Rewrite the simple if statement above using a short-circuiting logical operator.
# Answer here
  • Consider the if statement below. Rewrite the expression using the ternary operator (<cond> ? <true statement> : <false statement>).
#=
if a < 5
    println("$a < 5")
else
    println("$a ≥ 10")
end
=#

a = 10
# Answer here
  • The if-else statement above was changed to an if-elseif statement. Rewrite the expression using the ternary operator.
#=
if a < 5
    println("$a < 5")
elseif a < 10
    println("$a < 10")
else
    println("$a ≥ 10")
end
=#

a = 10
# Answer here

1.8 Exercise 8

  • Create an empty vector, v, of Int64 values and a counter, i = 0. Using a while loop, increase the value of i by 2 until i > 20 and store the result of each iteration in v.
# Answer here
  • Create an empty Dictionary, d, then use a for loop to populate it with the keys and values of the NamedTuple, ntp, below.
# Answer here
ntp = (; a = 100, b = "mystring", c = false)
  • Consider the comprehension below. In a few words, explain what is happening inside the comprehension.
A = [x^2 + 1 for x = 1:20]
# Answer here
  • Create an uninitialized 4x5 Array, B, of Int64 values, then use a nested for loop to fill B, row-wise, with the elements of A.
# Answer here
  • Repeat the step above, but this time, fill B column-wise with the elements of A.
# Answer here
  • Create a third 4x5 Array, C, that is type Int64 and contains only zeros. Use a single (non-nested) for loop to fill C, column-wise with the elements of A.
# Answer here
  • Perform an element-wise comparison of the values in B and C to check for equality; how would you interpret the resulting BitMatrix?
# Answer here

1.9 Exercise 9

Creatinine Clearance

The Cockcroft-Gault formula provides an estimate of glomerular filtration rate (GFR) by calculating serum creatinine clearance (CrCL). While imperfect, CrCL is the standard metric used to guide dose adjustments for renal impairment.

The goal of this exercise is to implement the CG formula as a function and apply it across a range of values. A commonly used version of the formula is given below.

CrCL (mL/min) = (140-age)/scr * tbw/72 * 0.85^isfemale

Where:

  • age (years)
  • scr (serum creatinine, mg/dL)
  • tbw (total body weight, kg)
  • isfemale (female sex, true/false)
  • Create a function, crcl1, using inline assignment and positional arguments.
# Answer here
  • Test crcl1 for a 41 y/o male patient weighing 95.6 kg with a SCr of 1.2 mg/dL
# Answer here
  • Test crcl1 for the same patient, but pass in, "41", for age. Read the error message carefully; if you did not know the cause up front, could you interpret the message and identify the problem?
# Answer here
  • Test crcl1 for the same patient, but pass in missing for isfemale. Did you expect an error message, or a missing return value? Why?
# Answer here
  • Create a function, crcl2, using the function keyword and kwargs. Set the default value for isfemale to false. Before calculating CrCL, the function should check whether the value of each continuous variable is a Number; if not, it should return a missing value. (hint: there are several ways to write the check; consider reviewing, ?isa and ?all)
# Answer here
  • Test crcl2 for the same patient, making sure to test the “normal” case, then a case where age, scr, or tbw is not a Number.
# Answer here
  • Create a function crcl3 using kwargs and type assertion for each argument. Set the default value for isfemale to false.
# Answer here
  • Test crcl3 for the same patient using both a “normal” and “error” case.
# Answer here
Error-handling

Note: It is good practice to include basic error-handling in your code, but the way you define “basic” will evolve as you become more comfortable with Julia. Simply returning a missing value for common errors as in crcl2, is a valid approach, especially if you’re working alone and your code is well-documented. In larger projects, type assertion (e.g., crcl3) and more advanced error-handling workflows (e.g., try-catch, throw, @assert) become more important. When starting out, you can worry less about error handling since the code you write will naturally become more elegant (and less error-prone) over time.

  • Use a comprehension and crcl3 to calculate CrCL across a range of scr values (0.5:0.05:1.25) for a 30 y/o female weighing 145 lbs.
# Answer here
  • Explore the code below, then try to articulate what is happening with each component. If you encounter a function or symbol that is unfamiliar, check the help menu, ?. The goal here is to provide a few examples for generating and manipulating data based up on the techniques outlined so far.
# load package needed for Normal, Bernoulli distributions
using Distributions

# a data structure for storing "patients"
patients = Dict()

# a loop to create 10 patients with randomly sampled characteristics 
for i = 1:10
    # random input
    age = rand(19:92)
    scr = rand(Normal(0.9, 0.2))
    tbw = rand(Normal(72, 20))
    isfemale = rand(Bernoulli())

    # save the input in a data structure for later
    input = (; age, scr, tbw, isfemale)

    # generate output
    output = crcl3(; input...)

    # save both as a entry in patients with a corresponding key
    patients[i] = (; input, output)
end

patients[1]     # key, *not* index

_in = [v.input for v in values(patients)]

_weights = [i.tbw for i in _in]

mean((i.tbw for i in _in))

1.10 Exercise 10

  • Consider the attempt at defining two methods for a function, m, below. Execute the code in the begin block and then programmatically check the number of methods associated with m.
begin
    m(; x::Real) = x + 3
    m(; x::AbstractString) = parse(Float64, x) + 3
end
# Answer here
  • What happens if you execute the call below? Why?
m(x = 3)
  • Redefine m to create the two intended methods above. How many total methods will m have? Why? What happens if you try to set m = nothing? Why?
# Answer here
  • Define a vector, x = [1,2,3]. Then, define a function that adds 3 to each element of x and returns the sum of its elements. The function should modify x in-place and follow the style convention for mutating functions.
# Answer here
  • Define a second version of your function above that performs the same operations without modifying the input outside of the function.
# Answer here