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")
# 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 )
# 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)
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
# 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
# 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'