1. Warm-up

Before starting, please ensure you have installed R and Rstudio.

# Where we are
getwd()
dir <- getwd()
dir

# Move to somewhere else
setwd(dir)

# Getting help
??paste
?paste
help("paste")
help.start()
example(paste) 

# Some examples of 'calculator' usage

# Arithmetics at your will
10 + 5
10 - 5
4 * 6
16 / 4
2 ^ 8 # also 2 ** 8

# Built-in functions
sqrt(1024)
log(4)
log10(100)
log2(256)
exp(2)
round(exp(2), digits =  1)

# Logical operators
2 == 2
3 > 2
5 >= 1
3 < 1
4 != 0

# Lets declare some trivial variables 
x <- 1                 # numeric
y <- 2
str1 <- "hello world"  # character
bool <- TRUE           # logical
print( str1 )
x; y; str1; bool

# which variables are stored in our workspace?
ls()

# Install packages

# from CRAN (https://cran.r-project.org/)
install.packages("package name")

# from Bioconductor (https://www.bioconductor.org)
if (!requireNamespace("BiocManager", quietly = TRUE)) {
   install.packages("BiocManager")
}
BiocManager::install()
 
BiocManager::install(c("package name"))

# Load packages
library("package name")

2. Vectors and operations

# Numeric vector (ordered collection of values of the same data type)
v1 <- c( 1, 2, 3, 4, 5 ) # c() stands for concatenation
c1 <- c( "a", "b", "c", "d" )
l1 <- c( TRUE, FALSE, TRUE, FALSE, FALSE )
names( v1 ) <- c("anna", "biel", "carles", "daniel", "eric" ) # You can name it!

# Other assignment methods
v2 = c( 1,2,3,4,5 )
c( 1,2,3,4,5 ) -> v3
assign( "v4", c(1,2,3,4,5) )

# There are no list assignments like in Python!
( v1, v2 ) <- c( 1, 2 ) # WRONG

# Operations are applied to each element of the vector
v2 <- v1 + 10
v3 <- v1 * 2
v4 <- v1 %% 2
l2 <- ! l1

# Also, you can directly operate with vectors
v5 <- v2 + v3
odds <- v1 * v4

# Subscripts or indexes start at 1
# Details: http://cran.r-project.org/doc/manuals/R-lang.html#Indexing
v1
v1[1]
v1[ 1:3 ]                 # index is generated with ":", creates a basic sequence of numbers
v1[ -2 ]                  # indexes can be defined by exclusion
v1[ -2:-4 ]               # exclusion can be mixed with ranges/sequences
v1[ -4:-2 ]               # result is included, excluded order doesn't matter
v1[ c(2,4,1) ]
v1[ -c(2,4,1) ]
v1[ l2 ]                  # you can also use "logical" indexes, directly
v1[ v4>=1 ]               # or with logical operators, i.e. conditions
which( v4 >= 1 )          # which is extremely useful!
v1[ which( v4 >= 1 ) ]    # now we index with numbers
v1[ c("carles", "eric") ] # or index with names

# There are special values
s1 <- c( v1, NA, NaN, Inf )
is.na( s1 ) # looking for missing values like 'Not Available'
is.nan( s1 ) # 'Not a Number' is the result of an operation like Inf/Inf, 0/0
s1[ !is.na(s1) & !is.infinite(s1) ] # remove missing values
s1[ is.na(s1) | is.infinite(s1) ] <- 0 # Replace them

# Some functions operate with all its elements
sum( v1 )
mean( v1 )
min( v1 )
max( v1 )
range( v1 )
summary( v1 ) # basic statistics
sd( v1 ) # standard deviation
var( v1 ) # variance
cor( v1,v3 ) # correlation
sample( 1:1000, size=5 )

# Obtain the length of a vector
length(v1) 

# Basic printing
print( v1 ) # in R we need to use parenthesis
cat( v1, v2, v3, v4 ) # 'cat' can be used with several objects
print( v1, v2, v3, v4 ) # WRONG! see the difference with 'cat'

# sprintf is also available
sprintf( "My numbers are: %d and %d\n", x, y )

3. Factors, matrices, data frames, lists

# factor: a vector used to group components or store categorical data
#  - mostly used for statistical modelling

seq1 <- c( "ATG", "ACG", "GCC", "ACG", "GCC" )
as.factor( c( 1,1,1,2,3,2,2,1,1,3,1,2,2,1 ) )
as.factor( seq1 )
levels( as.factor( seq1 ) )

#* matrix: table of n rows and p columns (remember, always first rows, then columns)
#  - all the elements of a matrix are of the same type

matrix( c(1,2,3,4,5,6,7,8), nrow=2, ncol=4 )
matrix( c(1,2,3,4,5,6,7,8), nrow=4, ncol=2 )
m1 <- matrix( c(1,2,3,4,5,6,7,8,9), nrow=3, ncol=3 )

print(m1)

# We can use again the same subscripts, but in 2D
m1[ 1, 1]
m1[ 2, 2]
m1[ 3, 3]
m1[,1]
m1[2,]
m1[1:2,]
m1[c(1,3),c(1,2)]

m1 > 4
which(m1 > 4) # Careful, indexes vector ( 1D ) returned

# In general, it is better to use which() only in one dimension, rows or columns
# Let'sdo it step by step. From inside to outside.
#
# Fix a search in one dimension (a given column or row) and select the other
# i.e. search one dimension (e.g row), get the other (e.g. cols), and retrieve results
m1[ , which( m1[3,] > 5 ) ]      # given row #3 > 5, fix cols, and get results
m1[ which( m1[,2] >= 5 ), ]      # give me all rows where col#2 is >= 5
m1[ which( m1[,1]>=2 ), ][,2:3]  # you can subset again the result: e.g. give me col#2,3, where col#1 >= 2

m1b <- m1[ which( m1[,2] >= 5 ), ] # same as above, store result in the 'm1b' matrix
m1c <- m1[ which( m1[,1]>=2 ), ][,2:3] # same as above, store resutl in  in the 'm1c' matrix

dim( m1b ) 
dim( m1c )

#* data.frame: table of n rows and p columns, different types per column allowed
# - Subscripts are used like in matrices. Check out also ?subset
# - Rows and columns have names
# - Usually character vectors are coerced to factors to save memory
# - After vectors, this is the most interesting data structure

students <- c( "anna", "biel", "carles", "daniel" )
IDs <- c( 123456, 234567, 345678, 456789 )
score <- c( 8.5, 7.0, 5.0, 8.75 )

classroom <- data.frame( IDs=IDs, Scores=score )
rownames( classroom ) <- students
rownames( classroom )
colnames( classroom )

classroom$IDs # each column can be accessed with its name

as.data.frame( m1 )    # coercion is allowed from matrix to data.frame
as.matrix( classroom ) # coercion from data.frame to matrix allowed only with same types, here numeric

#* lists: a vector of mode list. Content can be of any type.
# - Each element can also have a name

ll1 <- list( 1:5, letters[1:5] )
names( ll1 ) <- c( "numbers", "letters" )
ll1
ll1$numbers
ll1[[ 1 ]]

# Before. Some notes on data types or 'modes'
# http://cran.r-project.org/doc/manuals/R-intro.html#The-intrinsic-attributes-mode-and-length
#
# 'mode' is a mutually exclusive classification of objects according to their basic structure.
# 'class' is a property assigned to an object that determines how generic functions operate with it.

class( v1 )   # find the object class of v1
class( m1 )

mode( v1 )    # and similarly
mode( m1 )

as.character( v1 ) # coercion or change of data type
as.numeric(l1)

# In practice you will only use 'mode' or 'class' to inspect the types you get
# from functions and expressions and understand what's going on (i.e. debug)

4. More on data manipulation and input/output

seq( 1, 5, 0.5 )
rep( 1:5, 2 )
rep( "AT", 5 )
LoL <- paste( rep( "JA", 10 ), collapse="" )
LoL
values <- sample( 2:39, 4 )
line1 <- paste( paste( rep( "-", values[1]-2), collapse="" ), "> ", values[1], collapse="", sep="" )
line2 <- paste( paste( rep( "-", values[2]-2), collapse="" ), "> ", values[2], collapse="", sep="" ) 
line3 <- paste( paste( rep( "-", values[3]-2), collapse="" ), "> ", values[3], collapse="", sep="" ) 
line4 <- paste( paste( rep( "-", values[4]-2), collapse="" ), "> ", values[4], collapse="", sep="" )  
line5 <- paste( rep( 0:9, 4 ), collapse="" )
line6 <- sprintf( "%-10d%-10d%-10d%-10d",0, 1,2,3 )
line1;line2;line3;line4;line5;line6

# merging two data.frames
test1 <- data.frame( NIU=IDs, Score=sample( 1:10, 4 ), row.names=students )
test2 <- data.frame( IDs=IDs, Score=sample( 1:10, 4 ), row.names=students )
merge( test1, test2, by.x="NIU", by.y="IDs" )

# more on building matrices and data.frames
m2 <- rbind( v1, v2, v3, v4 )
m3 <- cbind( v1, v2, v3, v4 )
m2; m3
t( m3 ) # transpose 

# sort and ordering
nums <- round( abs( rnorm( 5 )*100  ) )
sort( nums )
order( nums )
classroom[ order( classroom$Scores, decreasing=T ), ]

# sets
seq1 <- c( "ATG", "ACG", "GCC", "ACG", "GCA" )
seq2 <- c( "ATG", "CCG", "ATA", "ACG", "GCC" )
c( seq1, seq2 )
union( seq1, seq2 )
intersect( seq1, seq2 )
setdiff(seq1, seq2 )
setequal( seq1, seq2 )
setequal( seq1, rev(seq1) )
is.element( "ATA", seq1 )
is.element( "ATA", seq2 )

# Filling gaps
v5
v5[ 3 ] <- 3
v5 <- NULL
v5[ 3 ] <- 3

# Write R object to a text file
write.table(classroom, "classroom.txt", sep="\t")

# Read from text file
classroom2 <- read.table( "classroom.txt", sep="\t" ) 

# You can also save/load 'binary' representations of objects
# This method usually is faster for big objects
save(classroom, file = "classroom.RData")
?save.image # All objects
load("classroom.RData")

# You may also want to check out the 'fread' function in the 'data.table' package

5. Functions and flow control: loops and conditional execution

# A function is another type of object. 
# It has a name, retrieves some arguments, and returns some results
# func_name <- function( arg1, arg2, ...)
# {
#   BODY
#   return( value )
#}

# Let's repeat the previous block of code. 
# Some small changes needed + more fun, explicit printing with cat

camel_race <- function( )
{
    values <- sample( 2:39, 4 ); 
    line1 <- paste( paste( rep( "-", values[1]-2), collapse="" ), "> ", values[1], collapse="", sep="" )
    line2 <- paste( paste( rep( "-", values[2]-2), collapse="" ), "> ", values[2], collapse="", sep="" ) 
    line3 <- paste( paste( rep( "-", values[3]-2), collapse="" ), "> ", values[3], collapse="", sep="" )
    line4 <- paste( paste( rep( "-", values[4]-2), collapse="" ), "> ", values[4], collapse="", sep="" )
    line5 <- paste( rep( 0:9, 4 ), collapse="" )
    line6 <- sprintf( "%-10d%-10d%-10d%-10d",0, 1,2,3 )
    cat("\n")
    leader_num <- which.max( values )
    cat( sprintf( "Camel %d leading the race!! At step: %d !!\n", leader_num, values[ leader_num ] ) )
    cat("\n")
    cat(line1,"\n")
    cat(line2,"\n")
    cat(line3,"\n")
    cat(line4,"\n")
    cat(line5,"\n")
    cat(line6,"\n")
}

camel_race()

# Now, create a file (in $PWD) called something like, example33.R
# and paste inside the function code (or anything you want to load!)
#
# Once done, you can edit it and load it with
source("example33.R")

# Let's make some changes in the function stored in the file
#
# 1. Add some arguments with default values
#    camel_race <- function( racer="Camel")
#
# 2. Change the message to something like this:
#  cat( sprintf( "%s %d leading the race!! At step: %d !!\n", racer, leader_num, values[ leader_num ] ) )

source("example33.R")
camel_race()
camel_race( racer="Horse")

# 3. We can change the also the function name to:
#  cool_race <- function()

source("example33.R")
cool_race()

# 4. We can add more arguments:
#  cool_race <- function( racer="Camel", participants=4)
#  values <- sample( 2:39, participants )

# At this point, dont't you think it will be nice to iterate!? In R we basically need to know:
#     for(var in seq) expr
#     while(cond) expr
#     repeat expr
#     break
#     next

for( idx in 5:0 ) {
    cat( "Countdown is: ", idx, "\n" );
}

# so, going back to our function

cool_race <- function( racer="Camel", participants=4 )
{
    values <- sample( 2:39, participants )
    lines <- NULL
    for( idx in 1:participants ) {
        lines[ idx ] <- paste( paste( rep( "-", values[idx]-2), collapse="" ), 
                               "> ", values[idx], collapse="", sep="" )
    }
    lines[ participants + 1 ] <- paste( rep( 0:9, 4 ), collapse="" )
    lines[ participants + 2 ] <- sprintf( "%-10d%-10d%-10d%-10d",0, 1,2,3 )

    cat("\n")
    leader_num <- which.max( values )
    cat( sprintf( "%s %d leading the race!! At step: %d !!\n", racer, leader_num, values[ leader_num ] ) )
    cat("\n")
    for( idx in 1:length( lines ) ) { cat( lines[ idx ], "\n" )}
}

# Next, let's check conditionals.
#     if(cond) expr
#     if(cond) cons.expr  else  alt.expr

# Now make the following changes:
# 1. Change this:
values <- sample( 2:39, participants )

# by this:
max_len <- 39
if( participants > max_len ) {
    cat( "\n", "#Participants reduced from", as.character(participants), 
         " to ", as.character(max_len), "\n" )
  participants <- max_len-2;
}
values <- sample( 2:max_len, participants )

# 2. And this:
cat( sprintf( "%s %d leading the race!! At step: %d !!\n", racer, leader_num, values[ leader_num ] ) );

# by this:
if( max(values) == max_len ) {
    cat( sprintf( "%s %d win the race!! At step: %d !!\n", racer, leader_num, values[ leader_num ] ) )
}
 else {
    cat( sprintf( "%s %d leading the race!! At step: %d !!\n", racer, leader_num, values[ leader_num ] ) )
}

# Note that sometimes there is no need for loops! 
# e.g. Applying a function, predefined or anonymous, to vector (or matrix)

# vectors
sapply( x, function( x ){ y <- x^3; y <- y+22; return(y) } ) 

# matrices
apply(m1, 2, mean) # '1' is rows, '2' is columns

6. Graphics

# The elementary command is plot()
x <- runif( 10 ) # ?runif; ?rnorm
y <- runif( 10 )
plot( x, y )

# There are several types
plot( x, y, type="p" ) # points
plot( x, y, type="l" ) # lines
plot( x, y, type="b" ) # both
plot( x, y, type="s" ) # steps

# At the beginning, graphics in R are extremely basic... too much

# High-level plots
barplot( x )
hist( x )
pie( x )
pairs( m1 )
image( m2 )
boxplot( x, y )
boxplot( m2 )

# And low-level ones
# points, lines, text
plot( x, y, main="Our scatter plot", xlab="x variables" )

par( mfrow=c(2,2) )
plot( x, y )
plot( x, y, pch=1, col="red" )
plot( x, y, pch=1, col=c( "red", "green", "blue", "magenta" ) )
plot( x, y, type="l" )

# See different types of "points"
par( mfrow=c(3,3) )
for( i in 1:9 ) { plot( x, y, pch=i, col=rainbow(25)) }

# Histogram
x <- rnorm( 100 )
y <- rnorm( 100 )
par( mfrow=c(1,2) )
hist( x )
hist( x , breaks=40)

boxplot( x )
boxplot( x, y )

# Nicer
par( mfrow=c(2,2) )
hist( x , main="A")
hist( x , breaks=40, main="B")
boxplot( x , main="C")
boxplot( x, y, notch=T, main="D")

# Addition 
par( mfrow=c(1,2) )
plot( x,y )
lines( c(-1,-1,1,1,-1),c(-1,2,2,-1,-1), col="red", lwd=3) # adding objects
plot( x,y, xlim=c(-1,1), ylim=c(-1,2) ) # change x,y limits
lines( c(-1,-1,1,1,-1),c(-1,2,2,-1,-1), col="red", lwd=3)

plot( x, type="l" ) 
lines( lowess(x), col="red", lwd=2 )

plot( x, y )
text( x, y-0.1, labels=1:length(x), cex=0.5 ) # -0.1 to print below
points( 0, 1, col="red", cex=1.5, pch=19 )    
points( 0, 0, col="yellow", cex=1.5, pch=19 )
points( 0, -1, col="green", cex=1.5, pch=19 )

# Legends
par( mfrow=c(2,1) )
z <- 1:length(x)
plot( z, x )
points( 0, 1, col="red", cex=1.5, pch=19 )    
legend( x="bottomright", legend=c("Original", "Red dot"), fill=c("black", "red") )
plot( z, x, type="l" )
lines( lowess(z,x), col="red", lwd=2 )    
legend( x="bottomright", legend=c("Original", "Smoothed"), fill=c("black", "red") )

## Nicer R graphics: ggplot2 

# Compare with base R plot using the 'DNase' dataset 
# This dataset comes with base R and it corresponds to an ELISA assay used to quantify the activity of the enzyme deoxyribonuclease (DNase), which degrades DNA. The object is a dataframe whose columns are Run, the assay run; conc, the protein concentration that was used; and density, the measured optical density.

head(DNase)

plot(DNase$conc, DNase$density)
hist(DNase$density, breaks=25, main = "")
boxplot(density ~ Run, data = DNase)

library(ggplot2)

ggplot(DNase, aes(x = conc, y = density), size = 0.8) + geom_point() # 'aes' stands for 'aesthetics'

ggplot(DNase, aes(x = density)) +
  geom_histogram()

ggplot(DNase, aes(x = Run, y = density, fill = Run)) +
  geom_boxplot()
  # geom_violin()

gg <- ggplot(DNase, aes(x = conc, y = density, color = Run)) +  geom_point() # You can store the plots

print(gg)  # print them

gg + geom_smooth(aes(fill = Run), method = "loess")  # and keep adding details

gg + theme_bw(base_size = 20) 

gg + ggtitle("Title") + xlab("Concentration") + ylab("Density")

gg + 
  scale_fill_manual(values = brewer.pal(11, "Spectral")) +
  scale_color_manual(values = brewer.pal(11, "Spectral")) 

# Some color palettes
library(RColorBrewer)
display.brewer.all()

# Faceting: facet_wrap, facet_grid
gg + facet_wrap(.~Run) 

# Change point size, trasparency
ggplot(DNase, aes(x = conc, y = density, alpha = density, size = as.numeric(Run))) + 
  geom_point() +
  facet_wrap(.~as.numeric(Run))

# Useful function to obtain datasets ready to plot by ggplot2: ?melt

# Save figures to a file
ggsave("myplot.pdf", plot = gg)

# You can explore further ggplot2 at https://ggplot2.tidyverse.org/
# Interactive graphics: shiny, plotly
# Heatmaps: better to use a different package: 'pheatmap'