7  Εισαγωγή δεδομένων

7.1 Εισαγωγή

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

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

7.1.1 Προαπαιτούμενα

Σε αυτό το κεφάλαιο, θα μάθετε πώς να διαβάζετε απλά αρχεία στην R με το πακέτο readr, το οποίο αποτελεί μέρος του βασικού tidyverse.

7.2 Ανάγνωση δεδομένων από ένα αρχείο

Αρχικά, θα επικεντρωθούμε στον πιο συνηθισμένο ορθογώνιο τύπο αρχείου δεδομένων: το CSV, που είναι συντομογραφία για τιμές διαχωρισμένες με κόμμα (comma separated values). Παρακάτω βλέπουμε πώς μοιάζει ένα απλό αρχείο CSV. Η πρώτη γραμμή, που συνήθως ονομάζεται γραμμή κεφαλίδας, δίνει τα ονόματα των στηλών και οι ακόλουθες έξι γραμμές παρέχουν τα δεδομένα. Οι στήλες οριοθετούνται με κόμμα.

Student ID,Full Name,favourite.food,mealPlan,AGE
1,Sunil Huffmann,Strawberry yoghurt,Lunch only,4
2,Barclay Lynn,French fries,Lunch only,5
3,Jayendra Lyne,N/A,Breakfast and lunch,7
4,Leon Rossini,Anchovies,Lunch only,
5,Chidiegwu Dunkel,Pizza,Breakfast and lunch,five
6,Güvenç Attila,Ice cream,Lunch only,6

Ο Πίνακας 7.1 δείχνει μία αναπαράσταση των ίδιων δεδομένων ως πίνακα.

Πίνακας 7.1: Data from the students.csv file as a table.
Student ID Full Name favourite.food mealPlan AGE
1 Sunil Huffmann Strawberry yoghurt Lunch only 4
2 Barclay Lynn French fries Lunch only 5
3 Jayendra Lyne N/A Breakfast and lunch 7
4 Leon Rossini Anchovies Lunch only NA
5 Chidiegwu Dunkel Pizza Breakfast and lunch five
6 Güvenç Attila Ice cream Lunch only 6

Μπορούμε να διαβάσουμε αυτό το αρχείο στην R χρησιμοποιώντας την read_csv(). Το πρώτο όρισμα είναι και το πιο σημαντικό: το μονοπάτι προς το αρχείο. Μπορείτε να φανταστείτε το μονοπάτι ως τη διεύθυνση του αρχείου: το αρχείο ονομάζεται students.csv και βρίσκεται στο φάκελο data.

students <- read_csv("data/students.csv")
#> Rows: 6 Columns: 5
#> ── Column specification ─────────────────────────────────────────────────────
#> Delimiter: ","
#> chr (4): Full Name, favourite.food, mealPlan, AGE
#> dbl (1): Student ID
#> 
#> ℹ Use `spec()` to retrieve the full column specification for this data.
#> ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Ο παραπάνω κώδικας θα λειτουργήσει εάν έχετε το αρχείο students.csv μέσα σε έναν φάκελο που ονομάζεται data στο project σας. Μπορείτε να κατεβάσετε το αρχείο students.csv από το https://pos.it/r4ds-students-csv ή μπορείτε να το διαβάσετε απευθείας από αυτήν τη διεύθυνση URL με:

students <- read_csv("https://pos.it/r4ds-students-csv")

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

7.2.1 Μία πρακτική συμβουλή

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

students
#> # A tibble: 6 × 5
#>   `Student ID` `Full Name`      favourite.food     mealPlan            AGE  
#>          <dbl> <chr>            <chr>              <chr>               <chr>
#> 1            1 Sunil Huffmann   Strawberry yoghurt Lunch only          4    
#> 2            2 Barclay Lynn     French fries       Lunch only          5    
#> 3            3 Jayendra Lyne    N/A                Breakfast and lunch 7    
#> 4            4 Leon Rossini     Anchovies          Lunch only          <NA> 
#> 5            5 Chidiegwu Dunkel Pizza              Breakfast and lunch five 
#> 6            6 Güvenç Attila    Ice cream          Lunch only          6

Στη στήλη favourite.food, υπάρχουν διάφορα τρόφιμα και, στη συνέχεια, η συμβολοσειρά χαρακτήρων N/A, η οποία θα έπρεπε να ήταν ένα πραγματικό NA, το οποίο η R θα αναγνωρίσει ως “μη διαθέσιμη τιμή”. Αυτό είναι κάτι που μπορούμε να αντιμετωπίσουμε χρησιμοποιώντας το όρισμα na. Από προεπιλογή, η read_csv() αναγνωρίζει μόνο κενές συμβολοσειρές ("") σε αυτό το σύνολο δεδομένων ως NAs, ενώ εμείς θέλουμε να αναγνωρίζει επίσης τη συμβολοσειρά χαρακτήρων "N/A".

students <- read_csv("data/students.csv", na = c("N/A", ""))

students
#> # A tibble: 6 × 5
#>   `Student ID` `Full Name`      favourite.food     mealPlan            AGE  
#>          <dbl> <chr>            <chr>              <chr>               <chr>
#> 1            1 Sunil Huffmann   Strawberry yoghurt Lunch only          4    
#> 2            2 Barclay Lynn     French fries       Lunch only          5    
#> 3            3 Jayendra Lyne    <NA>               Breakfast and lunch 7    
#> 4            4 Leon Rossini     Anchovies          Lunch only          <NA> 
#> 5            5 Chidiegwu Dunkel Pizza              Breakfast and lunch five 
#> 6            6 Güvenç Attila    Ice cream          Lunch only          6

Μπορεί επίσης να παρατηρήσετε ότι οι στήλες Student ID και Full Name περιβάλλονται από backticks. Αυτό συμβαίνει επειδή περιέχουν κενά, παραβιάζοντας τους συνήθεις κανόνες της R για τα επιτρεπτά ονόματα μεταβλητών - αυτά είναι μη συντακτικά ονόματα. Για να αναφερθείτε σε αυτές τις μεταβλητές, πρέπει να τις περιβάλλετε με `:

students |> 
  rename(
    student_id = `Student ID`,
    full_name = `Full Name`
  )
#> # A tibble: 6 × 5
#>   student_id full_name        favourite.food     mealPlan            AGE  
#>        <dbl> <chr>            <chr>              <chr>               <chr>
#> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only          4    
#> 2          2 Barclay Lynn     French fries       Lunch only          5    
#> 3          3 Jayendra Lyne    <NA>               Breakfast and lunch 7    
#> 4          4 Leon Rossini     Anchovies          Lunch only          <NA> 
#> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch five 
#> 6          6 Güvenç Attila    Ice cream          Lunch only          6

Μία εναλλακτική προσέγγιση είναι να χρησιμοποιήσετε τη συνάρτηση janitor::clean_names() για να χρησιμοποιήσετε κάποιους ευρετικούς κανόνες για να αντικαταστήσετε τα κενά με κάτω παύλες (snake case)1.

students |> janitor::clean_names()
#> # A tibble: 6 × 5
#>   student_id full_name        favourite_food     meal_plan           age  
#>        <dbl> <chr>            <chr>              <chr>               <chr>
#> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only          4    
#> 2          2 Barclay Lynn     French fries       Lunch only          5    
#> 3          3 Jayendra Lyne    <NA>               Breakfast and lunch 7    
#> 4          4 Leon Rossini     Anchovies          Lunch only          <NA> 
#> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch five 
#> 6          6 Güvenç Attila    Ice cream          Lunch only          6

Μία άλλη κοινή εργασία μετά την ανάγνωση δεδομένων είναι η εξέταση του τύπου των μεταβλητών. Για παράδειγμα, το meal_plan είναι μία κατηγορική μεταβλητή με ένα γνωστό σύνολο πιθανών τιμών, η οποία στην R θα πρέπει να αντιπροσωπεύεται ως παράγοντας:

students |>
  janitor::clean_names() |>
  mutate(meal_plan = factor(meal_plan))
#> # A tibble: 6 × 5
#>   student_id full_name        favourite_food     meal_plan           age  
#>        <dbl> <chr>            <chr>              <fct>               <chr>
#> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only          4    
#> 2          2 Barclay Lynn     French fries       Lunch only          5    
#> 3          3 Jayendra Lyne    <NA>               Breakfast and lunch 7    
#> 4          4 Leon Rossini     Anchovies          Lunch only          <NA> 
#> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch five 
#> 6          6 Güvenç Attila    Ice cream          Lunch only          6

Σημειώστε ότι οι τιμές στη μεταβλητή meal_plan έχουν παραμείνει ίδιες, αλλά ο τύπος της μεταβλητής που υποδεικνύεται κάτω από το όνομα της μεταβλητής έχει αλλάξει από χαρακτήρα (<chr>) σε παράγοντα (<fct>). Θα μάθετε περισσότερα σχετικά με τους παράγοντες στο Κεφάλαιο 16.

Προτού αναλύσετε αυτά τα δεδομένα, θα πρέπει πιθανώς να διορθώσετε τις στήλες age και id. Επί του παρόντος, η age είναι μία μεταβλητή χαρακτήρων επειδή μία από τις παρατηρήσεις πληκτρολογείται ως five αντί για το αριθμητικό 5. Θα συζητήσουμε τις λεπτομέρειες της επίλυσης αυτού του ζητήματος στο Κεφάλαιο 20.

students <- students |>
  janitor::clean_names() |>
  mutate(
    meal_plan = factor(meal_plan),
    age = parse_number(if_else(age == "five", "5", age))
  )

students
#> # A tibble: 6 × 5
#>   student_id full_name        favourite_food     meal_plan             age
#>        <dbl> <chr>            <chr>              <fct>               <dbl>
#> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only              4
#> 2          2 Barclay Lynn     French fries       Lunch only              5
#> 3          3 Jayendra Lyne    <NA>               Breakfast and lunch     7
#> 4          4 Leon Rossini     Anchovies          Lunch only             NA
#> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch     5
#> 6          6 Güvenç Attila    Ice cream          Lunch only              6

Μία νέα συνάρτηση εδώ είναι η if_else(), η οποία διαθέτει τρία ορίσματα. Το πρώτο όρισμα test πρέπει να είναι ένα λογικό διάνυσμα. Το αποτέλεσμα θα περιέχει την τιμή του δεύτερου ορίσματος, yes, όταν το test είναι TRUE και την τιμή του τρίτου ορίσματος, no, όταν είναι FALSE. Εδώ λέμε αν η μεταβλητή age είναι η συμβολοσειρά χαρακτήρων "five", κάνε την "5" και, αν όχι, άφησέ την ως την αντίστοιχη τιμή της age. Θα μάθετε περισσότερα για την if_else() και τα λογικά διανύσματα στο Κεφάλαιο 12.

7.2.2 Άλλα ορίσματα

Υπάρχουν μερικά άλλα σημαντικά ορίσματα που πρέπει να αναφέρουμε και θα είναι πιο εύκολο να τα δείξουμε αν πρώτα σας δείξουμε ένα εύχρηστο κόλπο: η read_csv() μπορεί να διαβάσει συμβολοσειρές κειμένου που έχετε δημιουργήσει και μορφοποιήσει ως αρχείο CSV:

read_csv(
  "a,b,c
  1,2,3
  4,5,6"
)
#> # A tibble: 2 × 3
#>       a     b     c
#>   <dbl> <dbl> <dbl>
#> 1     1     2     3
#> 2     4     5     6

Συνήθως, η read_csv() χρησιμοποιεί την πρώτη γραμμή των δεδομένων ως ονόματα των στηλών, κάτι που αποτελεί μία πολύ κοινή πρακτική. Ωστόσο, δεν είναι ασυνήθιστο να περιλαμβάνονται μερικές γραμμές μεταδεδομένων στην κορυφή του αρχείου. Μπορείτε να χρησιμοποιήσετε το skip = n για να παραλείψετε τις πρώτες n γραμμές ή να χρησιμοποιήσετε το comment = "#" για να απορρίψετε όλες τις γραμμές που ξεκινούν με (π.χ.) #:

read_csv(
  "The first line of metadata
  The second line of metadata
  x,y,z
  1,2,3",
  skip = 2
)
#> # A tibble: 1 × 3
#>       x     y     z
#>   <dbl> <dbl> <dbl>
#> 1     1     2     3

read_csv(
  "# A comment I want to skip
  x,y,z
  1,2,3",
  comment = "#"
)
#> # A tibble: 1 × 3
#>       x     y     z
#>   <dbl> <dbl> <dbl>
#> 1     1     2     3

Σε άλλες περιπτώσεις, τα δεδομένα ενδέχεται να μην έχουν ονόματα στηλών. Μπορείτε να χρησιμοποιήσετε το col_names = FALSE για να πείτε στη read_csv() να μην αντιμετωπίζει την πρώτη σειρά ως επικεφαλίδες και αντ’ αυτού να τις επισημαίνει διαδοχικά ξεκινώντας από το X1 έως το Xn:

read_csv(
  "1,2,3
  4,5,6",
  col_names = FALSE
)
#> # A tibble: 2 × 3
#>      X1    X2    X3
#>   <dbl> <dbl> <dbl>
#> 1     1     2     3
#> 2     4     5     6

Εναλλακτικά, μπορείτε να εισάγετε στο col_names ένα διάνυσμα χαρακτήρων που οι τιμές του οποίου θα χρησιμοποιηθούν ως ονόματα στηλών:

read_csv(
  "1,2,3
  4,5,6",
  col_names = c("x", "y", "z")
)
#> # A tibble: 2 × 3
#>       x     y     z
#>   <dbl> <dbl> <dbl>
#> 1     1     2     3
#> 2     4     5     6

Αυτά τα ορίσματα είναι όλα όσα πρέπει να γνωρίζετε για να διαβάσετε τα περισσότερα αρχεία CSV που θα συναντήσετε στην πράξη. (Για τα υπόλοιπα, θα πρέπει να επιθεωρήσετε προσεκτικά το αρχείο .csv που έχετε στα χέρια σας και να διαβάσετε το εγχειρίδιο της read_csv() όπου θα βρείτε πολλά ακόμη ορίσματα.)

7.2.3 Άλλοι τύποι αρχείων

Μόλις κατακτήσετε την read_csv(), η χρήση των άλλων συναρτήσεων του πακέτου readr γίνεται απλή. Είναι απλώς θέμα του να γνωρίζουμε ποια συνάρτηση να χρησιμοποίησοθμε:

  • Η read_csv2() διαβάζει αρχεία οριοθετημένα με ελληνικό ερωτηματικό. Αυτά χρησιμοποιούν ; αντί για , για να διαχωρίσουν πεδία και είναι κοινά σε χώρες που χρησιμοποιούν το , ως δείκτη των δεκαδικών.

  • Η read_tsv() διαβάζει αρχεία οριοθετημένα με στηλοθέτες.

  • Η read_delim() διαβάζει αρχεία με οποιονδήποτε οριοθέτη, προσπαθώντας να μαντέψει αυτόματα τον οριοθέτη εάν δεν τον καθορίσετε.

  • Η read_fwf() διαβάζει αρχεία σταθερού πλάτους. Μπορείτε να καθορίσετε τα πεδία με το πλάτος τους, χρησιμοποιώντας το fwf_widths() ή με τις θέσεις τους με το fwf_positions().

  • Η read_table() διαβάζει μία κοινή παραλλαγή αρχείων σταθερού πλάτους όπου οι στήλες χωρίζονται με απλό διάστημα.

  • Η read_log() διαβάζει αρχεία καταγραφής τύπου Apache.

7.2.4 Ασκήσεις

  1. Ποια συνάρτηση θα χρησιμοποιούσατε για να διαβάσετε ένα αρχείο όπου τα πεδία ήταν διαχωρισμένα με “|”;

  2. Εκτός από τα file, skip, και comment, ποια άλλα ορίσματα είναι κοινά στην read_csv() και την read_tsv();

  3. Ποια είναι τα πιο σημαντικά ορίσματα για την read_fwf();

  4. Ορισμένες φορές οι συμβολοσειρές σε ένα αρχείο CSV περιέχουν κόμματα. Για να μην προκαλούν προβλήματα, πρέπει να περιβάλλονται από έναν χαρακτήρα εισαγωγικού, όπως το " ή το '. Από προεπιλογή, η read_csv() υποθέτει ότι ο χαρακτήρας εισαγωγικού θα είναι ". Για να διαβάσετε το ακόλουθο κείμενο σε ένα πλαίσιο δεδομένων, ποιο όρισμα της read_csv() πρέπει να καθορίσετε;

    "x,y\n1,'a,b'"
  5. Προσδιορίστε ποιο είναι το πρόβλημα σε κάθε ένα από τα ακόλουθα ενσωματωμένα αρχεία CSV. Τι συμβαίνει όταν εκτελείτε τον κώδικα;

    read_csv("a,b\n1,2,3\n4,5,6")
    read_csv("a,b,c\n1,2\n1,2,3,4")
    read_csv("a,b\n\"1")
    read_csv("a,b\n1,2\na,b")
    read_csv("a;b\n1;3")
  6. Εξασκηθείτε στην αναφορά μη συντακτικών ονομάτων στο ακόλουθο πλαίσιο δεδομένων:

    1. Εξάγοντας τη μεταβλητή που ονομάζεται 1.
    2. Σχεδιάζοντας ένα διάγραμμα διασποράς της 1 έναντι της 2.
    3. Δημιουργώντας μιας νέας στήλης που ονομάζεται 3, η οποία περιέχει το αποτέλεσμα της διαίρεσης της 2 με την 1.
    4. Μετονομάζοντας τις στήλες σε one, two, και three.
    annoying <- tibble(
      `1` = 1:10,
      `2` = `1` * 2 + rnorm(length(`1`))
    )

7.3 Ελέγχοντας τον τύπο των στηλών

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

7.3.1 Εικάζοντας τύπους στηλών

Η readr χρησιμοποιεί ευρετικούς κανόνες για να προσδιορίσει τους τύπους στηλών. Για κάθε στήλη, κρατάει τις τιμές 1.000 γραμμών2 με ομοιόμορφη απόσταση από την πρώτη γραμμή μέχρι την τελευταία, αγνοώντας τις κενές τιμές. Στη συνέχεια εξετάζει τις ακόλουθες ερωτήσεις:

  • Το δείγμα περιέχει μόνο F, T, FALSE, ή TRUE (παραβλέποντας πεζά-κεφαλαία); Αν ναι, η στήλη είναι λογική.
  • Το δείγμα περιέχει μόνο αριθμούς (π.χ. 1, -4.5, 5e6, Inf); Αν ναι, η στήλη είναι αριθμητική.
  • Το δείγμα ταιριάζει με το πρότυπο ISO8601; Αν ναι, η αντίστοιχη στήλη περιέχει ημερομηνίες ή ημερομηνίες-ώρες. (Θα επιστρέψουμε στις ημερομηνίες-ώρες με περισσότερες λεπτομέρειες στην Ενότητα 17.2).
  • Διαφορετικά, η στήλη πρέπει να είναι τύπου συμβολοσειράς.

Μπορείτε να δείτε αυτή τη συμπεριφορά σε δράση σε αυτό το απλό παράδειγμα:

read_csv("
  logical,numeric,date,string
  TRUE,1,2021-01-15,abc
  false,4.5,2021-02-15,def
  T,Inf,2021-02-16,ghi
")
#> # A tibble: 3 × 4
#>   logical numeric date       string
#>   <lgl>     <dbl> <date>     <chr> 
#> 1 TRUE        1   2021-01-15 abc   
#> 2 FALSE       4.5 2021-02-15 def   
#> 3 TRUE      Inf   2021-02-16 ghi

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

7.3.2 Κενές τιμές, τύποι στηλών και προβλήματα

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

Πάρτε σαν παράδειγμα αυτό το απλό αρχείο CSV που περιέχει μόνο 1 στήλη:

simple_csv <- "
  x
  10
  .
  20
  30"

Αν το διαβάσουμε αυτό χωρίς πρόσθετα ορίσματα, το x μετατρέπεται σε στήλη χαρακτήρων:

read_csv(simple_csv)
#> # A tibble: 4 × 1
#>   x    
#>   <chr>
#> 1 10   
#> 2 .    
#> 3 20   
#> 4 30

Σε αυτήν την πολύ μικρή περίπτωση, μπορείτε εύκολα να δείτε την τιμή που λείπει ως .. Τι συμβαίνει όμως εάν έχετε χιλιάδες γραμμές με λίγες μόνο κενές τιμές και οι οποίες αντιπροσωπεύονται από ., διάσπαρτες ανάμεσα σε συμπληρωμένες τιμές; Μία προσέγγιση είναι να πείτε στην readr ότι το x είναι μία αριθμητική στήλη και στη συνέχεια να δείτε σε ποιο σημείο αποτυγχάνει. Αυτό μπορείτε να το κάνετε με το όρισμα col_types, το οποίο δέχεται μία λίστα με ονόματα, όπου τα ονόματα ταιριάζουν με τα ονόματα στηλών στο αρχείο CSV:

df <- read_csv(
  simple_csv, 
  col_types = list(x = col_double())
)
#> Warning: One or more parsing issues, call `problems()` on your data frame for
#> details, e.g.:
#>   dat <- vroom(...)
#>   problems(dat)

Τώρα η read_csv() αναφέρει ότι υπήρξε πρόβλημα και μας λέει ότι μπορούμε να μάθουμε περισσότερα χρησιμοποιώντας την problems():

problems(df)
#> # A tibble: 1 × 5
#>     row   col expected actual file                            
#>   <int> <int> <chr>    <chr>  <chr>                           
#> 1     3     1 a double .      /tmp/RtmpUeBGrj/file1e0a1a924f0a

Αυτό μας λέει ότι βρέθηκε ένα πρόβλημα στη γραμμή 3 και στήλη 1 όπου το πακέτο readr περίμενε έναν πραγματικό αριθμό αλλά εντόπισε ένα .. Αυτό υποδηλώνει ότι αυτό το σύνολο δεδομένων χρησιμοποιεί . για τιμές που λείπουν. Στη συνέχεια, ορίζουμε na = ".", και η αυτόματη εικασία επιτυγχάνει, δίνοντάς μας την αριθμητική στήλη που θέλουμε:

read_csv(simple_csv, na = ".")
#> # A tibble: 4 × 1
#>       x
#>   <dbl>
#> 1    10
#> 2    NA
#> 3    20
#> 4    30

7.3.3 Τύποι στηλών

Η readr παρέχει συνολικά εννέα τύπους στηλών που μπορούμε να χρησιμοποιήσουμε:

  • Οι col_logical() και col_double() διαβάζουν στήλες με λογικές τιμές και πραγματικούς αριθμούς αντίστοιχα. Η χρήση τους απαιτείται σχετικά σπάνια (εκτός από παραπάνω), μιας και το πακέτο readr συνήθως μαντεύει αντίστοιχες στήλες για εσάς.
  • Η col_integer() διαβάζει ακέραιους αριθμούς. Σπάνια κάνουμε διάκριση ανάμεσα σε ακέραιους και πραγματικούς αριθμούς σε αυτό το βιβλίο επειδή είναι λειτουργικά ισοδύναμοι, ωστόσο, η ρητή ανάγνωση ακεραίων μπορεί περιστασιακά να είναι χρήσιμη επειδή καταλαμβάνουν τη μισή μνήμη από τους πραγματικούς.
  • Η col_character() διαβάζει συμβολοσειρές. Αυτό μπορεί να είναι χρήσιμο για να προσδιορίσετε ρητά όταν έχετε μία στήλη που είναι ένα αριθμητικό αναγνωριστικό, δηλαδή μεγάλη σειρά ψηφίων που προσδιορίζει ένα αντικείμενο, αλλά δεν έχει νόημα να εφαρμόσετε μαθηματικές πράξεις σε αυτό. Παραδείγματα περιλαμβάνουν αριθμούς τηλεφώνου, αριθμούς κοινωνικής ασφάλισης, αριθμούς πιστωτικών καρτών κ.λπ.
  • Οι col_factor(), col_date() και col_datetime() δημιουργούν παράγοντες, ημερομηνίες και ημερομηνίες-ώρες αντίστοιχα. θα μάθετε περισσότερα για αυτούς όταν φτάσουμε σε αυτούς τους τύπους δεδομένων στα κεφάλαια Κεφάλαιο 16 και Κεφάλαιο 17.
  • Η col_number() χρησιμοποιείται για το διάβασμα αριθμητικών στηλών, αγνοώντας μη αριθμητικά στοιχεία, επιτρέποντας μεγαλύτερη ευελιξία, και είναι χρήσιμη για νομισματικές ισοδυναμίες. Περισσότερα γι’ αυτό θα μάθετε στο Κεφάλαιο 13.
  • Η col_skip() παρακάμπτει μία στήλη, ώστε να μην περιλαμβάνεται στο αποτέλεσμα, κάτι που μπορεί να είναι χρήσιμο για την επιτάχυνση της ανάγνωσης των δεδομένων, εάν έχετε ένα μεγάλο αρχείο CSV και θέλετε να χρησιμοποιήσετε μερικές μόνο από τις στήλες.

Είναι επίσης δυνατό να παρακάμψετε την προεπιλεγμένη στήλη μεταβαίνοντας από την list() σε cols() και θέτοντας το όρισμα .default:

another_csv <- "
x,y,z
1,2,3"

read_csv(
  another_csv, 
  col_types = cols(.default = col_character())
)
#> # A tibble: 1 × 3
#>   x     y     z    
#>   <chr> <chr> <chr>
#> 1 1     2     3

Μία άλλη χρήσιμη βοηθητική συνάρτηση είναι η cols_only() που θα διαβάζει μόνο στις στήλες που καθορίζετε:

read_csv(
  another_csv,
  col_types = cols_only(x = col_character())
)
#> # A tibble: 1 × 1
#>   x    
#>   <chr>
#> 1 1

7.4 Διαβάζοντας δεδομένα από πολλαπλά αρχεία

Μερικές φορές τα δεδομένα σας χωρίζονται σε πολλά αρχεία αντί να περιέχονται σε ένα μόνο αρχείο. Για παράδειγμα, μπορεί να έχετε δεδομένα πωλήσεων για πολλούς μήνες, με τα δεδομένα κάθε μήνα σε ξεχωριστό αρχείο: 01-sales.csv για τον Ιανουάριο, 02-sales.csv για το Φεβρουάριο και 03-sales.csv για το Μάρτιο. Με την read_csv() μπορείτε να διαβάσετε αυτά τα δεδομένα ταυτόχρονα και να τα στοιβάξετε το ένα πάνω στο άλλο, δημιουργώντας ένα συγκεντρωτικό πλαίσιο δεδομένων.

sales_files <- c("data/01-sales.csv", "data/02-sales.csv", "data/03-sales.csv")
read_csv(sales_files, id = "file")
#> # A tibble: 19 × 6
#>   file              month    year brand  item     n
#>   <chr>             <chr>   <dbl> <dbl> <dbl> <dbl>
#> 1 data/01-sales.csv January  2019     1  1234     3
#> 2 data/01-sales.csv January  2019     1  8721     9
#> 3 data/01-sales.csv January  2019     1  1822     2
#> 4 data/01-sales.csv January  2019     2  3333     1
#> 5 data/01-sales.csv January  2019     2  2156     9
#> 6 data/01-sales.csv January  2019     2  3987     6
#> # ℹ 13 more rows

Για ακόμη μία φορά, ο παραπάνω κώδικας θα δουλέψει εάν έχετε τα CSV αρχεία σας σε έναν data φάκελο στο project σας. Μπορείτε να κατεβάσετε αυτά τα αρχεία από τα https://pos.it/r4ds-01-sales, https://pos.it/r4ds-02-sales, και https://pos.it/r4ds-03-sales ή μπορείτε να διαβάσετε απευθείας, όπως φαίνεται παρακάτω:

sales_files <- c(
  "https://pos.it/r4ds-01-sales",
  "https://pos.it/r4ds-02-sales",
  "https://pos.it/r4ds-03-sales"
)
read_csv(sales_files, id = "file")

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

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

sales_files <- list.files("data", pattern = "sales\\.csv$", full.names = TRUE)
sales_files
#> [1] "data/01-sales.csv" "data/02-sales.csv" "data/03-sales.csv"

7.5 Γράφοντας δεδομένα σε ένα αρχείο

Η readr διαθέτει επίσης δύο χρήσιμες λειτουργίες για την εγγραφή δεδομένων πίσω στο δίσκο: την write_csv() και την write_tsv(). Τα πιο σημαντικά ορίσματα σε αυτές τις συναρτήσεις είναι το x (το πλαίσιο δεδομένων προς αποθήκευση) και το file (η τοποθεσία που θα χρησιμοποιηθεί για την αποθήκευση). Μπορείτε επίσης να καθορίσετε πώς γράφονται οι τιμές που λείπουν με το όρισμα na, και εάν θέλετε να προσαρτήσετε το πλαίσιο δεδομένων σας σε ένα υπάρχον αρχείο.

write_csv(students, "students.csv")

Τώρα ας διαβάσουμε ξανά αυτό το αρχείο csv. Σημειώστε ότι οι πληροφορίες τύπου μεταβλητής που μόλις ρυθμίσατε χάνονται όταν τις αποθηκεύετε στο CSV, επειδή ξεκινάτε ξανά με την ανάγνωση από ένα αρχείο απλού κειμένου:

students
#> # A tibble: 6 × 5
#>   student_id full_name        favourite_food     meal_plan             age
#>        <dbl> <chr>            <chr>              <fct>               <dbl>
#> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only              4
#> 2          2 Barclay Lynn     French fries       Lunch only              5
#> 3          3 Jayendra Lyne    <NA>               Breakfast and lunch     7
#> 4          4 Leon Rossini     Anchovies          Lunch only             NA
#> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch     5
#> 6          6 Güvenç Attila    Ice cream          Lunch only              6
write_csv(students, "students-2.csv")
read_csv("students-2.csv")
#> # A tibble: 6 × 5
#>   student_id full_name        favourite_food     meal_plan             age
#>        <dbl> <chr>            <chr>              <chr>               <dbl>
#> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only              4
#> 2          2 Barclay Lynn     French fries       Lunch only              5
#> 3          3 Jayendra Lyne    <NA>               Breakfast and lunch     7
#> 4          4 Leon Rossini     Anchovies          Lunch only             NA
#> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch     5
#> 6          6 Güvenç Attila    Ice cream          Lunch only              6

Αυτό καθιστά τα CSV αρχεία λίγο αναξιόπιστα για την προσωρινή αποθήκευση ενδιάμεσων αποτελεσμάτων — θα πρέπει να δημιουργείτε εκ νέου τις προδιαγραφές των στηλών κάθε φορά που φορτώνετε τα δεδομένα. Υπάρχουν δύο βασικές εναλλακτικές:

  1. Οι write_rds() και read_rds() είναι ομοιόμορφα περιτυλίγματα κώδικα (wrappers) γύρω από τις βασικές συναρτήσεις readRDS() και saveRDS(). Αυτές αποθηκεύουν δεδομένα στην προσαρμοσμένη δυαδική μορφή της R που ονομάζεται RDS. Αυτό σημαίνει ότι όταν φορτώνετε ξανά το αντικείμενο, φορτώνετε το ακριβώς το ίδιο αντικείμενο R που αποθηκεύσατε.

    write_rds(students, "students.rds")
    read_rds("students.rds")
    #> # A tibble: 6 × 5
    #>   student_id full_name        favourite_food     meal_plan             age
    #>        <dbl> <chr>            <chr>              <fct>               <dbl>
    #> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only              4
    #> 2          2 Barclay Lynn     French fries       Lunch only              5
    #> 3          3 Jayendra Lyne    <NA>               Breakfast and lunch     7
    #> 4          4 Leon Rossini     Anchovies          Lunch only             NA
    #> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch     5
    #> 6          6 Güvenç Attila    Ice cream          Lunch only              6
  2. Το πακέτο arrow σας επιτρέπει να διαβάζετε και να γράφετε αρχεία parquet, μία γρήγορη μορφή δυαδικού αρχείου που μπορεί να διαμοιραστεί σε διάφορες γλώσσες προγραμματισμού. Θα επιστρέψουμε στο arrow σε μεγαλύτερο βάθος στο Κεφάλαιο 22.

    library(arrow)
    write_parquet(students, "students.parquet")
    read_parquet("students.parquet")
    #> # A tibble: 6 × 5
    #>   student_id full_name        favourite_food     meal_plan             age
    #>        <dbl> <chr>            <chr>              <fct>               <dbl>
    #> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only              4
    #> 2          2 Barclay Lynn     French fries       Lunch only              5
    #> 3          3 Jayendra Lyne    NA                 Breakfast and lunch     7
    #> 4          4 Leon Rossini     Anchovies          Lunch only             NA
    #> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch     5
    #> 6          6 Güvenç Attila    Ice cream          Lunch only              6

Τα αρχεία parquet τείνουν να είναι πολύ πιο γρήγορα από τα RDS και μπορεί να χρησιμοποιηθούν και εκτός της R, αλλά απαιτούν το πακέτο arrow.

7.6 Εισαγωγή δεδομένων

Μερικές φορές θα χρειαστεί να συναρμολογήσετε ένα tibble “με το χέρι” κάνοντας μία μικρή εισαγωγή δεδομένων στο αρχείο R κώδικα σας. Υπάρχουν δύο χρήσιμες συναρτήσεις που σας βοηθούν να το κάνετε αυτό, οι οποίες διαφέρουν ως προς το αν σχεδιάζετε το tibble κατά στήλες ή κατά γραμμές. Η tibble() λειτουργεί ανά στήλες:

tibble(
  x = c(1, 2, 5), 
  y = c("h", "m", "g"),
  z = c(0.08, 0.83, 0.60)
)
#> # A tibble: 3 × 3
#>       x y         z
#>   <dbl> <chr> <dbl>
#> 1     1 h      0.08
#> 2     2 m      0.83
#> 3     5 g      0.6

Η διάταξη των δεδομένων ανά στήλες μπορεί να δυσκολέψει τον τρόπο με τον οποίο συσχετίζονται οι γραμμές, επομένως μία εναλλακτική είναι η tribble(), συντομογραφία για το transposed tibble, η οποία σας επιτρέπει να τοποθετήσετε τα δεδομένα σας γραμμή προς γραμμή. Η tribble() προσαρμόζεται για την εισαγωγή δεδομένων στον κώδικα: οι επικεφαλίδες στηλών ξεκινούν με ~ και οι εγγραφές διαχωρίζονται με κόμμα. Αυτό καθιστά δυνατή τη διάταξη μικρών ποσοτήτων δεδομένων σε μία ευανάγνωστη μορφή:

tribble(
  ~x, ~y, ~z,
  1, "h", 0.08,
  2, "m", 0.83,
  5, "g", 0.60
)
#> # A tibble: 3 × 3
#>       x y         z
#>   <dbl> <chr> <dbl>
#> 1     1 h      0.08
#> 2     2 m      0.83
#> 3     5 g      0.6

7.7 Σύνοψη

Σε αυτό το κεφάλαιο, μάθατε πώς να φορτώνετε αρχεία CSV με την read_csv() και να κάνετε τη δική σας εισαγωγή δεδομένων με τις tibble() και tribble(). Έχετε μάθει πώς λειτουργούν τα αρχεία csv, μερικά από τα προβλήματα που ενδέχεται να αντιμετωπίσετε και πώς να τα ξεπεράσετε. Θα επανέλθουμε στην εισαγωγή δεδομένων μερικές ακόμη φορές σε αυτό το βιβλίο: το Κεφάλαιο 20 θα σας δείξει πώς να φορτώνετε δεδομένα από το Excel και τα Google Sheets, το Κεφάλαιο 21 θα σας δείξει πώς να φορτώνετε δεδομένα από βάσεις δεδομένων, το Κεφάλαιο 22 από αρχεία parquet, το Κεφάλαιο 23 από JSON και το Κεφάλαιο 24 από ιστοτόπους.

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


  1. Το πακέτο janitor δεν αποτελεί μέρος του tidyverse, αλλά προσφέρει εύχρηστες λειτουργίες για τον καθαρισμό δεδομένων και λειτουργεί καλά σε ροές εργασιών σε δεδομένα που χρησιμοποιούν |>.↩︎

  2. Μπορείτε να παρακάμψετε την προεπιλογή των 1.000 γραμμών με το όρισμα guess_max.↩︎