Statistical Programming in R

The blueprint of R

Layers in R

There are several ‘layers’ in R. Some layers you are allowed to fiddle around in, some are forbidden. In general there is the following distinction:

  • The global environment.
  • User environments
  • Functions
  • Packages
  • Namespaces

Environments

The global environment can be seen as a olympic-size swimming pool. Everything you do has its place there.

If you’d like, you may create another, seperate environment to work in.

  • A user environment would by default not have access to other environments

Functions

  • If you create a function, it is positioned in the global environment.

  • Everything that happens in a function, stays in a function. Unless you specifically tell the function to share the information with the global environment.

  • See functions as a shampoo bottle in a swimming pool to which you add some water. If you’d like to see the color of the mixture, you’d have to squeeze the bottle for it to come out.

Packages

  • Packages have their own space.

    • Everything needed to run the functions in a package is needly contained within its own space
    • See packages as seperate (mini) pools that are connected to the main pool (the global environment)

Loading packages

There are two ways to load a package in R

library(stats)

and

require(stats)

require() will produce a warning when a package is not found. In other words, it will not stop as function library() does.

Installing packages

The easiest way to install e.g. package mice is to use

install.packages("mice")

Alternatively, you can also do it in RStudio through

Tools --> Install Packages

Namespaces

  • Namespaces. These are the deeper layers that feed new water to the surface of the mini pools.
    • Packages can have namespaces.
    • Functions within packages are executed within the package or namespace and have access to the global environment.
    • Objects in the global environment that match objects in the function’s namespace are ignored when running functions from packages!

R in depth

Workspaces and why you should sometimes save them

A workspace contains all changes you made to environments, functions and namespaces.

A saved workspace contains everything at the time of the state wherein it was saved.

You do not need to run all the previous code again if you would like to continue working at a later time.

  • You can save the workspace and continue exactly where you left.

Workspaces are compressed and require relatively little memory when stored. The compression is very efficient and beats reloading large datasets from raw text.

History and why it is useful

R by default saves (part of) the code history and RStudio expands this functionality greatly.

Most often it may be useful to look back at the code history for various reasons.

  • There are multiple ways to access the code history.

    1. Use arrow up in the console. This allows you to go back in time, one codeline by one. Extremely useful to go back to previous lines for minor alterations to the code.
    2. Use the history tab in the environment pane. The complete project history can be found here and the history can be searched. This is particularly convenient when you know what code you are looking for.

Working in projects in RStudio

  • Every project has its own history
  • Every research project has its own project
  • Every project can have its own folder, which also serves as a research archive
  • Every project can have its own version control system
  • R-studio projects can relate to Git (or other online) repositories

Modeling

Modeling in R

To model objects based on other objects, we use ~ (tilde)

- For example, to model body mass index (BMI) on weight, we would type
BMI ~ weight

Tilde is used to separate the left- and right-hand sides in a model formula.

For functions (or models), within models we use I() - For example, to model body mass index (BMI) on its deterministic function of weight and height, we would type

BMI ~ I(weight / height^2)

Example

Remember the boys data from package mice:

lm(bmi ~ wgt, data = boys)
## 
## Call:
## lm(formula = bmi ~ wgt, data = boys)
## 
## Coefficients:
## (Intercept)          wgt  
##     14.5401       0.0935

Example continued

Remember the boys data from package mice:

lm(bmi ~ I(wgt / (hgt / 100)^2), data = boys)
## 
## Call:
## lm(formula = bmi ~ I(wgt/(hgt/100)^2), data = boys)
## 
## Coefficients:
##        (Intercept)  I(wgt/(hgt/100)^2)  
##          -0.005553            1.000034

More efficient

It is ‘nicer’ to store the output from the function in an object. The convention for regression models is an object called fit.

fit <- lm(bmi ~ I(wgt / (hgt / 100)^2), data = boys)

The object fit contains a lot more than just the regression weights. To inspect what is inside you can use

ls(fit)
##  [1] "assign"        "call"          "coefficients"  "df.residual"  
##  [5] "effects"       "fitted.values" "model"         "na.action"    
##  [9] "qr"            "rank"          "residuals"     "terms"        
## [13] "xlevels"

Inspecting what is inside fit

Another approach to inspecting the contents of fit is the function attributes()

attributes(fit)
## $names
##  [1] "coefficients"  "residuals"     "effects"       "rank"         
##  [5] "fitted.values" "assign"        "qr"            "df.residual"  
##  [9] "na.action"     "xlevels"       "call"          "terms"        
## [13] "model"        
## 
## $class
## [1] "lm"

The benefit of using attributes() is that it directly tells you the class of the object.

Classes in R

class(fit)
## [1] "lm"

Classes are used for an object-oriented style of programming. This means that you can write a specific function that - has fixed requirements with respect to the input. - presents output or graphs in a predefined manner.

When a generic function fun is applied to an object with class attribute c("first", "second"), the system searches for a function called fun.first and, if it finds it, applies it to the object.

If no such function is found, a function called fun.second is tried. If no class name produces a suitable function, the function fun.default is used (if it exists). If there is no class attribute, the implicit class is tried, then the default method.

Classes example: plotting without class

plot(bmi ~ wgt, data = boys)

Classes example: plotting with class

plot(lm(bmi ~ wgt, data = boys), which = 1)

Classes example: plotting with class

plot(lm(bmi ~ wgt, data = boys), which = 2)

Classes example: plotting with class

plot(lm(bmi ~ wgt, data = boys), which = 3)

Classes example: plotting with class

plot(lm(bmi ~ wgt, data = boys), which = 4)

Classes example: plotting with class

plot(lm(bmi ~ wgt, data = boys), which = 5)

Classes example: plotting with class

plot(lm(bmi ~ wgt, data = boys), which = 6)

Why is plot different for class "lm"?

The function plot() is called, but not used. Instead, because the linear model has class "lm", R searches for the function plot.lm().

If function plot.lm() would not exist, R tries to apply function plot() (which would have failed in this case because plot requires x and y as input)

plot.lm() is created by John Maindonald and Martin Maechler. They thought it would be useful to have a standard plotting environment for objects with class "lm".

Since the elements that class "lm" returns are known, creating a generic function class is straightforward.

R-coding

The Google style guide

Naming conventions

File Names

File names should end in .R and, of course, be meaningful.

GOOD:

predict_ad_revenue.R

BAD:

foo.R

Identifiers

Don’t use underscores ( _ ) or hyphens ( - ) in identifiers. Identifiers should be named according to the following conventions.

  1. The preferred form for variable names is all lower case letters and words separated with dots (variable.name), but variableName is also accepted;
  2. function names have initial capital letters and no dots (FunctionName);
  3. constants are named like functions but with an initial k.

Identifiers (continued)

  • variable.name is preferred, variableName is accepted
    GOOD: avg.clicks
    OK: avgClicks
    BAD: avg_Clicks

  • FunctionName
    GOOD: CalculateAvgClicks
    BAD: calculate_avg_clicks , calculateAvgClicks
  • kConstantName

Syntax

Line Length

The maximum line length is 80 characters.

# This is to demonstrate that at about eighty characters you would move off of the page

# Also, if you have a very wide function
fit <- lm(age ~ bmi + hgt + wgt + hc + gen + phb + tv + reg + bmi * hgt + wgt * hgt + wgt * hgt * bmi, data = boys)

# it would be nice to pose it as
fit <- lm(age ~ bmi + hgt + wgt + hc + gen + phb + tv + reg + bmi * hgt 
          + bmi * wgt + wgt * hgt + wgt * hgt * bmi, data = boys)
#or
fit <- lm(age ~ bmi + hgt + wgt + hc + gen + phb + tv + reg 
          + bmi * hgt 
          + bmi * wgt
          + wgt * hgt 
          + wgt * hgt * bmi, 
          data = boys)

Indentation

When indenting your code, use two spaces. RStudio does this for you!

Never use tabs or mix tabs and spaces.

Exception: When a line break occurs inside parentheses, align the wrapped line with the first character inside the parenthesis.

Spacing

Place spaces around all binary operators (=, +, -, <-, etc.).

Exception: Spaces around =’s are optional when passing parameters in a function call.

lm(age ~ bmi, data=boys)

or

lm(age ~ bmi, data = boys)

Spacing (continued)

Do not place a space before a comma, but always place one after a comma.

GOOD:

tab.prior <- table(df[df$days.from.opt < 0, "campaign.id"])
total <- sum(x[, 1])
total <- sum(x[1, ])

Spacing (continued)

BAD:

# Needs spaces around '<'
tab.prior <- table(df[df$days.from.opt<0, "campaign.id"])  
# Needs a space after the comma
tab.prior <- table(df[df$days.from.opt < 0,"campaign.id"])  
# Needs a space before <-
tab.prior<- table(df[df$days.from.opt < 0, "campaign.id"]) 
# Needs spaces around <-
tab.prior<-table(df[df$days.from.opt < 0, "campaign.id"])  
# Needs a space after the comma
total <- sum(x[,1])  
# Needs a space after the comma, not before 
total <- sum(x[ ,1])  

Spacing (continued)

Place a space before left parenthesis, except in a function call.

GOOD:

if (debug)

BAD:

if(debug)

Extra spacing

Extra spacing (i.e., more than one space in a row) is okay if it improves alignment of equals signs or arrows (<-).

plot(x    = x.coord,
     y    = data.mat[, MakeColName(metric, ptiles[1], "roiOpt")],
     ylim = ylim,
     xlab = "dates",
     ylab = metric,
     main = (paste(metric, " for 3 samples ", sep = "")))

Do not place spaces around code in parentheses or square brackets.

Exception: Always place a space after a comma.

Extra spacing

GOOD:

if (debug)
x[1, ]

BAD:

if ( debug )  # No spaces around debug
x[1,]  # Needs a space after the comma 

In general…

  • Use common sense and BE CONSISTENT.

  • The point of having style guidelines is to have a common vocabulary of coding
    • so people can concentrate on what you are saying, rather than on how you are saying it.
  • If code you add to a file looks drastically different from the existing code around it, the discontinuity will throw readers out of their rhythm when they go to read it. Try to avoid this.