Σε αυτές τις σύντομες σημειώσεις θα καλύψουμε τις βασικές εισαγωγικές έννοιες του λογισμικού R:

  1. RStudio
  2. Objects
    • Χρήσιμοι τύποι δεδομένων
  3. Πράξεις
    • Αριθμητικές Πράξεις
    • Λογικές Πράξεις
  4. Δομές Δεδομένων
    • Διανύσματα
    • Λίστες
    • Πίνακες
    • Data Frames
    • Αποθήκευση και ανάκτηση δεδομένων
  5. Αποθήκευση Εντολών
    • R Scripts
    • R Functions
  6. Στατιστικές Λειτουργίες
    • Βασικές Στατιστικές Εντολές
    • Μοντέλα
    • Αποτελέσματα Ανάλυσης
    • Ανάκτηση Μεταβλητών από Αποτελέσματα
  7. Plotting
    • Βασικές Εντολές Γραφημάτων
    • Παράμετροι
    • Αποθήκευση/Ανάκτηση Συνόλου Παραμέτρων
  8. Βασικές Αρχές Προγραμματισμού
    • Δομή if/else
    • Δομή for
    • Δομή while
    • Συναρτήσεις κατηγορίας apply
  9. Δημιουργία δυναμικών εγγράφων με RMarkdown

1 RStudio

1.1 R Scripts

Οι εντολές που δίνονται απευθείας στο παράθυρο Console δεν αποθηκεύονται κάπου συστηματικά ώστε να μπορούν να επαναληφθούν εύκολα (ανακτώνται με τον cursor up/down). Τα R scripts δίνουν τη δυνατότητα να αποθηκεύονται εντολές/συναρτήσεις κλπ σε αρχείο εντολών (με κατάληξη .R) και να εκτελείται μέρος ή όλο το αρχείο εύκολα.

  • Ctrl-Enter : Εκτέλεση τρέχουσας γραμμής
  • Ctrl-Alt-R: Εκτέλεση όλου του αρχείου

2 R Objects

Τα objects είναι γενικά μεταβλητές στις οποίες αποθηκεύονται δεδομένα διαφόρων τύπων (αριθμοί, χαρακτήρες, διανύσματα, πίνακες κλπ). Η γενική εντολή ανάθεσης (assignment) είναι <- (το = είναι ισοδύναμο στις περισσότερες περιπτώσεις). Μετά από μια εντολή ανάθεσης η μεταβλητή (και μέρος του περιεχομένου της) φαίνονται στο παράθυρο Environment. Γράφοντας μόνο το όνομα της μεταβλητής επιστρέφεται το περιεχόμενο. Τα ονόματα των objects είναι case sensitive. Νέα ανάθεση στην ίδια μεταβλητή αποθηκεύει τη νέα τιμή ενώ η παλιά χάνεται.

a <- 2+4
a
## [1] 6
a <- 15
a
## [1] 15
A <- 6
A
## [1] 6
a
## [1] 15
rm(a)

2.1 Χρήσιμες κατηγορίες objects (data classes)

  • Πραγματικοί Αριθμοί: double (“διπλής ακρίβειας”)
  • Ακέραιοι Αριθμοί: integer
  • Αριθμητικά δεδομένα: numeric (double/integer)
  • Λογικοί : logical (0 = FALSE, άλλο=TRUE)
  • Χαρακτήρες : character

Έλεγχος κατηγορίας : is.double, is.integer, κλπ.

Μετατροπή από μια κατηγορία σε άλλη : as.double, as.integer, κλπ.

Μετατροπή double σε integer γίνεται και μέσω των συναρτήσεων floor (ακέραιο μέρος) και round (στρογγύλευση), οι οποίες όμως δεν αλλάζουν τον τύπο της μεταβλητής.

a=5.8
a
## [1] 5.8
is.integer(a)
## [1] FALSE
is.double(a)
## [1] TRUE
is.numeric(a)
## [1] TRUE
is.logical(a)
## [1] FALSE
# Αλλαγή τύπου 
b=as.integer(a)
b
## [1] 5
is.integer(b)
## [1] TRUE
is.double(b)
## [1] FALSE
is.numeric(b)
## [1] TRUE
# Υπολογισμός ακέραιου μέρους και στρογγύλευσης
c=floor(a)
c
## [1] 5
is.integer(c)
## [1] FALSE
is.double(c)
## [1] TRUE
d=round(a)
d
## [1] 6
is.integer(d)
## [1] FALSE
is.double(d)
## [1] TRUE
# Μετατροπή σε logical
a=4
is.double(a)
## [1] TRUE
b=as.logical(a)
b
## [1] TRUE
is.logical(b)
## [1] TRUE
c=as.integer(b)
c
## [1] 1
a=0
b=as.logical(a)
b
## [1] FALSE
c=as.integer(b)
c
## [1] 0
#Character
a="hello"
a
## [1] "hello"
is.character(a)
## [1] TRUE
is.numeric(a)
## [1] FALSE

3 Πράξεις

3.1 Αριθμητικές Πράξεις

2+2
## [1] 4
2*2
## [1] 4
2^3
## [1] 8

3.2 Λογικές Πράξεις

2<3
## [1] TRUE
2 >=5
## [1] FALSE
2==2 #Προσοχή! = : ανάθεση, == : έλεγχος ισότητας
## [1] TRUE
# Άρνηση : !

2 > 4 
## [1] FALSE
!(2>4)
## [1] TRUE
2<4
## [1] TRUE
!(2<4)
## [1] FALSE
# Σύζευξη : & 

2<3 & 2<4
## [1] TRUE
2<3 & 2<1
## [1] FALSE
# & μεταξύ αριθμών : οι αριθμοί θεωρούνται logical : 0=F, διαφορετικά=Τ

1&2
## [1] TRUE
3&4
## [1] TRUE
3&0
## [1] FALSE
#Προσοχή στις παρενθέσεις !

(4 < 5 ) & 1
## [1] TRUE
4 < (5 & 1)
## [1] FALSE

4 Δομές Δεδομένων (data structures)

4.1 Διάνυσμα (vector)

Μονοδιάστατη δομή, περιέχει στοιχεία ίδιου τύπου. Δημιουργία μέσω συνάρτησης combine: c().

Αριθμός στοιχείων διανύσματος: length()

v=c(1,2,3,4,5)
v
## [1] 1 2 3 4 5
is.vector(v)
## [1] TRUE
v=c(1:10)
v
##  [1]  1  2  3  4  5  6  7  8  9 10
# Δημιουργία διανύσματος με επαναλαμβανόμενα στοιχεία 
w=rep(10, 4)
w
## [1] 10 10 10 10
#Συμπλήρωση με επιπλέον στοιχεία
v=c(v,15)
v
##  [1]  1  2  3  4  5  6  7  8  9 10 15
w=c("Mon", "Tue", "Wed")
w
## [1] "Mon" "Tue" "Wed"
is.vector(w)
## [1] TRUE
length(w)
## [1] 3
w=c(w,2)
w
## [1] "Mon" "Tue" "Wed" "2"
# Προσοχή: Εδώ το 2 προστέθηκε ως character για να συμφωνεί με τα υπόλοιπα στοιχεία του w. 

Πλοήγηση (εύρεση στοιχείων) διανύσματος : [ ]

v=c(10,20,30,40,50, 60,70)
v
## [1] 10 20 30 40 50 60 70
v[2]
## [1] 20
v[c(2,4)]
## [1] 20 40
v[2:4]
## [1] 20 30 40
v[-2] # εκτός του δεύτερου στοιχείου
## [1] 10 30 40 50 60 70
v[-c(2,4)]
## [1] 10 30 50 60 70
v[-(2:4)]
## [1] 10 50 60 70

4.2 List

Μονοδιάστατη γενική δομή, περιέχει στοιχεία διαφορετικών γενικά τύπων (μπορεί και λίστες). Δημιουργείται με τη συνάρτηση list()

L=list(1,2,"hello",FALSE)

L
## [[1]]
## [1] 1
## 
## [[2]]
## [1] 2
## 
## [[3]]
## [1] "hello"
## 
## [[4]]
## [1] FALSE
L[1]
## [[1]]
## [1] 1
is.numeric(L[1])
## [1] FALSE
is.logical(L[4])
## [1] FALSE
# Ένα στοιχείο της λίστας μπορεί να είναι το ίδιο άλλη λίστα

L=list(1,2,"hello", list(3,"a"))

4.3 Πίνακας (Matrix)

Διδιάστατη δομή (γραμμές και στήλες), όλα τα στοιχεία του ίδιου τύπου.

Δημιουργία Πίνακα

1. Μέσω εντολών rbind, cbind - rbind: ενώνει διανύσματα γραμμής κάθετα - сbind: ενώνει διανύσματα στήλης οριζόντια

rbind(c(1,2), c(3,4))
##      [,1] [,2]
## [1,]    1    2
## [2,]    3    4
cbind(c(1,2), c(3,4))
##      [,1] [,2]
## [1,]    1    3
## [2,]    2    4

2. Μέ μετατροπή διανύσματος σε πίνακα μέσω συνάρτησης matrix

v=c(1:6)

# Kατά γραμμή πρώτα
m1=matrix(v,nrow=2,byrow=T)
m1
##      [,1] [,2] [,3]
## [1,]    1    2    3
## [2,]    4    5    6
# Kατά στήλη πρώτα
m2=matrix(v,nrow=2,byrow=F)
m2
##      [,1] [,2] [,3]
## [1,]    1    3    5
## [2,]    2    4    6

Επιλογή στοιχείων πίνακα

Αντίστοιχα με τα διανύσματα, τώρα σε δύο διαστάσεις

m1=matrix(c(1:24),nrow=6,byrow = T)

m1
##      [,1] [,2] [,3] [,4]
## [1,]    1    2    3    4
## [2,]    5    6    7    8
## [3,]    9   10   11   12
## [4,]   13   14   15   16
## [5,]   17   18   19   20
## [6,]   21   22   23   24
m1[2,3]
## [1] 7
#Κενό σε μια διάσταση σημαίνει επιλογή όλων των γραμμών ή στηλών

#2η γραμμή όλες οι στήλες
m1[2,]
## [1] 5 6 7 8
#1η στήλη, όλες οι γραμμές
m1[,1]
## [1]  1  5  9 13 17 21
# πρώτες 2 γραμμές, στήλες 2 και 4
m1[1:2, c(2,4)]
##      [,1] [,2]
## [1,]    2    4
## [2,]    6    8

** Επιλογή με λογικά κριτήρια **

Η επιλογή γραμμών ή στηλών μπορεί να γίνει μέσω λογικών διανυσμάτων όπου T σημαίνει ότι η αντίστοιχη γραμμή/στήλη επιλέγεται και F ότι δεν επιλέγεται.

m1
##      [,1] [,2] [,3] [,4]
## [1,]    1    2    3    4
## [2,]    5    6    7    8
## [3,]    9   10   11   12
## [4,]   13   14   15   16
## [5,]   17   18   19   20
## [6,]   21   22   23   24
# Επιλογή γραμμών 1 και 3

m1[c(T,F,T,F,F,F),]
##      [,1] [,2] [,3] [,4]
## [1,]    1    2    3    4
## [2,]    9   10   11   12

Το λογικό διάνυσμα επιλογής μπορεί να έχει δημιουργηθεί από ελέγχους πάνω στα ίδια τα στοιχεία του διανύσματος ή του πίνακα.

v=c(1:10)
v
##  [1]  1  2  3  4  5  6  7  8  9 10
#Έλεγχος των στοιχείων του v αν είναι >=4
v>=4
##  [1] FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
#Επιλογή των στοιχείων του v τα οποία είναι >=4
v[v>=4]
## [1]  4  5  6  7  8  9 10
#Λειτουργεί και με διαφορετικά διανύσματα
w=c(11:20)

v
##  [1]  1  2  3  4  5  6  7  8  9 10
w
##  [1] 11 12 13 14 15 16 17 18 19 20
w[v>=4]
## [1] 14 15 16 17 18 19 20
# Αντίστοιχα ισχύουν για την επιλογή γραμμών ή στηλών πίνακα

m1
##      [,1] [,2] [,3] [,4]
## [1,]    1    2    3    4
## [2,]    5    6    7    8
## [3,]    9   10   11   12
## [4,]   13   14   15   16
## [5,]   17   18   19   20
## [6,]   21   22   23   24
# Έλεγχος των στοιχείων της 2ης στήλης που είναι >= 12
m1[,2]>=12
## [1] FALSE FALSE FALSE  TRUE  TRUE  TRUE
# Επιλογή ολόκληρων των γραμμών για τις οποίες τα στοιχεί της 2ης στήλης είναι >= 12
m1[m1[,2]>=12,]
##      [,1] [,2] [,3] [,4]
## [1,]   13   14   15   16
## [2,]   17   18   19   20
## [3,]   21   22   23   24

4.4 Data Frame

Είναι η πιο χρήσιμη δομή δεδομένων στο R.

Διδιάστατη δομή (γραμμές - στήλες)

Κάθε στήλη περιέχει στοιχεία ίδιου τύπου. Οι στήλες είναι γενικά διαφορετικού τύπου. Είναι η πιο κατάλληλη δομή για να παραστήσει δείγμα, όπου οι γραμμές αντιστοιχούν σε διαφορετικές παρατηρήσεις ενώ οι στήλες στις μεταβλητές κάθε παρατήρησης.

a=c(1:5)

b=c('Mon','Tue','Mon','Wed','Wed')

c=c(T,T,F,T,F)

df=data.frame(a,b,c)
df

Οι στήλες μπορεί να έχουν ονόματα. Στο παραπάνω παράδειγμα “κληρονόμησαν” τα ονόματα των διανυσμάτων από τα οποία προήλθε το data.frame.

Τα ονόματα των στηλών μπορεί να αναζητηθούν και να αλλαχθούν με τη συνάρτηση names.

names(df) #Επιστρέφει διάνυσμα με τα ονόματα των στηλών του df
## [1] "a" "b" "c"
# Αλλαγή του πρώτου ονόματος σε "id_number"
names(df)[1]='id_number'
names(df)
## [1] "id_number" "b"         "c"
# Αλλαγή όλων των ονομάτων
names(df)=c('id_number', 'weekday', 'rain')
names(df)
## [1] "id_number" "weekday"   "rain"
df

Επιλογή στοιχείων data.frame

Μπορεί να γίνει όπως και στους πίνακες με επιλογές γραμμών/στηλών

df[1,2]
## [1] Mon
## Levels: Mon Tue Wed
df[c(1:3),2]
## [1] Mon Tue Mon
## Levels: Mon Tue Wed

Μια στήλη επίσης μπορεί να επιλεγεί με το όνομα.

names(df)
## [1] "id_number" "weekday"   "rain"
#Επιλογή 1ης στήλης με αριθμό
df[,1]
## [1] 1 2 3 4 5
#Επιλογή 1ης στήλης με όνομα
df$id_number
## [1] 1 2 3 4 5
#Επιλογή ολόκληρων των γραμμών που έχουν rain=TRUE
df[df$rain==T, ]
#Επιλογή id_number και weekday για τις γραμμές που έχουν rain=TRUE
df[df$rain==T, c(1:2) ]

4.5 Αποθήκευση και Ανάκτηση Δεδομένων

Οποιαδήποτε αντικείμενα έχουν οριστεί σε ένα R session μπορούν να αποθηκευτούν σε ένα αρχείο για μελλοντική ανάκτηση και χρήση, με την εντολή save. Η εντολή αποθηκεύει τα αντικείμενα σε ένα αρχείο με επέκταση “.Rdata”, του οποίο το όνομα δίνεται στην επιλογή file:

a1=5
a2=c(4,3,2)
a3=matrix(runif(10), 2,5)
save(a1, a2, a3, file="a.Rdata")

Το αρχείο τοποθετείται στον τρέχοντα φάκελο εργασίας. Ο φάκελος εργασίας φαίνεται με την εντολή getwd() ενώ διαφορετικός φάκελος εργασίας ορίζεται με την εντολή setwd(“directoryname”).

Η εντολή load(“filename.Rdata”) διαβάζει το αρχείο filename.Rdata και ορίζει εκ νέου όλα τα αντικείμενα που έχουν αποθηκευτεί σε αυτό με τις τιμές τους. Αν υπάρχουν ήδη αντικείμενα με το ίδιο όνομα αυτά αντικαθίστανται από τα αντίστοιχα ανακτημένα.

rm(a1)
a2=4
a2
## [1] 4
load("a.Rdata")
a1
## [1] 5
a2
## [1] 4 3 2

Η εντολή save.image() αποθηκεύει όλα τα αντικείμενα που έχουν οριστεί στο χώρο εργασίας σε ένα αρχείο με όνομα “.Rdata” που τοποθετείται στον τρέχοντα φάκελο εργασίας.

Εκτός από την αποθήκευση και ανάκτηση ήδη ορισμένων αντικειμένων, συχνά χρειάζεται η εισαγωγή δεδομένων από εξωτερικά αρχεία άλλου τύπου (π.χ. κειμένου, Excel κλπ) σε dataframes στο R. Αυτό γίνεται με εντολές της κατηγορίας read (π.χ. read.table(), read.csv() κλπ).

Η πιο συνηθισμένη εντολή αυτής της κατηγορίας είναι η read.csv που διαβάζει ένα αρχείο δεδομένων σε μορφή κειμένου και το μεταφέρει σε ένα dataframe. Κάθε γραμμή του αρχείου αποθηκεύεται σε μια γραμμή του dataframe. Οι στήλες μέσα στο αρχείο κειμένου διαχωρίζονται με ένα συγκεκριμένο χαρακτήρα (π.χ. κόμμα, space, tab, κλπ). Τα ονόματα των στηλών ανακτώνται (αν είναι επιθυμητό) από την πρώτη γραμμή του αρχείου και πρέπει επίσης να είναι διαχωρισμένα με τον ίδιο χαρακτήρα. Ο τύπος των δεδομένων για κάθε στήλη αναγνωρίζεται αυτόματα (αν είναι ο ίδιος σε όλες τις γραμμές). Ο χαρακτήρας που διαχωρίζει τις στήλες ορίζεται με την επιλογή sep=“,” για κόμμα, sep=" για tabs. Με την επιλογή header=TRUE/FALSE ορίζεται αν η πρώτη γραμμή του αρχείου θα χρησιμοποιηθεί για ονόματα στηλών ή είναι η πρώτη γραμμή των δεδομένων, αντίστοιχα.

ozone=read.csv(file="ozone.data", sep="\t", header=T)
names(ozone)
## [1] "ozone"       "radiation"   "temperature" "wind"

Για την εισαγωγή αρχείων με την εντολή read.csv το εξωτερικό αρχείο πρέπει να είναι μορφής απλού κειμένου με τις προδιαγραφές που αναφέρθηκαν παραπάνω. Αρχεία δεδομένων από άλλα λογισμικά συνήθως αποθηκεύονται σε μορφή συμβατή με το αντίστοιχο λογισμικό (π.χ. .xlsl για αρχεία Excel, .sav για αρχεία SPSS κλπ.). Για να μπορέσουν να εισαχθούν στο R με την εντολή read.csv θα πρέπει πρώτα να αποθηκευτούν σε μορφή κειμένου με κατάλληλες εντολές από το άλλο λογισμικό (π.χ. save as csv από το Excel). Εναλλακτικά μπορούν να χρησιμοποιηθούν άλλες εντολές τύπου read για απευθείας ανάκτηση δεδομένων συγκεκριμένου τύπου.

5 Αποθήκευση Εντολών - Scripts and Functions

Όπως είδαμε παραπάνω σε ένα R script αποθηκεύονται εντολές που μπορούν να εκτελεστούν όλες μαζί σε επόμενη στιγμή. Ένα script μπορεί να εκτελεστεί είτε μέσα από το παράθυρό του (με Ctrl-Enter ανά γραμμή ή Ctrl-Alt-R όλο) είτε από το παράθυρο εντολών του Rstudio (Console) με την εντολή source(“name.R”), όπου name.R είναι το όνομα με το οποίο έχει αποθηκευτεί το script στο φάκελο εργασίας. Ο ενεργός φάκελος εργασίας βρίσκεται με τη συνάρτηση getwd(), και αλλάζει με τη συνάρτηση setwd(“folder path”).

Κάθε φορά που εκτελείται ένα script όλες οι εντολές εκτελούνται με ακριβώς τον ίδιο τρόπο. Αν ο χρήστης θέλει να αλλάξει π.χ. μια τιμή μεταβλητής, πρέπει να κάνει την αλλαγή μέσα στο script, να το αποθηκεύσει εκ νέου και να το εκτελέσει.

Για παράδειγμα, αν το script με όνομα test.R περιέχει τις εξής εντολές

x=2
y=x^3
y
## [1] 8

τότε κάθε φορά που δίνεται η εντολή source(“test.R”), η μεταβλητή x θα παίρνει την τιμή 2 και η μεταβλητή y την τιμή 8. Αν ο χρήστης θέλει να υπολογίσει τον κύβο του 4, θα πρέπει μέσα στο αρχείο test.R να αλλάξει την εντολή x=2 σε x=4, να αποθηκεύσει το αρχείο test.R και να το ξανακαλέσει.

Το εργαλείο της συνάρτησης δίνει ένα πιο ευέλικτο τρόπο εκτέλεσης εντολών. Δημιουργώντας μια συνάρτηση ο χρήστης ορίζει κάποιες μεταβλητές οι οποίες παίρνουν τιμές κατά τη στιγμή κλήσης της συνάρτησης, και επομένως μπορεί να είναι διαφορετικές κάθε φορά που καλείται η συνάρτηση, χωρίς να χρειάζεται επαναπρογραμματισμός και αποθήκευση.

Για παράδειγμα, με τις παρακάτω εντολές δημιουργείται η συνάρτηση cube, δηλαδή ένα νέο object, τύπου συνάρτησης, που υπολογίζει και επιστρέφει τον κύβο του αριθμού x, η τιμή του οποίου δίνεται από τον χρήστη κάθε φορά που καλείται η συνάρτηση. H x είναι το όρισμα (input) και η y το αποτέλεσμα (output) της συνάρτησης cube.

cube = function(x)
{
  y=x^3
  return(y)
}

Όλες οι εντολές που εκτελούνται κάθε φορά που καλείται η συνάρτηση περιλαμβάνονται μέσα σε {}.

Η συνάρτηση cube αποτελεί ένα νέο object που καλείται όπως όλες οι υπόλοιπες προκαθορισμένες συναρτήσεις του R. Τα ονόματα των μεταβλητών x,y θεωρούνται τοπικές μεταβλητές, δηλαδή οι τιμές τους ισχύουν μόνο όσο εκτελείται η συνάρτηση και δεν έχουν σχέση με άλλες μεταβλητές που μπορεί να έχουν οριστεί με το ίδιο όνομα στο εξωτερικό περιβάλλον εργασίας. Επίσης κάθε φορά που καλείται η συνάρτηση cube, αυτές μηδενίζονται και ορίζονται εκ νέου μέσα στη συνάρτηση (δεν διατηρούνται οι τιμές τους από προηγούμενες κλήσεις). Τέλος, τα ονόματα x,y είναι τυπικά, δηλαδή όταν καλείται η συνάρτηση στη θέση του x μπορεί να μπει οποιοσδήποτε αριθμός ή οποιαδήποτε μεταβλητή έχει ήδη οριστεί και θέλουμε να υπολογίσουμε τον κύβο της, και το αποτέλεσμα μπορούμε να το αποθηκεύσουμε σε όποια μεταβλητή θέλουμε και όχι αναγκαστικά στην y.

a=cube(2)
a
## [1] 8
b=cube(5)
b
## [1] 125
x=2
y=cube(x)
y
## [1] 8
d=4
e=cube(d)
e
## [1] 64

Μια συνάρτηση μπορεί να έχει περισσότερα από ένα ορίσματα οποιουδήποτε τύπου (π.χ. αριθμό, χαρακτήρα, διάνυσμα κλπ) αλλά μόνο ένα αποτέλεσμα. Φυσικά αν χρειάζεται, το αποτέλεσμα μπορεί να είναι ένα διάνυσμα ή γενικότερα μια λίστα που περιλαμβάνει περισσότερα αποτελέσματα που προέρχονται από την εκτέλεση της συνάρτησης. Για παράδειγμα συνάρτηση circletest παίρνει ως ορίσματα τους αριθμούς (x,y,r), υπολογίζει την απόσταση του σημείου \((x,y)\) από την αρχή των αξόνων, δηλαδή την ποσότητα \(d=\sqrt{x^2+y^2}\), ελέγχει αν το σημείο βρίσκεται μέσα στον κύκλο με κέντρο την αρχή των αξόνων και ακτίνα \(r\), δηλαδή αν \(d\leq r\) και αποθηκεύει το αποτέλεσμα (TRUE/FALSE) στη λογική μεταβλητή incircle. Το αποτέλεσμα της συνάρτησης είναι μια λίστα που περιέχει τα d, incircle. Επειδή τα d, incircle είναι διαφορετικού τύπου, το αποτέλεσμα πρέπει να είναι λίστα και όχι π.χ. διάνυσμα που απαιτεί όλα τα στοιχεία να είναι του ίδιου τύπου.

circletest = function(x,y,r)
{
  d=sqrt(x^2+y^2)
  incircle=(d <= r)
  result=list(d,incircle)
  return(result)
}

#Κλήση της circletest
z=circletest(2,3,4)
z
## [[1]]
## [1] 3.605551
## 
## [[2]]
## [1] TRUE

6 Βασικά στοιχεία προγραμματισμού

Πολλές λειτουργίες/εργασίες στο R απαιτούν προγραμματισμό πέρα από τις απλές αναθέσεις τιμών σε μεταβλητές. Παρακάτω δίνουμε μερικά παραδείγματα από 3 βασικές λειτουργίες: for, if, while. Αυτές μπορούν να ενσωματωθούν είτε σε script files, είτε σε συναρτήσεις, σύμφωνα με όσα έχουμε πει παραπάνω.

6.1 Λειτουργία for

Με την εντολή for μπορεί να προγραμματιστεί μια λειτουργία που πρέπει να επαναληφθεί συγκεκριμένο αριθμό φορών, κάθε φορά με διαφορετική τιμή μιας μεταβλητής.

Για παράδειγμα, έστω ότι θέλουμε να κατασκευάσουμε ένα διάνυσμα y διάστασης 10, όπου το στοιχείο i είναι ίσο με \(y_i = 3 i^2 - 1\). Αυτό γίνεται με τις παρακάτω εντολές:

y=rep(0,10) #Πρώτα πρέπει να οριστεί το διάνυσμα για να μπορούν να αλλάξουν μετά οι τιμές των στοιχείων του. 
for (i in 1:10)
{
  y[i]=3*i^2 - 1
}
y
##  [1]   2  11  26  47  74 107 146 191 242 299

Σε ένα δεύτερο παράδειγμα, έστω ότι θέλουμε να υπολογίσουμε το άθροισμα \(\sum_{i=1}^10 \log(i+1)\).

S=0 # Ορίζουμε μια μεταβλητή S στην οποία θα προστίθεται η ποσότητα \log(i+1) σε κάθε επανάληψη του for.
for (i in 1:10)
{
  S=S+log(i+1)
}
S
## [1] 17.50231

6.2 Λειτουργία if, if else

Με την εντολή if μπορεί να προγραμματιστεί μια λειτουργία που πραγματοποιείται μόνο αν ικανοποιείται μια συνθήκη Με το συνδυασμό if.. else.. μπορούν να προγραμματιστούν 2 λειτουργίες, έτσι ώστε αν ισχύει μια συνθήκη γίνεται η πρώτη λειτουργία, ενώ αν δεν ισχύει γίνεται η δεύτερη.

x=2

x
## [1] 2
if (2==2) {x=4}

x
## [1] 4
if (2<1) {x=5}

x
## [1] 4
a=3

if (a<1) {x=1} else {x=3}

x
## [1] 3

Με διαδοχικά else/if μπορεί προγραμματιστούν περισσότερες από μια περιπτώσεις. Για παράδειγμα έστω μια συνάρτηση cases, η οποία παίρνει όρισμα έναν αριθμό x και επιστρέφει το \[ y = \left\{ \begin{array}{ll} 1,&\mbox{αν\ } x<0 \\ 2,&\mbox{αν\ } 0 \leq x < 4 \\ 3,&\mbox{αν\ } 4 \leq x < 8 \\ 4,&\mbox{αν\ } x \geq 0 \end{array} \right . \] Η συνάρτηση αυτή μπορεί να γραφεί ως εξής:

cases = function(a)
{
  if (a<0) 
  {
    y=1
  } else if (a<4)
  {
    y=2
  } else if (a<8) 
  {
    y=3
  }
  else 
  {
     y=4
  }
  
  return(y)
}

cases(-2)
## [1] 1
cases(3)
## [1] 2
cases(5)
## [1] 3
cases(20)
## [1] 4

6.3 Λειτουργία while

Η εντολή while συνδυάζει τις for και if: Μια λειτουργία ή σειρά λειτουργιών εκτελείται επαναλαμβανόμενα, όσο συνεχίζει να ισχύει μια συνθήκη. Την πρώτη φορά που θα πάψει να ισχύει η συνθήκη, η λειτουργία σταματά να εκτελείται. Προφανώς για να σταματήσει κάποια στιγμή η λειτουργία θα πρέπει μέσα σε κάποια επανάληψη να συμβεί κάτι που κάνει τη συνθήκη να σταματήσει να ικανοποιείται.

Για παράδειγμα, έστω ότι έχουμε ένα διάνυσμα με ηλικίες ατόμων, και θέλουμε να βρούμε τη μέση τιμή των ηλικιών των πρώτων ατόμων που έχουν ηλικία έως το πολύ 20, δηλαδή από το άτομο 1 μέχρι το πρώτο που θα συναντήσουμε που έχει ηλικία πάνω από 20 (αυτό δεν θα υπολογιστεί στη μέση τιμή).

v=c(10, 8, 12, 20, 15, 22, 23, 12, 23, 32)

s=0 # Μεταβλητή που αθροίζει διαδοχικα τις ηλικίες
i=1 # Ξεκινάμε από το άτομο 1
while (v[i] <= 20)
{
  s=s+v[i]
  i=i+1
}
   # όταν σταματήσει το loop η i θα έχει τιμή μεγαλύτερη κατά 1 από τον αριθμό των ατόμων που μετρήθηκαν. 
  # Επομένως η μέση τιμή που θέλουμε είναι s/(i-1)
  # Αν βέβαια το πρώτο άτομο είναι ήδη μεγαλύτερο του 20, το i στο τέλος θα είναι 1 και μέση τιμή δεν υπάρχει. 

if (i==1) 
{
  av=-2
  } else 
{
  av=s/(i-1)
}

av
## [1] 13

Στο παραπάνω παράδειγμα υπάρχει το πρόβλημα ότι αν το διάνυσμα έχει όλα τα στοιχεία <=20, τότε μετά το τελευταίο στοιχείο το i θα πάρει τιμή μεγαλύτερη απο το μέγεθος του διανύσματος και θα επιστραφεί σφάλμα. Για να το αποφύγουμε αυτό θα πρέπει στη συνθήκη του while να προσθέσουμε την i<=length(v)

v=c(10, 8, 12, 20, 15, 12, 13, 12, 13, 12)

n=length(v)
s=0 
i=1 
while (v[i] <= 20 & i<=n )
{
  s=s+v[i]
  i=i+1
}
  
if (i==1) 
{
  av=-2
  } else 
{
  av=s/(i-1)
}

av
## [1] 12.7

6.4 Αποφυγή for loops με συναρτήσεις της κατηγορίας apply

Οι αλγοριθμικές διαδικασίες for, if..else, while είναι γενικά πολύ χρήσιμες για τον προγραμματισμό περίπλοκων λειτουργιών σε δομές δεδομένων του R. Όμως γενικά είναι απαιτητικές σε χρόνο και μνήμη και ιδιαίτερα σε πολύ μεγάλα σύνολα δεδομένων μπορεί να προκαλέσουν σημαντικές καθυστερήσεις στην εκτέλεση των προγραμμάτων. Επειδή το R είναι μια γλώσσα που βασίζεται σε πολύ μεγάλο βαθμό σε δομές όπως οι πίνακες και τα διανύσματα, έχουν δημιουργηθεί συναρτήσεις που εκτελούν επαναληπτικά λειτουργίες στις γραμμές και τις στήλες ενός πίνακα με πολύ πιο γρήγορο τρόπο από ότι οι γενικές διαδικασίες που είδαμε παραπάνω. Για αυτό το λόγο είναι σημαντικό όποιος ασχολείται με δημιουργία και διαχείριση μεγάλων συνόλων δεδομένων να τις γνωρίζει και να μπορεί να τις εφαρμόζει όσο το δυνατό περισσότερο, έτσι ώστε τα προγράμματα να τρέχουν πιο γρήγορα και αποδοτικά. Η γενική κατηγορία αυτών των συναρτήσεων συναρτήσεων είναι οι συναρτήσεις τύπου apply.

6.4.1 Η συνάρτηση apply

Ας θεωρήσουμε ένα πίνακα x διαστάσεων 4Χ6:

x=matrix(1:24,nrow=4)
x
##      [,1] [,2] [,3] [,4] [,5] [,6]
## [1,]    1    5    9   13   17   21
## [2,]    2    6   10   14   18   22
## [3,]    3    7   11   15   19   23
## [4,]    4    8   12   16   20   24

Έστω ότι θέλουμε να δημιουργήσουμε ένα διάνυσμα s που περιέχει τα αθροίσματα των γραμμών του πίνακα x. Αυτό μπορεί να γίνει δημιουργώντας ένα for loop, που υπολογίζει το άθροισμα των στοιχείων κάθε γραμμής χρησιμοποιώντας την εντολή sum, και τοποθετεί το άθροισμα στο αντίστοιχο στοιχείο ενός διανύσματος s διάστασης 4:

s=rep(0,4)
for (i in 1:4)
{
    s[i]=sum(x[i,])
}
s
## [1] 66 72 78 84

Η παραπάνω διαδικασία θα μπορούσε να προγραμματιστεί πιο σύντομα χρησιμοποιώντας τη συνάρτηση apply. Η συνάρτηση έχει τη γενική σύνταξη

apply(x, Margin, Function)

όπου: x είναι ένα διάνυσμα (διάσταση 1) ή ένας πίνακας (διάσταση 2), και Function είναι μια συνάρτηση που θέλουμε να εφαρμοστεί επαναληπτικά στα στοιχεία του πίνακα. Η παράμετρος Margin καθορίζει τον τρόπο επαναληπτικής εφαρμογής. Συγκεκριμένα αν Margin=1 η εφαρμογή γίνεται σε όλες τις γραμμές μια προς μια, αν Margin=2 η εφαρμογή γίνεται σε όλες τις στήλες μια προς μια, ενώ αν Margin=c(1,2) η συνάρτηση εφαρμόζεται σε όλα τα στοιχεία του πίνακα ένα προς ένα.

Στο παραπάνω παράδειγμα, θέλουμε να εφαρμόσουμε τη συνάρτηση sum σε όλες τις γραμμές του πίνακα x μια προς μια. Επομένως η κατάλληλη εντολή είναι

s=apply(x,1,sum)
s
## [1] 66 72 78 84

Χρησιμοποιώντας Margin=2, μπορούμε αντίστοιχα να υπολογίσουμε τα αθροίσματα των στηλών:

t=apply(x,2,sum)
t
## [1] 10 26 42 58 74 90

Στη θέση της συνάρτησης sum θα μπορούσαμε να χρησιμοποιήσουμε οποιαδήποτε άλλη συνάρτηση μπορεί να εφαρμοστεί πάνω σε διανύσματα, π.χ. mean, sd κλπ. Επίσης μπορούμε να χρησιμοποιήσουμε συναρτήσεις που έχουμε ορίσει εμείς και όχι μόνο προκαθορισμένες συναρτήσεις του R. Για παράδειγμα μπορούμε να εφαρμόσουμε τη συνάρτηση cube που ορίσαμε παραπάνω και υπολογίζει την τρίτη δύναμη ενός αριθμού, για να υπολογίσουμε τους κύβους όλων των στοιχείων του x ένα προς ένα ως εξής:

y=apply(x,c(1,2),cube)
y
##      [,1] [,2] [,3] [,4] [,5]  [,6]
## [1,]    1  125  729 2197 4913  9261
## [2,]    8  216 1000 2744 5832 10648
## [3,]   27  343 1331 3375 6859 12167
## [4,]   64  512 1728 4096 8000 13824

Αν η συνάρτηση που θέλουμε να εφαρμόσουμε μέσω της εντολής apply έχει επιπλέον ορίσματα, οι τιμές τους μέσα στην εντολή apply δίνονται μετά το όνομα της συνάρτησης, χρησιμοποιώντας τη σύνταξη arg=val, όπου arg το όνομα του ορίσματος όπως αυτό δίνεται στον ορισμό της συνάρτησης και val η τιμή που θέλουμε να πάρει. Για παράδειγμα ας ορίσουμε μια συνάρτηση pow που υπολογίζει τη n-οστή δύναμη ενός αριθμού x:

pow=function(a,n)
{
  return(a^n)
}

Έστω ότι θέλουμε να εφαρμόσουμε τη συνάρτηση pow στα στοιχεία του πίνακα x, για να υπολογίσουμε την τετραγωνική ρίζα καθενός από αυτά. Μπορούμε να εφαρμόσουμε μέσω της εντολής apply τη συνάρτηση pow δίνοντας στο όρισμα n την τιμή 2:

s=apply(x,c(1,2),pow,n=1/2)
s
##          [,1]     [,2]     [,3]     [,4]     [,5]     [,6]
## [1,] 1.000000 2.236068 3.000000 3.605551 4.123106 4.582576
## [2,] 1.414214 2.449490 3.162278 3.741657 4.242641 4.690416
## [3,] 1.732051 2.645751 3.316625 3.872983 4.358899 4.795832
## [4,] 2.000000 2.828427 3.464102 4.000000 4.472136 4.898979

Στη θέση του ορίσματος που παραλείπεται μέσα στην εντολή apply χρησιμοποιείται το αντίστοιχο τμήμα του πίνακα x. Για παράδειγμα στην προηγούμενη εντολή δίνεται τιμή μόνο στο όρισμα n της συνάρτησης pow. Στη θέση του ορίσματος a χρησιμοποιείται κάθε φορά το στοιχείο του πίνακα x. Με αντίστοιχο τρόπο, αν δώσουμε τιμή στο όρισμα a και παραλείψουμε το όρισμα n της συνάρτησης pow μέσα στην apply, μπορούμε να υπολογίσουμε τις δυνάμεις του a, με εκθέτες τα στοιχεία του πίνακα x:

w=apply(x,c(1,2),pow,a=0.9)
w
##        [,1]      [,2]      [,3]      [,4]      [,5]       [,6]
## [1,] 0.9000 0.5904900 0.3874205 0.2541866 0.1667718 0.10941899
## [2,] 0.8100 0.5314410 0.3486784 0.2287679 0.1500946 0.09847709
## [3,] 0.7290 0.4782969 0.3138106 0.2058911 0.1350852 0.08862938
## [4,] 0.6561 0.4304672 0.2824295 0.1853020 0.1215767 0.07976644

Στην κατηγορία αυτών των συναρτήσεων εκτός από την apply υπάρχουν και πολλές άλλες (lapply, sapply, mapply, κλπ) που έχουν πιο γενικές ή πιο εξειδικευμένες λειτουργίες.

7 Στατιστικές Λειτουργίες

7.1 Κατανομές Πιθανότητας

Στο R έχει σχεδιαστεί με συστηματικό τρόπο ο υπολογισμός πιθανοτήτων, ποσοστημορίων και η παραγωγή τυχαίων αριθμών από μεγάλο αριθμό γνωστών κατανομών πιθανότητας. Ο γενικός τρόπος σύνταξης της εντολής είναι μέσω της ονομασίας της κατανομής με πρόθεμα ένα κατάλληλο γράμμα που δείχνει τι πρέπει να υπολογιστεί από αυτή την κατανομή. Τα δυνατά προθέματα είναι: d για συνάρτηση πυκνότητας πιθανότητας για συνεχείς ή συνάρτηση μάζας πιθανότητας για διακριτές κατανομές, p για , q, r

7.2 Βασικές Στατιστικές Εντολές

Οι βασικές εντολές για στατιστική ανάλυση αναφέρονται στον υπολογισμό ποσοτικών μεγεθών περιγραφικής στατιστικής. Για ένα διάνυσμα που περιέχει ένα τυχαίο δείγμα από μια κατανομή μπορούμε να υπολογίσουμε Μέση τιμή (mean), Διασπορά (var), Τυπική Απόκλιση (sd), Διάμεσο (median), κλπ. Περίληψη των ποσοστημορίων του δείγματος μπορούμε να πάρουμε με την εντολή summary:

mean(ozone$ozone)
## [1] 42.0991
sd(ozone$ozone)
## [1] 33.27597
summary(ozone$ozone)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##     1.0    18.0    31.0    42.1    62.0   168.0

Αν υποθέσουμε ότι το δείγμα προέρχεται από κανονική κατανομή με άγνωστη μέση τιμή και διασπορά, με την εντολή t.test μπορούμε να κάνουμε έλεγχο \(t\) για τη μέση τιμή: \(H_0: \mu=\mu_0, H_1: \mu (=,>,<) \mu_0\), όπως επίσης και να υπολογίσουμε διαστήματα εμπιστοσύνης. Το επίπεδο σημαντικότητας \(\alpha\) ορίζεται μέσω της παραμέτρου conf.level=\(1-\alpha\). Η μορφή της εναλλακτικής δίνεται από την παράμετρο alternative, που μπορεί να πάρει τιμές “two.sided”, “less”, “greater”. Η υποτιθέμενη τιμή \(\mu_0\) δίνεται από την παράμετρο \(mu\).

t.test(ozone$ozone, mu=35, alternative = "two.sided")
## 
##  One Sample t-test
## 
## data:  ozone$ozone
## t = 2.2477, df = 110, p-value = 0.02659
## alternative hypothesis: true mean is not equal to 35
## 95 percent confidence interval:
##  35.83986 48.35834
## sample estimates:
## mean of x 
##   42.0991
t.test(ozone$ozone, mu=35, alternative = "greater")
## 
##  One Sample t-test
## 
## data:  ozone$ozone
## t = 2.2477, df = 110, p-value = 0.0133
## alternative hypothesis: true mean is greater than 35
## 95 percent confidence interval:
##  36.85984      Inf
## sample estimates:
## mean of x 
##   42.0991
t.test(ozone$ozone, mu=35, alternative = "less")
## 
##  One Sample t-test
## 
## data:  ozone$ozone
## t = 2.2477, df = 110, p-value = 0.9867
## alternative hypothesis: true mean is less than 35
## 95 percent confidence interval:
##      -Inf 47.33835
## sample estimates:
## mean of x 
##   42.0991

Για να υπολογίσουμε διάστημα εμπιστοσύνης μέσω της εντολής t.test δείτε παρακάτω στην ανάκτηση μεταβλητών από αποτελέσματα.

7.3 Μοντέλα

Για την εκτίμηση παραμέτρων σε π

7.4 Αποτελέσματα Ανάλυσης

7.5 Ανάκτηση Μεταβλητών από Αποτελέσματα

8 Plotting

8.1 Βασικές Εντολές Γραφημάτων

8.2 Παράμετροι

8.3 Αποθήκευση/Ανάκτηση Συνόλου Παραμέτρων

9 R Markdown για Δυναμικά Κείμενα, Τεκμηρίωση και Αναπαραξιμότητα Κώδικα

Όταν γίνεται μια ανάλυση δεδομένων στο R (ή και σε οποιοδήποτε άλλο λογισμικό), τις περισσότερες φορές τα αποτελέσματα της ανάλυσης πρέπει να ενσωματωθούν σε ένα κείμενο εργασίας (π.χ. μια εργασία μαθήματος, μια εργασία που θα υποβληθεί σε περιοδικό, μια διπλωματική εργασία κλπ.). Για να γίνει αυτό θα πρέπει τα αποτελέσματα, είτε αυτά είναι πίνακες κειμένου είτε σχήματα, να αποθηκεύονται σε κατάλληλα αρχεία και αυτά να εισάγονται στο κείμενο της εργασίας. Αυτή η διαδικασία πρέπει να επαλαναμβάνεται κάθε φορά που γίνεται μια αλλαγή στις παραμέτρους της ανάλυσης, με κίνδυνο παραλείψεων και λαθών στην εισαγωγή των κατάλληλων αρχείων.

Ανεξάρτητα από τη συγγραφή εργασιών, όταν για μια ανάλυση γράφονται μεγάλα τμήματα κώδικα, είναι σημαντικό να γίνεται επαρκής τεκμηρίωση (documentation) των προγραμμάτων για δύο βασικούς λόγους. Πρώτον, αν σε μεταγενέστερο χρόνο χρειαστεί να επαναληφθεί η ανάλυση ή να γίνουν κάποιες τροποποιήσεις στον κώδικα, είναι πολύ δύσκολο και χρονοβόρο να γίνει αυτό αν δεν υπάρχει τεκμηρίωση με επεξηγήσεις και οδηγίες για την εκτέλεση. Δεύτερο, ιδιαίτερα σε μεγάλες και πολύπλοκες μελέτες που υποβάλλονται για δημοσίευση σε επιστημονικά περιοδικά (και όχι μόνο), είναι απαραραίτητο να εξασφαλίζεται η αναπαραξιμότητα (reproducibility) των αναλύσεων που έχουν διεξαχθεί. Αυτό σημαίνει ότι ένας τρίτος θα πρέπει να μπορεί, αν έχει στη διάθεσή του τα προγράμματα και τα δεδομένα που έχουν χρησιμοποιηθεί, να μπορεί να αναπαραγάγει τα αποτελέσματα που έχουν δημοσιευθεί ή έχουν υποβληθεί για δημοσίευση. Για το λόγο πολλά περιοδικά δίνουν τη δυνατότητα στους συγγραφείς να αναρτούν τα προγράμματα που έχουν χρησιμοποιήσει σε κατάλληλα αποθετήρια για χρήση από την επιστημονική κοινότητα. Σε αυτή την περίπτωση είναι σημαντικό τα προγράμματα που αναρτώνται να είναι πλήρως τεκμηριωμένα και αναπαράξιμα. Για την τεκμηρίωση ενός προγράμματος ο πιο συνηθισμένος τρόπος είναι να γράφονται αναλυτικά σχόλια μέσα στα προγράμματα με επεξηγήσεις και οδηγίες για την εκτέλεση.

Ένας πολύ χρήσιμος και αποτελεσματικός μηχανισμός και για τα δύο παραπάνω θέματα είναι η διαδικασία R Markdown.

Το R Markdown είναι ένας τύπος αρχείου κειμένου με σχετικά απλές εντολές μορφοποίησης (βασισμένες στο γενικότερο πρότυπο Markdown) που συγγραφή δυναμικού κειμένου. Αυτό σημαίνει ότι στο κείμενο περιλαμβάνονται και τμήματα με εντολές στη γλώσσα R (ή και σε άλλες γλώσσες προγραμματισμού όπως η python). Όποτε αυτό το κείμενο “εκτελείται” από το Rstudio, τα τμήματα του κώδικα εκτελούνται και τα αποτελέσματα ενσωματώνονται μέσα στο κείμενο. Αυτό σημαίνει ότι αλλαγές είτε στο συνοδευτικό κείμενο της εργασίας είτε στους αντίστοιχους κώδικες γίνονται ενιαία στο ίδιο αρχείο Rmarkdown χωρίς να χρειάζεται κάθε φορά μεταφορά και επικόλληση των αποτελεσμάτων από το R στον αντίστοιχο επεξεργαστή κειμένου.

Για μια εισαγωγή στη γλώσα RMarkdown δείτε https://rmarkdown.rstudio.com/articles_intro.html

Μια σύντομη περίληψη των εντολών RMarkdown https://rstudio.com/wp-content/uploads/2016/03/rmarkdown-cheatsheet-2.0.pdf?_ga=2.48070951.1440147942.1611558223-216792143.1611045836

Σύντομος οδηγός https://rstudio.com/wp-content/uploads/2015/03/rmarkdown-reference.pdf?_ga=2.77958804.1440147942.1611558223-216792143.1611045836