Διαδρομές
/
R
R
/
Ασκήσεις
/
Δέντρα και Tibble-περιπέτειες
Δέντρα και Tibble-περιπέτειες

Δέντρα και Tibble-περιπέτειες

Άσκηση εκμάθησης

Εισαγωγή

Σε άλλα σημεία της ύλης, έχουμε δει διάφορους τύπους δεδομένων με διαφορετικά χαρακτηριστικά.

  • Τα ατομικά διανύσματα είναι μονοδιάστατα και ομοιογενή ως προς τον τύπο.
  • Οι λίστες είναι μονοδιάστατες και τα στοιχεία τους μπορούν να είναι ετερογενών τύπων.
  • Οι μήτρες και οι πίνακες είναι πολυδιάστατοι και ομοιογενείς.

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

Παραλλαγές dataframe

Με το πέρασμα των δεκαετιών, η 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

Τα tibbles αποτελούν βασικό κομμάτι του Tidyverse, οπότε πρόσθεσέ τα με library(tibble) ή library(tidyverse).

Δημιουργία ενός tibble

Με τον πιο απλό τρόπο, μπορούμε να χρησιμοποιήσουμε τη συνάρτηση tibble() για να ενώσουμε διανύσματα-στήλες, όπως με τη data.frame(). Ένα παράδειγμα αυτού φάνηκε σε προηγούμενη ενότητα.

Αν είναι πιο βολικό να εισάγεις τις τιμές ανά γραμμή, η αντίστοιχη συνάρτηση είναι η tribble().

Στην πράξη, υπάρχουν δεκάδες τρόποι να δημιουργήσεις tibbles, αφού αποτελούν την προεπιλεγμένη μορφή εξόδου για μια μεγάλη ποικιλία συναρτήσεων του Tidyverse.

Χειρισμός ενός tibble

Η Έννοια του Συναρτησιακού Προγραμματισμού συζήτησε τη βιβλιοθήκη 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   

Τροποποίηση ενός tibble

Πρώτη επιφύλαξη: η προεπιλεγμένη συμπεριφορά 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     

Οδηγίες

Ένας μεγάλος οπωρώνας κερασιών έχει συνήθως ένα μικρό απόθεμα από πλεονάζοντα δέντρα που θα ήθελε να προσφέρει ως ξυλεία. Αυτό βοηθάει να παραμείνει ο οπωρώνας υγιής και ταυτόχρονα προσφέρει επιπλέον έσοδα. Σου ζήτησαν να βοηθήσεις γράφοντας κώδικα για κάποιες λειτουργίες που θα βοηθήσουν στη διαδικασία επιλογής δέντρων όταν τηλεφωνήσει ένας υποψήφιος πελάτης.

Note

Η ενσωματωμένη βιβλιοθήκη 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.

1. Μετονόμασε μια στήλη

Δυστυχώς υπήρξε ένα λάθος καταχώρησης που δεν διορθώθηκε ποτέ και μια στήλη στο σύνολο δεδομένων ονομάζεται 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

2. Πρόσθεσε τις στήλες Girth και Weight

Η περίμετρος (γνωστή και ως μήκος περιφέρειας) είναι ακόμα μια χρήσιμη μέτρηση, όπως και το βάρος της ξυλείας. Η περίμετρος υπολογίζεται εύκολα με π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

3. Αντίγραφο του συνόλου δεδομένων για τον οπωρώνα

Για κάθε υποψήφιο πελάτη, ο οπωρώνας κρατάει ένα ειδικό αντίγραφο του συνόλου δεδομένων για να βοηθήσει στην πώληση. Αυτό το σύνολο δεδομένων έχει τις πιο σχετικές στήλες, 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

4. Αντίγραφο του συνόλου δεδομένων για τον πελάτη

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

Όρισε τη συνάρτηση 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
Επεξεργασία μέσω GitHub Ο σύνδεσμος ανοίγει σε νέο παράθυρο ή καρτέλα
R Exercism

Έτοιμος να ξεκινήσεις την άσκηση Δέντρα και Tibble-περιπέτειες;

Γράψου στο Exercism για να μάθεις και να κατακτήσεις R με 21 έννοιες111 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.