Ένα vector (τεχνικά ένα atomic vector) στη R είναι μια συλλογή τιμών του ίδιου type: συνήθως integer, numeric, character, logical; σπάνια complex ή raw.
Αποθηκεύονται συνεχόμενα στη μνήμη, παρόμοια με έναν πίνακα στη C.
Αν της δώσεις μικτές εισόδους, η R θα τις μετατρέψει αθόρυβα όλες σε έναν κοινό τύπο, συνήθως character, κάτι που μπορεί να οδηγήσει σε απροσδόκητα αποτελέσματα.
v <- c(2, 5.4, "txt")
v
#> [1] "2" "5.4" "txt"
typeof(v)
#> [1] "character"
Αν έχεις ήδη όλες τις τιμές, χρησιμοποίησε τη c() (συντομογραφία του "concatenate"):
x <- 10
v <- c(4, 7, x)
Η συνάρτηση c() είναι πολύ ευέλικτη και οι είσοδοί της μπορούν να περιλαμβάνουν άλλα διανύσματα και εύρη.
Η c() δέχεται οποιονδήποτε αριθμό και ποικιλία εισόδων και τα ισοπεδώνει σε ένα μόνο διάνυσμα.
Αν το διάνυσμα ξεκινά μικρό και μεγαλώνει πολύ κατά την εκτέλεση (για παράδειγμα, προσθέτοντας τιμές σε έναν βρόχο), μπορεί να είναι σημαντικά πιο γρήγορο να προεκχωρήσεις ένα μεγάλο διάνυσμα που μπορεί να γεμίσει με πραγματικά δεδομένα αργότερα:
v <- vector(length = 100)
Αυτό ελαχιστοποιεί την ανάγκη αντιγραφής του αντικειμένου: μια αργή και απαιτητική σε μνήμη λειτουργία για μεγάλα διανύσματα.
Για ένα εύρος διαδοχικών τιμών (αύξουσες ή φθίνουσες) μπορείς να χρησιμοποιήσεις τον συμβολισμό :
v <- 1:5 # equivalent to c(1, 2, 3, 4, 5)
w <- 10:7 # c(10, 9, 8, 7)
Στη R, τα εύρη δεν είναι ξεχωριστός τύπος: απλώς ένας σύντομος τρόπος να κατασκευάσεις ένα διάνυσμα.
Η συνάρτηση seq() προσφέρει λεπτότερο έλεγχο:
# specify step size
seq(2, 11, by = 3) # c(2, 5, 8, 11)
# specify number of values, evenly-spaced
seq(2.5, 4, length.out = 5) # c(2.500, 2.875, 3.250, 3.625, 4.000)
Η συνάρτηση rep() κάνει εύκολη τη δημιουργία επαναλαμβανόμενων μοτίβων:
count <- 3
rep(7, count)
#> [1] 7 7 7
rep(1:3, count)
#> [1] 1 2 3 1 2 3 1 2 3
rep(1:3, each = 2) # the "each" keyword is required in this case
#> [1] 1 1 2 2 3 3
Μερικά εύρη είναι προκαθορισμένα ως ενσωματωμένες σταθερές:
letters # LETTERS is the UPPERCASE equivalent
#> [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
month.name
#> [1] "January" "February" "March" "April" "May" "June" "July" "August"
#> [9] "September" "October" "November" "December"
month.abb
#> [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"
Οι πράξεις εφαρμόζονται αυτόματα στοιχείο προς στοιχείο κατά μήκος του διανύσματος. Οι βρόχοι, οι κατανοήσεις λιστών και οι αναδρομές που είναι συνηθισμένες σε άλλες γλώσσες δεν χρειάζονται και (για λόγους απόδοσης) καλό είναι να αποφεύγονται όπου είναι δυνατόν.
2 + 1:3 # c(3, 4, 5)
2 * 1:3 # c(2, 4, 6)
12 / 1:3 # c(12 6 4)
2 ^ (1:3) # c(2, 4, 8) : exponentiation, operator precedence order here needs ()
Λειτουργούν και ζεύγη διανυσμάτων, με τα στοιχεία να αντιμετωπίζονται ανά ζεύγος:
v <- 1:3
w <- 5:7
v + w
#> [1] 6 8 10
v * w
#> [1] 5 12 21
v ^ w
#> [1] 1 64 2187
Αυτό είναι πιο ισχυρό απ' ό,τι φαίνεται με την πρώτη ματιά, και θα υπάρχουν περισσότερα να εξερευνήσεις σε επόμενες έννοιες.
Όπως σε πολλές γλώσσες, μπορούμε να χρησιμοποιήσουμε τη θέση μέσα σε αγκύλες:
v <- 4:7
v[2]
#> [1] 5
v[2:4]
#> [1] 5 6 7
Αλλά προσοχή: η R χρησιμοποιεί από προεπιλογή αρίθμηση θέσεων με βάση το 1. Αυτό είναι συνηθισμένο σε επιστημονικές γλώσσες (Fortran, Matlab, Julia) αλλά διαφέρει από την αρίθμηση με βάση το 0 της C και των πολλών διαδόχων της.
Μπορούν να χρησιμοποιηθούν εύρη, όπως στο παραπάνω παράδειγμα αλλά και στην εξαιρετικά ευέλικτη συνάρτηση c():
v <- c(4, 7, 10)
w <- c(2, v[2:3], 11) # c(2, 7, 10, 11)
Τα vectors (όπως τα περισσότερα πράγματα στη R) μπορούν να αντιμετωπιστούν ως τροποποιήσιμα, χρησιμοποιώντας παρόμοια ευέλικτη σύνταξη για να γράψεις σε ένα ή πολλά στοιχεία:
v <- 1:10
v[2] <- 42
v[4:6] <- c(11, 12, 13)
v
#> [1] 1 42 3 11 12 13 7 8 9 10
Οι προγραμματιστές που είναι εξοικειωμένοι με άλλες γλώσσες μπορεί να υποθέσουν ότι το v[-1] είναι τρόπος να προσπελάσουν το τελευταίο στοιχείο ενός διανύσματος.
Λάθος!
Γι' αυτό χρησιμοποίησε τη tail().
v <- c(2, 4, 6) # defaults to last 6 entries
tail(v)
#> [1] 2 4 6
tail(v, n=1) # just the last entry
#> [1] 6
Οι αρνητικές θέσεις είναι στην πραγματικότητα τρόπος να αφαιρέσεις ένα στοιχείο:
v <- c(2, 4, 6)
v[-2]
#> [1] 2 6 # second element gone
Αυτό επιστρέφει ένα συντομότερο αντίγραφο του διανύσματος. Το ίδιο το v παραμένει αμετάβλητο.
Η εφαρμογή συγκρίσεων σε ένα διάνυσμα θα επιστρέψει ένα διάνυσμα από booleans ίδιου μήκους.
Αυτό μπορεί να είναι πολύ βολικό μέσα σε συναρτήσεις όπως οι all() και any():
v <- c(4, 7, 10)
v >= 6
#> [1] FALSE TRUE TRUE
all(v > 6)
#> [1] FALSE # not all elements match this condition
any(v > 6)
#> [1] TRUE # at least one element matches
Για πιο περίπλοκες λογικές εκφράσεις, είναι σημαντικό να χρησιμοποιείς τους τελεστές & και |, που λειτουργούν σε διανύσματα.
Οι διπλοί χαρακτήρες && και || προορίζονται μόνο για σύγκριση μεμονωμένων τιμών, όπως συζητείται στην [έννοια Booleans][concept-boolean].
v <- c(4, 7, 10)
v %% 2 == 0 & v > 5
#> [1] FALSE FALSE TRUE
Αυτό θα είναι ένα θέμα στο οποίο θα επανέλθουμε σε επόμενα θέματα. Για την ώρα, απλώς πρόσεξε τη μορφή της εξόδου στην κονσόλα:
1:20
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#> [18] 18 19 20
Οι γραμμές ξεκινούν με αριθμούς μέσα σε [ ] για να δείξουν τη θέση του πρώτου στοιχείου (χρήσιμο για μεγάλα διανύσματα που αναδιπλώνονται σε πολλές γραμμές).
Μα γιατί οι μεμονωμένες τιμές προηγούνται από το [1];
Δεν υπάρχουν πραγματικά βαθμωτές τιμές στη R!
Αυτό που φαίνεται σαν ένας μοναδικός αριθμός ή γράμμα είναι στην πραγματικότητα ένα διάνυσμα μήκους 1.
Έχουμε ήδη δει ίχνη αυτού στις έννοιες Βασικά και Booleans με αριθμητικούς και λογικούς τύπους.
Τα διανύσματα που περιγράφηκαν παραπάνω είναι πιο επίσημα γνωστά ως atomic vectors. Αυτό συμβαίνει επειδή μπορούν να περιέχουν μόνο απλές, αδιαίρετες (εξ ου και "atomic") τιμές όπως numeric, character, boolean. Πιο σύνθετες δομές μπορούν να σχηματίσουν recursive vectors, τα οποία θα παρουσιαστούν όταν συζητηθούν οι lists.