Σε άλλα σημεία της ύλης, έχουμε δει διάφορους τύπους δεδομένων με διαφορετικά χαρακτηριστικά.
Αυτή η Έννοια θα εξετάσει τρόπους αποθήκευσης πολυδιάστατων, ετερογενών δεδομένων. Στην πράξη, τα περισσότερα δεδομένα του πραγματικού κόσμου είναι έτσι, οπότε τώρα φτάνουμε στην καρδιά του τρόπου με τον οποίο χρησιμοποιείται (κατά κύριο λόγο) η R στην πράξη.
Με το πέρασμα των δεκαετιών, η R έχει προσθέσει πολλούς τύπους δεδομένων για τη διαχείριση πινακοποιημένων δεδομένων.
Αυτή η ύλη θα επικεντρωθεί κυρίως στα tibbles, αλλά είναι χρήσιμο να γνωρίζεις τις εναλλακτικές.
data.frame
Στη Base R, ένα data.frame είναι μια list από vectors ίσου μήκους.
Μπορείς να το σκεφτείς ως έναν ορθογώνιο πίνακα δεδομένων, όπου κάθε στήλη είναι ομοιογενής, αλλά κάθε γραμμή μπορεί (και συνήθως συμβαίνει) να περιέχει διαφορετικούς τύπους δεδομένων.
Ένα παράδειγμα που το δείχνει αυτό:
# create the column vectors
languages <- c("Fortran", "R", "Python", "Julia")
created <- c(1957, 1993, 1991, 2012)
has.syllabus <- c(FALSE, TRUE, TRUE, TRUE)
# join columns to create the dataframe
df <- data.frame(languages, created, has.syllabus)
df
#> languages created has.syllabus
#> 1 Fortran 1957 FALSE
#> 2 R 1993 TRUE
#> 3 Python 1991 TRUE
#> 4 Julia 2012 TRUE
Έχουμε μια στήλη με συμβολοσειρές χαρακτήρων, μια στήλη με αριθμούς και μια στήλη με booleans. Σε μεγαλύτερη κλίμακα, αυτός είναι ένας διαισθητικός τρόπος να αναπαραστήσουμε πολλές συλλογές δεδομένων του πραγματικού κόσμου.
tibble
Ο σχεδιασμός του data.frame είναι παλιός.
Η εμπειρία πολλών δεκαετιών, μαζί με τις αλλαγές στον τρόπο που χρησιμοποιείται η R, οδήγησε σε έναν επανασχεδιασμό, δημιουργώντας μια εκσυγχρονισμένη εναλλακτική στο Tidyverse: τα tibbles.
Σε σύγκριση με τη Base R, τα tibbles έχουν:
Με λίγα λόγια, ένα tibble στοχεύει να "κάνει λιγότερα και να παραπονιέται περισσότερο", κάτι που περιγράφεται και ως "τεμπέλικο και γκρινιάρικο".
Οι τύποι είναι συνήθως εναλλάξιμοι: κάθε συνάρτηση που δέχεται ένα data.frame θα δεχτεί και ένα tibble, και αντίστροφα.
Οι μετατροπές μεταξύ των τύπων είναι εύκολες, με τις as_tibble(df) και as.data.frame(tbl).
Για νέα δουλειά, η χρήση tibbles πιθανότατα θα σε βοηθήσει να γράψεις πιο ανθεκτικό κώδικα.
Ωστόσο, ο παλιός κώδικας και τα παλιά δεδομένα αφθονούν στον κόσμο της R, οπότε το data.frame είναι πιθανό να παραμείνει διαδεδομένο για πολύ καιρό.
# column vectors are same as for data.frame
library(tibble)
tbl <- tibble(languages, created, has.syllabus)
tbl
# A tibble: 4 × 3
#> languages created has.syllabus
#> <chr> <dbl> <lgl>
#> 1 Fortran 1957 FALSE
#> 2 R 1993 TRUE
#> 3 Python 1991 TRUE
#> 4 Julia 2012 TRUE
Πρόσεξε την προεπιλεγμένη μορφή εκτύπωσης: η γραμμή σχολίου με τις διαστάσεις εκτυπώνεται αυτόματα, ενώ εμφανίζονται και οι τύποι των στηλών.
Τα tibbles αποτελούν βασικό κομμάτι του Tidyverse, οπότε πρόσθεσέ τα με library(tibble) ή library(tidyverse).
Με τον πιο απλό τρόπο, μπορούμε να χρησιμοποιήσουμε τη συνάρτηση tibble() για να ενώσουμε διανύσματα-στήλες, όπως με τη data.frame().
Ένα παράδειγμα αυτού φάνηκε σε προηγούμενη ενότητα.
Αν είναι πιο βολικό να εισάγεις τις τιμές ανά γραμμή, η αντίστοιχη συνάρτηση είναι η tribble().
Στην πράξη, υπάρχουν δεκάδες τρόποι να δημιουργήσεις tibbles, αφού αποτελούν την προεπιλεγμένη μορφή εξόδου για μια μεγάλη ποικιλία συναρτήσεων του Tidyverse.
Η Έννοια του Συναρτησιακού Προγραμματισμού συζήτησε τη βιβλιοθήκη purrr για τον χειρισμό διανυσμάτων και λιστών (μονοδιάστατων δομών δεδομένων).
Για τα dataframes (είτε παραδοσιακά είτε tibbles), η βιβλιοθήκη που θα χρησιμοποιήσεις είναι το dplyr.
Τα dataframes, συμπεριλαμβανομένων των tibbles, μπορούν να αντιμετωπιστούν ως λίστες από διανύσματα-στήλες, οπότε η αναφορά σε θέση της λίστας ανακτά μια συγκεκριμένη στήλη.
tbl
# A tibble: 4 × 3
#> languages created has.syllabus
#> <chr> <dbl> <lgl>
#> 1 Fortran 1957 FALSE
#> 2 R 1993 TRUE
#> 3 Python 1991 TRUE
#> 4 Julia 2012 TRUE
tbl$created
#> [1] 1957 1993 1991 2012
Μπορούμε επίσης να αναφερθούμε σε ένα dataframe με θέσεις, με τον τρόπο που το κάνουμε σε έναν πίνακα.
tbl[c(2, 4), 1:2]
# A tibble: 2 × 2
#> languages created
#> <chr> <dbl>
#> 1 R 1993
#> 2 Julia 2012
Στη σύγχρονη R με το οικοσύστημα του Tidyverse, οι συναρτήσεις του dplyr είναι γενικά πιο ευέλικτες και βολικές, και θα είναι το επίκεντρο για το υπόλοιπο αυτής της Έννοιας.
Πάρε μία μόνο στήλη με την pull() δίνοντας το όνομα ή τον αύξοντα αριθμό (οι αρνητικοί αριθμοί μετρούν από τα δεξιά προς τα αριστερά).
tbl |> pull(created)
#> [1] 1957 1993 1991 2012
Αυτό δίνει το ίδιο αποτέλεσμα με το tbl$created, αλλά με μια συνάρτηση φιλική προς το pipeline.
Για να πάρεις πολλές στήλες, η κατάλληλη συνάρτηση είναι η select(), η οποία είναι εξαιρετικά ευέλικτη.
Πάρε (ή απόρριψε) στήλες με βάση ιδιότητες του ονόματός τους ή του τύπου τους.
# Range with position and/or name
tbl |> select(1:created)
# A tibble: 4 × 2
#> languages created
#> <chr> <dbl>
#> 1 Fortran 1957
#> 2 R 1993
#> 3 Python 1991
#> 4 Julia 2012
# Use type of column
tbl |> select(where(is.numeric))
# A tibble: 4 × 1
#> created
#> <dbl>
#> 1 1957
#> 2 1993
#> 3 1991
#> 4 2012
Επιτρέπονται πολλαπλά κριτήρια, με τους τελεστές Boolean &, | και ! (και, ή, όχι).
Τόση δύναμη φαίνεται υπερβολική για το μικρό μας dataframe με γλώσσες.
Ευτυχώς, το tibble starwars περιλαμβάνεται στο dplyr και μας δίνει κάτι μεγαλύτερο για εξάσκηση.
# pick a subset of columns
starwars |>
select(name | ends_with("color")) |>
head(5)
# A tibble: 5 × 4
#> name hair_color skin_color eye_color
#> <chr> <chr> <chr> <chr>
#> 1 Luke Skywalker blond fair blue
#> 2 C-3PO NA gold yellow
#> 3 R2-D2 NA white, blue red
#> 4 Darth Vader none white yellow
#> 5 Leia Organa brown light brown
Πάρε τις γραμμές που ταιριάζουν με κάποια κριτήρια με τη filter(), ή εξαίρεσέ τες με τη filter_out().
starwars |>
select(name:mass) |>
filter(between(height, 150, 165) & !is.na(mass))
# A tibble: 4 × 3
#> name height mass
#> <chr> <int> <dbl>
#> 1 Leia Organa 150 49
#> 2 Beru Whitesun Lars 165 75
#> 3 Nien Nunb 160 68
#> 4 Ben Quadinaros 163 65
Τα κριτήρια φιλτραρίσματος μπορούν να είναι οσοδήποτε πολύπλοκα, αλλά βασίζονται πάντα στο περιεχόμενο των γραμμών.
Αν γνωρίζουμε τους αριθμούς των γραμμών, μπορούμε να χρησιμοποιήσουμε διάφορες συναρτήσεις slice().
starwars |>
select(name | homeworld) |>
slice(20:25)
# A tibble: 6 × 2
#> name homeworld
#> <chr> <chr>
#> 1 Palpatine Naboo
#> 2 Boba Fett Kamino
#> 3 IG-88 NA
#> 4 Bossk Trandosha
#> 5 Lando Calrissian Socorro
#> 6 Lobot Bespin
Πρώτη επιφύλαξη: η προεπιλεγμένη συμπεριφορά copy-on-modify σημαίνει ότι το αρχικό tibble συνήθως μένει αμετάβλητο.
Οι περισσότερες τροποποιήσεις εφαρμόζονται ανά στήλη.
Τα ονόματα των στηλών μπορούν να αλλάξουν με τη rename(newname = oldname), ή με τη rename_with() για να εφαρμόσεις μια συνάρτηση.
Πρόσεξε τη σύνταξη μέσα στη rename().
Το περιεχόμενο της στήλης oldname δεσμεύεται στο όνομα newname, εξ ου και η σειρά.
Η σειρά των στηλών μπορεί να αλλάξει με τη relocate().
Οι καθορισμένες στήλες μετακινούνται από προεπιλογή στην πιο αριστερή θέση, αλλά μπορείς να χρησιμοποιήσεις ένα όρισμα .before ή .after για ακριβέστερη τοποθέτηση.
sw <- starwars |> select(name:species) |> slice(1:4)
sw
# A tibble: 4 × 11
#> name height mass hair_color skin_color eye_color birth_year sex gender homeworld species
#> <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr> <chr> <chr>
#> 1 Luke Skywalker 172 77 blond fair blue 19 male masculine Tatooine Human
#> 2 C-3PO 167 75 NA gold yellow 112 none masculine Tatooine Droid
#> 3 R2-D2 96 32 NA white, blue red 33 none masculine Naboo Droid
#> 4 Darth Vader 202 136 none white yellow 41.9 male masculine Tatooine Human
sw |> relocate(c(species, homeworld), .after = name)
# A tibble: 4 × 11
#> name species homeworld height mass hair_color skin_color eye_color birth_year sex gender
#> <chr> <chr> <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr>
#> 1 Luke Skywalker Human Tatooine 172 77 blond fair blue 19 male masculine
#> 2 C-3PO Droid Tatooine 167 75 NA gold yellow 112 none masculine
#> 3 R2-D2 Droid Naboo 96 32 NA white, blue red 33 none masculine
#> 4 Darth Vader Human Tatooine 202 136 none white yellow 41.9 male masculine
Για μεγαλύτερες αλλαγές, η mutate() σου επιτρέπει να:
NULL.Είναι σαφές ότι η mutate() είναι ισχυρή, δυνητικά μπερδεμένη, και ένας λόγος να είμαστε πολύ ευγνώμονες για το copy-on-modify.
Δεν υπάρχει προφανής λόγος να σε απασχολεί ο Δείκτης Μάζας Σώματος των χαρακτήρων του Star Wars, αλλά για κάθε ενδεχόμενο:
starwars |>
select(c(name, species, height, mass)) |>
mutate(BMI = mass / (height / 100)^2) |>
head(4)
# A tibble: 4 × 5
#> name species height mass BMI
#> <chr> <chr> <int> <dbl> <dbl>
#> 1 Luke Skywalker Human 172 77 26.0
#> 2 C-3PO Droid 167 75 26.9
#> 3 R2-D2 Droid 96 32 34.7
#> 4 Darth Vader Human 202 136 33.3
Οι πράξεις ανά γραμμή είναι λιγότερο συχνές όταν τροποποιείς ένα μεμονωμένο tibble (η συγχώνευση πολλών tibbles θα συζητηθεί σε μεταγενέστερη έννοια).
Μία εξαίρεση: η arrange() ταξινομεί τις γραμμές με βάση τις τιμές σε μία ή περισσότερες στήλες.
tbl
#> A tibble: 4 × 3
#> languages created has.syllabus
#> <chr> <dbl> <lgl>
#> 1 Fortran 1957 FALSE
#> 2 R 1993 TRUE
#> 3 Python 1991 TRUE
#> 4 Julia 2012 TRUE
tbl |> arrange(languages)
#> A tibble: 4 × 3
#> languages created has.syllabus
#> <chr> <dbl> <lgl>
#> 1 Fortran 1957 FALSE
#> 2 Julia 2012 TRUE
#> 3 Python 1991 TRUE
#> 4 R 1993 TRUE
Ένας μεγάλος οπωρώνας κερασιών έχει συνήθως ένα μικρό απόθεμα από πλεονάζοντα δέντρα που θα ήθελε να προσφέρει ως ξυλεία. Αυτό βοηθάει να παραμείνει ο οπωρώνας υγιής και ταυτόχρονα προσφέρει επιπλέον έσοδα. Σου ζήτησαν να βοηθήσεις γράφοντας κώδικα για κάποιες λειτουργίες που θα βοηθήσουν στη διαδικασία επιλογής δέντρων όταν τηλεφωνήσει ένας υποψήφιος πελάτης.
Η ενσωματωμένη βιβλιοθήκη datasets έχει πολλά σύνολα δεδομένων για πειραματισμό και αυτή η άσκηση χρησιμοποιεί το σύνολο δεδομένων trees.
Για να καλέσεις το σύνολο δεδομένων, απλώς χρησιμοποίησε το όνομα trees.
library(datasets) # loading is optional
trees |> head(3)
#> A data.frame: 3 × 3
#> Girth Height Volume
#> <dbl> <dbl> <dbl>
#> 1 8.3 70 10.3
#> 2 8.6 65 10.3
#> 3 8.8 63 10.2
Πρόσεξε τις τρεις στήλες: Girth, Height και Volume
Το σύνολο δεδομένων είναι αρχικά στη μορφή ενός data.frame, αλλά ίσως θελήσεις να δουλέψεις με αυτό ως tibble.
Δυστυχώς υπήρξε ένα λάθος καταχώρησης που δεν διορθώθηκε ποτέ και μια στήλη στο σύνολο δεδομένων ονομάζεται Girth, ενώ στην πραγματικότητα είναι η Diameter.
Όρισε ένα data.frame/tibble με το όνομα tree_data που διορθώνει το όνομα της στήλης Girth στο trees σε Diameter.
head(tree_data, 3)
#> A tibble: 3 × 3
#> Diameter Height Volume
#> <dbl> <dbl> <dbl>
#> 8.3 70 10.3
#> 8.6 65 10.3
#> 8.8 63 10.2
Η περίμετρος (γνωστή και ως μήκος περιφέρειας) είναι ακόμα μια χρήσιμη μέτρηση, όπως και το βάρος της ξυλείας.
Η περίμετρος υπολογίζεται εύκολα με πD, όπου D είναι η διάμετρος.
Το βάρος όλης της ξυλείας μπορεί να υπολογιστεί με ρV, όπου ρ = 35 είναι η πυκνότητα του ξύλου κερασιάς και V είναι ο όγκος της ξυλείας.
Όρισε τη συνάρτηση girth_n_weight(data, rnd_digits), η οποία δέχεται ένα dataframe και αριθμητικά ορίσματα.
Η συνάρτηση πρέπει να επιστρέφει ένα νέο dataframe με τις δύο επιπλέον στήλες: Girth και Weight, στρογγυλοποιημένες στον κατάλληλο αριθμό ψηφίων που δίνεται από το rnd_digits.
girth_n_weight(tree_data, 1) |> head(3)
#> A tibble: 3 × 5
#> Diameter Height Volume Girth Weight
#> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 8.3 70 10.3 26.1 360.5
#> 8.6 65 10.3 27.0 360.5
#> 8.8 63 10.2 27.6 357.0
Για κάθε υποψήφιο πελάτη, ο οπωρώνας κρατάει ένα ειδικό αντίγραφο του συνόλου δεδομένων για να βοηθήσει στην πώληση.
Αυτό το σύνολο δεδομένων έχει τις πιο σχετικές στήλες, Weight και Height, μετακινημένες στην αρχή και είναι ταξινομημένο κατά Weight.
Όρισε τη συνάρτηση orchard_copy(data, important_cols), η οποία δέχεται ένα dataframe ως είσοδο.
Αυτή πρέπει να επιστρέφει ένα νέο dataframe με το Weight και το Height μετακινημένα στην αρχή και τις γραμμές ταξινομημένες κατά Weight.
tree_data |> girth_n_weight() |> orchard_copy() |> head(3)
#> A tibble: 3 × 5
#> Weight Height Diameter Volume Girth
#> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 357 63 9 10 28
#> 360 70 8 10 26
#> 360 65 9 10 27
Κάθε υποψήφιος πελάτης θα πάρει μια προσωπική έκδοση του συνόλου δεδομένων με βάση τις προτιμήσεις του. Για τον σκοπό αυτό, ο πελάτης μπορεί να ορίσει: το ελάχιστο ύψος, το μέγιστο ύψος και το μέγιστο βάρος οποιουδήποτε μεμονωμένου δέντρου.
Όρισε τη συνάρτηση customer_copy(data, min_height, max_height, max_weight).
Επίστρεψε ένα αντίγραφο του dataframe για τον πελάτη με τις στήλες Height, Weight, Diameter και Girth, με τις γραμμές που βρίσκονται εντός των περιορισμών.
tree_data |>
girth_n_weight(1) |>
orchard_copy() |>
customer_copy(65, 75, 1500) |>
head(3)
#> A tibble: 3 × 4
#> Height Weight Diameter Girth
#> <dbl> <dbl> <dbl> <dbl>
#> 70 360.5 8.3 26.1
#> 65 360.5 8.6 27.0
#> 66 546.0 11.0 34.6
Γράψου στο Exercism για να μάθεις και να κατακτήσεις R με 21 έννοιες111 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.