Chapter 27 Accessibility of variables

It is essential to always keep in mind where your variables are, and whether they are defined and accessible:

  1. Variables defined inside a function are not accessible outside from it!

  2. Variables defined outside a function are accessible inside, and are not modified, even if they have the same name.

out_val <- 3
vartest <- function() {
    in_val <- 4
    print(in_val)
    print(out_val)
}
vartest()
## [1] 4
## [1] 3
in_val
## Error in eval(expr, envir, enclos): object 'in_val' not found
out_val
## [1] 3
out_val_2
## Error in eval(expr, envir, enclos): object 'out_val_2' not found

What happens in the function club, stays in the function club.

Be very careful when creating variables inside a conditional statement as the variable may never have been created and cause (sometimes unperceptible) errors.

var1 <- 3
vartest <- function() {
    a <- 4  # 'a' is defined inside
    print(a)  # print 'a'
    print(var1)  # print var1
}
a  # we cannot print 'a' as it exists only inside the function
## [1] 19.96041
vartest()  # calling vartest() will print a and var1
## [1] 4
## [1] 3
rm(var1)  # remove var1
vartest()  # calling the function again does not work anymore
## [1] 4
## Error in vartest(): object 'var1' not found

Tip It is good practice to define variables outside the conditions and then modify their values to avoid any problems.

a <- 3
if (a > 5) {
    b <- 2
}
a + b

If you had b already assigned in your environment, with a different value, you could have had a bigger problem!

# Error: object 'b' not found

27.1 Why should I care about programming practices?


  • It makes your life easier;
  • It helps you achieve greater readability and makes sharing and reusing your code a lot less painful;
  • It helps reduce the time you will spend remembering and understanding your own code.

Keep a clean and nice code

Proper indentation and spacing is the first step to get an easy to read

code:

  • Use spaces between and after you operators

x>=1&x<=10 is more difficult to read then x >= 1 & x <= 10

  • Use consistently the same assignation operator.

<- is often preferred. = is OK, but do not switch all the time between the two

  • Use brackets when using flow control statements:

    • Inside brackets, indent by at least two spaces;
    • Put closing brackets on a separate line, except when preceding an else statement.
  • Define each variable on its own line.

  • Use Cmd + I or Ctrl + I in RStudio to indent the highlighted code automatically;

if ((a[x, y] > 1) & (a[x, y] < 2)) {
    print("Between 1 and 2")
}
if ((a[x, y] > 1) & (a[x, y] < 2)) {
    print("Between 1 and 2")
}

This code is not spaced, and therefore hard to read. All brackets are badly aligned, and it looks “messy”.

a <- 4
b = 3
if (a < b) {
    if (a == 0)
        print("a zero")
} else {
    if (b == 0) {
        print("b zero")
    } else print(b)
}

On the left, code is not spaced, nor indented. All brackets are in the same line, and it looks “messy”. On the right, it looks more organized, no?

a <- 4
b <- 3
if (a < b) {
    if (a == 0) {
        print("a zero")
    }
} else {
    if (b == 0) {
        print("b zero")
    } else {
        print(b)
    }
}

27.2 Use functions to simplify your code

Write your own function: 1. When portion of the code is repeated more than a few times in your script; 2. If only a part of the code changes and includes options for different arguments.

This would also reduce the number of potential errors done by copy-pasting, and the time needed to correct them.

Let’s modify the example from Challenge 3 and suppose that all \(CO_2\) uptake from Mississipi plants was overestimated by 20 and Quebec underestimated by 50.

We could write this:

for (i in 1:length(CO2[, 1])) {
    if (CO2$Type[i] == "Mississippi") {
        CO2$conc[i] <- CO2$conc[i] - 20
    }
}
for (i in 1:length(CO2[, 1])) {
    if (CO2$Type[i] == "Quebec") {
        CO2$conc[i] <- CO2$conc[i] + 50
    }
}

Or this:

recalibrate <- function(CO2, type, bias) {
    for (i in 1:nrow(CO2)) {
        if (CO2$Type[i] == type) {
            CO2$conc[i] <- CO2$conc[i] + bias
        }
    }
    return(CO2)
}
newCO2 <- recalibrate(CO2 = CO2, type = "Mississipi", bias = -20)
newCO2 <- recalibrate(newCO2, "Quebec", +50)

27.3 Use meaningful names for functions

Same function as before, but with vague names:

rc <- function(c, t, b) {
    for (i in 1:nrow(c)) {
        if (c$Type[i] == t) {
            c$uptake[i] <- c$uptake[i] + b
        }
    }
    return(c)
}

Whenever possible, avoid using names of existing R functions and variables to avoid confusion and conflits.

27.3.1 Use comments


What is c and rc?

That being said:




Whenever possible, avoid using names of existing R functions and variables to avoid confusion and conflits.

27.4 Use comments: #

.alert[Final tip]. Add comments to describe what your code does, how to use its arguments or a detailed step-by-step description of the function.

# Recalibrates the CO2 dataset by modifying the CO2 uptake
# concentration by a fixed amount depending on the region
# of sampling.  Arguments CO2: the CO2 dataset type: the
# type ('Mississippi' or 'Quebec') that need to be
# recalibrated bias: the amount to add or remove to the
# concentration uptake
recalibrate <- function(CO2, type, bias) {
    for (i in 1:nrow(CO2)) {
        if (CO2$Type[i] == type) {
            CO2$uptake[i] <- CO2$uptake[i] + bias
        }
    }
    return(CO2)
}

Challenge 6: Group exercise

Using what you learned, write an if() statement that tests whether a numeric variable x is 0. If not, it assigns \(cos(x)/x\) to z, otherwise it assigns \(1\) to z.

Create a function called my_function() that takes the variable x as argument and returns z.

If we assign \(45\), \(20\), and \(0\) to x respectively, which of the following options would represent the results?


1. \(0.054\), \(0.012\), and \(0\);

2. \(0.020\), \(0.054\), and \(1\);

3. \(0.012\), \(0.020\), and \(1\).


In addition to this, discuss with your group about a function that you would like to create (it can or it may not be related to your research). Be prepared to briefly describe it to us!

???

This exercise should take place in breakout rooms within 10 minutes. After rejoining the main room, a poll should be opened to participants. Once you obtain the response from participants, show them the correct answer and code. You may request that one of the participants explain their answer before showing the results.

27.5 Group exercise: Solution

Correct answer is option 3 ( \(0.12\), \(0.20\), and \(1\) ).


my_function <- function(x) {
    if (x != 0) {
        z <- cos(x)/x
    } else {
        z <- 1
    }
    return(z)
}
my_function(45)
## [1] 0.01167382
my_function(20)
## [1] 0.0204041
my_function(0)
## [1] 1