forked from hadley/r4ds
-
Notifications
You must be signed in to change notification settings - Fork 0
/
data-transform.qmd
882 lines (644 loc) · 60.7 KB
/
data-transform.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
# Μετασχηματισμός Δεδομένων {#sec-data-transform}
```{r}
#| echo: false
source("_common.R")
```
## Εισαγωγή
Η οπτικοποίηση είναι ένα σημαντικό εργαλείο για τη δημιουργία γνώσης, αλλά είναι σπάνιο να λαμβάνετε τα δεδομένα ακριβώς με τη σωστή μορφή που χρειάζεστε για να δημιουργήσετε το διάγραμμα που θέλετε.
Συχνά θα χρειαστεί να δημιουργήσετε μερικές νέες μεταβλητές ή συνόψεις για να απαντήσετε τις ερωτήσεις σας με τα δεδομένα σας ή ίσως απλώς θέλετε να μετονομάσετε τις μεταβλητές ή να αναδιατάξετε τις παρατηρήσεις για να κάνετε τα δεδομένα λίγο πιο εύκολα στο χειρισμό.
Θα μάθετε πώς να τα κάνετε όλα αυτά (και πολλά άλλα!) σε αυτό το κεφάλαιο, το οποίο θα σας δώσει μία εισαγωγή στον μετασχηματισμό δεδομένων χρησιμοποιώντας το πακέτο **dplyr** και ένα νέο σύνολο δεδομένων με πτήσεις που αναχώρησαν από τη Νέα Υόρκη το 2013.
Ο στόχος αυτού του κεφαλαίου είναι να σας δώσει μία επισκόπηση όλων των βασικών εργαλείων για τη μετατροπή ενός πλαισίου δεδομένων.
Θα ξεκινήσουμε με συναρτήσεις που εφαρμόζονται σε γραμμές και στη συνέχεια σε στήλες ενός πλαισίου δεδομένων και, στη συνέχεια, θα επιστρέψουμε για να μιλήσουμε περισσότερο για το pipe, ένα σημαντικό εργαλείο που χρησιμοποιείτε για να συνδυάσετε συναρτήσεις.
Στη συνέχεια θα εισαγάγουμε την ικανότητα εργασίας με ομάδες δεδομένων.
Θα τελειώσουμε το κεφάλαιο με μία μελέτη περίπτωσης που παρουσιάζει αυτές τις συναρτήσεις εν δράση και θα επανέλθουμε στις συναρτήσεις με περισσότερες λεπτομέρειες σε επόμενα κεφάλαια, καθώς θα αρχίζουμε να εξερευνούμε συγκεκριμένους τύπους δεδομένων (π.χ. αριθμούς, συμβολοσειρές, ημερομηνίες).
### Προαπαιτούμενα
Σε αυτό το κεφάλαιο θα επικεντρωθούμε στο πακέτο dplyr, ακόμη ένα βασικό μέλος του tidyverse.
Θα παρουσιάσουμε τις βασικές ιδέες χρησιμοποιώντας δεδομένα από το πακέτο nycflights13 και θα χρησιμοποιήσουμε το πακέτο ggplot2 για να μας βοηθήσει να κατανοήσουμε τα δεδομένα.
```{r}
#| label: setup
library(nycflights13)
library(tidyverse)
```
Παρατηρήστε προσεκτικά το μήνυμα συγκρούσεων που εκτυπώνεται όταν φορτώνετε το tidyverse.
Σας λέει ότι το πακέτο dplyr αντικαθιστά ορισμένες συναρτήσεις από το βασικό σύνολο λειτουργιών της R.
Εάν θέλετε να χρησιμοποιήσετε τη βασική έκδοση αυτών των συναρτήσεων μετά τη φόρτωση της dplyr, θα πρέπει να χρησιμοποιήσετε τα πλήρη ονόματά τους: `stats::filter()` και `stats::lag()`.
Μέχρι στιγμής έχουμε αγνοήσει από ποιο πακέτο προέρχεται μία συνάρτηση γιατί τις περισσότερες φορές δεν έχει σημασία.
Ωστόσο, η γνώση του πακέτου μπορεί να σας βοηθήσει να αναζητήσετε βοήθεια και να βρείτε σχετικές συναρτήσεις, οπότε όταν πρέπει να είμαστε ακριβείς σχετικά με το πακέτο από το οποίο προέρχεται μία συνάρτηση, θα χρησιμοποιήσουμε την ίδια σύνταξη με την R: `packagename::functionname()`.
### nycflights13
Για να εξερευνήσουμε τις βασικές συναρτήσεις της dplyr, θα χρησιμοποιήσουμε το σύνολο δεδομένων `nycflights13::flights`.
Αυτό περιέχει όλες τις `r format(nrow(nycflights13::flights), big.mark = ",")` πτήσεις που αναχώρησαν από την πόλη της Νέας Υόρκης το 2013.
Αυτά τα δεδομένα προέρχονται από το [Γραφείο Στατιστικών Μεταφορών](https://www.transtats.bts.gov/DL_SelectFields.aspx?gnoyr_VQ=FGJ&QO_fu146_anzr=b0-gvzr) των ΗΠΑ και τεκμηριώνονται στο `?flights`.
```{r}
flights
```
Το `flights` είναι ένα tibble, ένας ειδικός τύπος πλαισίου δεδομένων που χρησιμοποιείται από το tidyverse για να αποφευχθούν ορισμένες συχνές προκλήσεις.
Η πιο σημαντική διαφορά μεταξύ των tibbles και των πλαισίων δεδομένων είναι ο τρόπος εκτύπωσης των tibbles.
Τα πρώτα έχουν σχεδιαστεί για μεγάλα σύνολα δεδομένων, επομένως εμφανίζουν μόνο τις πρώτες λίγες σειρές και μόνο τις στήλες που χωρούν σε μία οθόνη.
Υπάρχουν μερικές επιλογές για να δείτε τα πάντα.
Εάν χρησιμοποιείτε το RStudio, το πιο βολικό είναι ίσως το `View(flights)`, το οποίο θα ανοίξει μία διαδραστική προβολή με δυνατότητα κύλισης και φιλτραρίσματος.
Διαφορετικά, μπορείτε να χρησιμοποιήσετε την `print(flights, width = Inf)` για να εμφανίσετε όλες τις στήλες ή να χρησιμοποιήσετε την `glimpse()`:
```{r}
glimpse(flights)
```
Και στις δύο όψεις, τα ονόματα των μεταβλητών ακολουθούνται από συντομογραφίες που σας λένε τον τύπο κάθε μεταβλητής: το `<int>` είναι συντομογραφία για ακέραιους αριθμούς, το `<dbl>` για πραγματικούς αριθμούς, το `<chr>` για χαρακτήρες (γνωστοί και ως συμβολοσειρές) και το `<dttm>` για ημερομηνίες-ώρα.
Όλα αυτά είναι σημαντικά επειδή οι χειρισμοί που μπορείτε να εφαρμόσετε σε μία στήλη εξαρτώνται πολύ από τον "τύπο" της.
### Βασικές αρχές της dplyr
Ακολουθούν οι κύριες συναρτήσεις της dplyr που θα σας επιτρέψουν να λύσετε τη συντριπτική πλειονότητα των προκλήσεων χειρισμού δεδομένων που θα συναντήσετε.
Αλλά πριν συζητήσουμε τις μεμονωμένες διαφορές τους, αξίζει να αναφέρουμε τι κοινό έχουν:
1. Το πρώτο τους όρισμα είναι πάντα ένα πλαίσιο δεδομένων.
2. Τα επόμενα ορίσματα συνήθως περιγράφουν σε ποιες στήλες θα λειτουργήσουν, χρησιμοποιώντας τα ονόματα των μεταβλητών (χωρίς εισαγωγικά).
3. Η έξοδος είναι πάντα ένα νέο πλαίσιο δεδομένων.
Επειδή κάθε συνάρτηση κάνει ένα πράγμα καλά, η επίλυση σύνθετων προβλημάτων συνήθως απαιτεί συνδυασμό πολλών συναρτήσεων, και αυτό θα το κάνουμε με το pipe, `|>`.
Θα συζητήσουμε το pipe περισσότερο στην @sec-the-pipe, αλλά εν συντομία, παίρνει ό,τι υπάρχει στα αριστερά του και το περνά στη συνάρτηση στα δεξιά του, έτσι ώστε το `x |> f(y)` να είναι ισοδύναμο με `f(x, y)`, και το `x |> f(y) |> g(z)` ισοδυναμεί με `g(f(x, y), z)`.
Ο ευκολότερος τρόπος για να περιγράψετε το pipe είναι με τη λέξη "τότε".
Αυτό καθιστά δυνατό να αποκτήσετε μία αίσθηση του παρακάτω κώδικα, παρόλο που δεν έχετε μάθει ακόμη τις λεπτομέρειες:
```{r}
#| eval: false
flights |>
filter(dest == "IAH") |>
group_by(year, month, day) |>
summarize(
arr_delay = mean(arr_delay, na.rm = TRUE)
)
```
Οι συναρτήσεις της dplyr οργανώνονται σε τέσσερις ομάδες με βάση το σε τι εφαρμόζονται: **γραμμές**, **στήλες**, **ομάδες** ή **πίνακες**.
Στις επόμενες ενότητες θα μάθετε τις πιο σημαντικές συναρτήσεις για γραμμές, στήλες και ομάδες και, στη συνέχεια, θα επιστρέψουμε στις συναρτήσεις join που λειτουργούν σε πίνακες στο @sec-joins.
Ας αρχίσουμε!
## Γραμμές
Οι πιο σημαντικές συναρτήσεις που λειτουργούν στις γραμμές ενός συνόλου δεδομένων είναι η `filter()`, η οποία αλλάζει το ποιες γραμμές θα παρουσιάζονται χωρίς να αλλάξει τη σειρά τους και η `arrange()`, η οποία αλλάζει τη σειρά των γραμμών χωρίς να αλλάζει ποιες υπάρχουν.
Και οι δύο συναρτήσεις επηρεάζουν μόνο τις γραμμές και οι στήλες παραμένουν αμετάβλητες.
Θα συζητήσουμε επίσης και την `distinct()` που βρίσκει γραμμές με μοναδικές τιμές αλλά σε αντίθεση με την `arrange()` και την `filter()` μπορεί επίσης να τροποποιήσει προαιρετικά τις στήλες.
### `filter()`
Η `filter()` σας επιτρέπει να διατηρείτε γραμμές με βάση τις τιμές των στηλών[^data-transform-1].
Το πρώτο όρισμα είναι το πλαίσιο δεδομένων.
Το δεύτερο και τα επόμενα ορίσματα είναι οι συνθήκες που πρέπει να ισχύουν για να διατηρηθεί η σειρά.
Για παράδειγμα, θα μπορούσαμε να βρούμε όλες τις πτήσεις που αναχώρησαν με καθυστέρηση μεγαλύτερη των 120 λεπτών (δύο ώρες):
[^data-transform-1]: Αργότερα, θα μάθετε για την οικογένεια `slice_*()` που σας επιτρέπει να επιλέγετε γραμμές με βάση τις θέσεις τους.
```{r}
flights |>
filter(dep_delay > 120)
```
Εκτός από το `>` (μεγαλύτερο από), μπορείτε να χρησιμοποιήσετε και το `>=` (μεγαλύτερο ή ίσο με), το`<` (μικρότερο από), το `<=`(μικρότερο από ή ίσο με), το `==` (ίσο με), και το `!=` (διαφορετικό).
Μπορείτε επίσης να συνδυάσετε συνθήκες με `&` ή `,` για να υποδείξετε το "και" (έλεγχος και για τις δύο συνθήκες) ή με `|` για να υποδείξετε το "ή" (έλεγχος για οποιαδήποτε από τις δύο συνθήκες):
```{r}
# Πτήσεις που αναχώρησαν την 1η Ιανουαρίου
flights |>
filter(month == 1 & day == 1)
# Πτήσεις που αναχώρησαν τον Ιανουάριο ή τον Φεβρουάριο
flights |>
filter(month == 1 | month == 2)
```
Υπάρχει μία χρήσιμη συντόμευση όταν συνδυάζετε τα `|` και `==`: το `%in%`.
Διατηρεί γραμμές όπου η μεταβλητή ισούται με μία από τις τιμές στα δεξιά:
```{r}
# Ένας πιο σύντομος τρόπος για να επιλέξετε πτήσεις που αναχώρησαν τον Ιανουάριο ή τον Φεβρουάριο
flights |>
filter(month %in% c(1, 2))
```
Θα επανέλθουμε σε αυτές τις συγκρίσεις και τους λογικούς τελεστές με περισσότερες λεπτομέρειες στο @sec-logicals.
Όταν εκτελείτε την `filter()`, το πακέτο dplyr εκτελεί τη λειτουργία φιλτραρίσματος, δημιουργώντας ένα νέο πλαίσιο δεδομένων και, στη συνέχεια, το εκτυπώνει.
Δεν τροποποιεί το υπάρχον σύνολο δεδομένων `flights`, επειδή οι συναρτήσεις της dplyr δεν τροποποιούν ποτέ τις εισόδους τους.
Για να αποθηκεύσετε το αποτέλεσμα, πρέπει να χρησιμοποιήσετε τον τελεστή ανάθεσης, `<-`:
```{r}
jan1 <- flights |>
filter(month == 1 & day == 1)
```
### Συχνά λάθη
Όταν ξεκινάτε με την R, το πιο εύκολο λάθος που μπορείτε να κάνετε είναι να χρησιμοποιήσετε το `=` αντί για το `==` όταν ελέγχετε για ισότητα.
Η `filter()` θα σας ενημερώσει όταν συμβεί αυτό:
```{r}
#| error: true
flights |>
filter(month = 1)
```
Άλλο ένα κοινό λάθος είναι ότι γράφετε "or" δηλώσεις όπως θα κάνατε στα αγγλικά:
```{r}
#| eval: false
flights |>
filter(month == 1 | 2)
```
Ο παραπάνω κώδικας "δουλεύει", με την έννοια ότι δεν επιστρέφει σφάλμα, αλλά δεν κάνει αυτό που θέλετε επειδή το `|` ελέγχει πρώτα τη συνθήκη `month == 1` και μετά ελέγχει τη συνθήκη `2`, η οποία δεν είναι μία λογική συνθήκη για έλεγχο.
Θα μάθουμε περισσότερα για το τι συμβαίνει εδώ και γιατί στην @sec-boolean-operations.
### `arrange()`
Η `arrange()` αλλάζει τη σειρά των γραμμών με βάση την τιμή των στηλών.
Δέχεται σαν ορίσματα ένα πλαίσιο δεδομένων και ένα σύνολο ονομάτων στηλών (ή πιο περίπλοκων εκφράσεων) για να τα ταξινομήσετε.
Εάν παρέχετε περισσότερα από ένα ονόματα στηλών, κάθε πρόσθετη στήλη θα χρησιμοποιηθεί για την επίλυση ισοπαλιών στις τιμές των προηγούμενων στηλών.
Για παράδειγμα, ο παρακάτω κώδικας ταξινομεί τις γραμμές με βάση την ώρα αναχώρησης, η οποία κατανέμεται σε τέσσερις στήλες.
Πρώτα παίρνουμε τα πιο πρόσφατα χρόνια, και στη συνέχεια ανάμεσα σε ένα χρόνο τους πρώτους μήνες κ.ο.κ.
```{r}
flights |>
arrange(year, month, day, dep_time)
```
Μπορείτε να χρησιμοποιήσετε το όρισμα `desc()` σε μία στήλη μέσα στην `arrange()` για να αναδιατάξετε το πλαίσιο δεδομένων με βάση αυτή τη στήλη με φθίνουσα σειρά (μεγαλύτερο προς μικρότερο).
Για παράδειγμα, ο ακόλουθος κώδικας διατάσσει τις πτήσεις ανάλογα με την καθυστέρηση που έχουν σημειώσει, από τις μεγαλύτερες έως τις μικρότερες:
```{r}
flights |>
arrange(desc(dep_delay))
```
Σημειώστε ότι ο αριθμός των γραμμών δεν έχει αλλάξει -- τακτοποιούμε μόνο τα δεδομένα, δεν τα φιλτράρουμε.
### `distinct()`
Η `distinct()` βρίσκει όλες τις μοναδικές γραμμές σε ένα σύνολο δεδομένων, επομένως από τεχνική άποψη, λειτουργεί κυρίως στις γραμμές.
Τις περισσότερες φορές, ωστόσο, θα θέλετε τον διακριτό συνδυασμό ορισμένων μεταβλητών, οπότε μπορείτε επίσης να παρέχετε προαιρετικά ονόματα στηλών:
```{r}
# Αφαιρέστε τις διπλότυπες γραμμές, εάν υπάρχουν
flights |>
distinct()
# Βρείτε όλα τα μοναδικά ζεύγη προέλευσης και προορισμού
flights |>
distinct(origin, dest)
```
Εναλλακτικά, εάν θέλετε να διατηρήσετε άλλες στήλες κατά το φιλτράρισμα για μοναδικές γραμμές, μπορείτε να χρησιμοποιήσετε την επιλογή `.keep_all = TRUE`.
```{r}
flights |>
distinct(origin, dest, .keep_all = TRUE)
```
Δεν είναι τυχαίο ότι όλες αυτές οι ξεχωριστές πτήσεις πραγματοποιούνται την 1η Ιανουαρίου: Η `distinct()` θα βρει την πρώτη εμφάνιση μιας μοναδικής γραμμής στο σύνολο δεδομένων και θα απορρίψει τις υπόλοιπες.
Εάν θέλετε να βρείτε τον αριθμό των εμφανίσεων, καλύτερα να αντικαταστήσετε την `distinct()` με την `count()`, ενώ χρησιμοποιώντας το όρισμα `sort = TRUE` μπορείτε να τις διατάξετε σε φθίνουσα σειρά σύμφωνα με τον αριθμό εμφανίσεων.
Θα μάθετε περισσότερα για τα αθροίσματα στην @sec-counts.
```{r}
flights |>
count(origin, dest, sort = TRUE)
```
### Ασκήσεις
1. Σε μία μόνο ροή για κάθε συνθήκη, βρείτε όλες τις πτήσεις που πληρούν την προϋπόθεση:
- Να είχε καθυστέρηση άφιξης δύο ή περισσότερων ωρών
- Να πέταξε στο Χιούστον («IAH» ή «HOU»)
- Να λειτουργούσαν από τις αεροπορικές εταιρείες United, American ή Delta
- Να αναχώρησε το καλοκαίρι (Ιούλιο, Αύγουστο και Σεπτέμβριο)
- Να έφτασε με περισσότερο από δύο ώρες καθυστέρηση, αλλά δεν αναχώρησε με καθυστέρηση
- Να καθυστέρησαν τουλάχιστον μία ώρα, αλλά συμπληρώθηκαν πάνω από 30 λεπτά κατά την πτήση
2. Ταξινομήστε το `flights` για να βρείτε τις πτήσεις με τις μεγαλύτερες καθυστερήσεις αναχώρησης.
Βρείτε τις πτήσεις που έφυγαν πιο νωρίς το πρωί.
3. Ταξινομήστε το `flights` για να βρείτε τις ταχύτερες πτήσεις.
(Υπόδειξη: Δοκιμάστε να συμπεριλάβετε έναν μαθηματικό υπολογισμό μέσα στη συνάρτησή σας.)
4. Υπήρχε πτήση κάθε μέρα του 2013;
5. Ποιες πτήσεις διένυσαν τη μεγαλύτερη απόσταση; Ποιο ταξίδεψε τη λιγότερη απόσταση;
6. Έχει σημασία με ποια σειρά χρησιμοποιήσατε την `filter()` και την `arrange()` εάν χρησιμοποιείτε και τις δύο; Γιατί/γιατί όχι?
Σκεφτείτε τα αποτελέσματα και πόση δουλειά θα πρέπει να κάνουν αυτές οι συναρτήσεις.
## Στήλες
Υπάρχουν τέσσερις σημαντικές συναρτήσεις που επηρεάζουν τις στήλες χωρίς να αλλάζουν τις γραμμές: η `mutate()` δημιουργεί νέες στήλες που προέρχονται από τις ήδη υπάρχουσες στήλες, η `select()` που αλλάζει τις στήλες που είναι παρούσες, η `rename()` που αλλάζει τα ονόματα των στηλών και η `relocate()` που αλλάζει τις θέσεις των στηλών.
### `mutate()` {#sec-mutate}
Η δουλειά της `mutate()` είναι να προσθέτει νέες στήλες που υπολογίζονται από τις υπάρχουσες.
Στα κεφάλαια του μετασχηματισμού δεδομένων, θα μάθετε ένα μεγάλο σύνολο συναρτήσεων το οποίο μπορείτε να χρησιμοποιήσετε για να χειριστείτε διαφορετικούς τύπους μεταβλητών.
Προς το παρόν, θα παραμείνουμε στη βασική άλγεβρα, η οποία μας επιτρέπει να υπολογίσουμε το `κέρδος` (`gain`), τον χρόνο που έχει περάσει στον αέρα μία καθυστερημένη πτήση, και την `ταχύτητα` (`speed`) σε μίλια ανά ώρα:
```{r}
flights |>
mutate(
gain = dep_delay - arr_delay,
speed = distance / air_time * 60
)
```
Από προεπιλογή, η `mutate()` προσθέτει νέες στήλες στη δεξιά πλευρά του συνόλου δεδομένων σας, γεγονός που καθιστά δύσκολο να δείτε τι έχει συμβεί.
Γι' αυτό μπορούμε να χρησιμοποιήσουμε το όρισμα `.before` για να προσθέσουμε τις μεταβλητές στην αριστερή πλευρά[^data-transform-2]:
[^data-transform-2]: Θυμηθείτε ότι στο RStudio, ο ευκολότερος τρόπος για να δείτε ένα σύνολο δεδομένων με πολλές στήλες είναι η εντολή `View()`.
```{r}
flights |>
mutate(
gain = dep_delay - arr_delay,
speed = distance / air_time * 60,
.before = 1
)
```
Το `.` υποδεικνύει ότι το `.before` είναι ένα όρισμα στη συνάρτηση, και όχι το όνομα μιας τρίτης νέας μεταβλητής που θα δημιουργήσουμε.
Μπορείτε επίσης να χρησιμοποιήσετε το όρισμα `.after` για να προσθέσετε τη νέα στήλη μετά από μία συγκεκριμένη μεταβλητή, ενώ τόσο στο όρισμα `.before` όσο και στο `.after` μπορείτε να χρησιμοποιήσετε το όνομα της μεταβλητής αντί για τη θέση της.
Για παράδειγμα, θα μπορούσαμε να προσθέσουμε τις νέες μεταβλητές μετά την `day`:
```{r}
#| results: false
flights |>
mutate(
gain = dep_delay - arr_delay,
speed = distance / air_time * 60,
.after = day
)
```
Εναλλακτικά, μπορείτε να ελέγξετε ποιες μεταβλητές θα διατηρηθούν με το όρισμα `.keep`.
Ένα ιδιαίτερα χρήσιμο όρισμα είναι το `"used"` που προσδιορίζει ότι θέλουμε να διατηρήσουμε μόνο τις στήλες που εμπλέκονται ή δημιουργήθηκαν στο βήμα της `mutate()`.
Για παράδειγμα, η ακόλουθη έξοδος θα περιέχει μόνο τις μεταβλητές `dep_delay`, `arr_delay`, `air_time`, `gain`, `hours`, και `gain_per_hour`.
```{r}
#| results: false
flights |>
mutate(
gain = dep_delay - arr_delay,
hours = air_time / 60,
gain_per_hour = gain / hours,
.keep = "used"
)
```
Έχετε υπόψη ότι επειδή δεν έχουμε αναθέσει το αποτέλεσμα του παραπάνω υπολογισμού πίσω στις `flights`, οι νέες μεταβλητές `gain,` `hours`, και `gain_per_hour` θα τυπωθούν αλλά δεν θα αποθηκευτούν σε ένα πλαίσιο δεδομένων.
Εάν θέλουμε να είναι διαθέσιμα σε ένα πλαίσιο δεδομένων για μελλοντική χρήση, θα πρέπει να σκεφτούμε προσεκτικά εάν θέλουμε το αποτέλεσμα να ανατεθεί ξανά στο `flights`, αντικαθιστώντας το αρχικό πλαίσιο δεδομένων με πολλές περισσότερες μεταβλητές ή σε ένα νέο αντικείμενο .
Συχνά, η σωστή απάντηση είναι ένα νέο αντικείμενο που ονομάζεται με τρόπο που υποδεικνύει το περιεχόμενό του, π.χ.
`delay_gain`, αλλά μπορεί επίσης να έχετε καλούς λόγους για την αντικατάσταση του `flights`.
### `select()` {#sec-select}
Δεν είναι ασυνήθιστο να λαμβάνετε σύνολα δεδομένων με εκατοντάδες ή και χιλιάδες μεταβλητές.
Σε αυτήν την περίπτωση, η πρώτη πρόκληση είναι συχνά απλώς να εστιάσετε στις μεταβλητές που σας ενδιαφέρουν.
Η `select()` επιτρέπει να εστιάζεται γρήγορα σε ένα χρήσιμο υποσύνολο χρησιμοποιώντας λειτουργίες που βασίζονται στα ονόματα των μεταβλητών:
- Επιλογή στηλών με το όνομα τους:
```{r}
#| results: false
flights |>
select(year, month, day)
```
- Επιλογή όλων των στηλών που βρίσκονται ανάμεσα στις στήλες year και day (συμπερίληψη):
```{r}
#| results: false
flights |>
select(year:day)
```
- Επιλογή όλων των μεταβλητών εκτός αυτών που βρίσκονται ανάμεσα στις στήλες year και day (αποκλεισμός):
```{r}
#| results: false
flights |>
select(!year:day)
```
Ιστορικά, αυτή η εργασία γινόταν με το `-` αντί για το `!`, οπότε είναι πιθανό να το συναντήσετε εκεί έξω.
Αυτοί οι δύο χειριστές υπηρετούν τον ίδιο σκοπό, με μικρές διαφορές ως προς τη συμπεριφορά τους.
Συνιστούμε να χρησιμοποιείτε το `!` μιας και διαβάζεται ως "όχι", και συνδυάζεται καλά με τα `&` και `|`.
- Επιλέξτε όλες τις στήλες που είναι χαρακτήρες:
```{r}
#| results: false
flights |>
select(where(is.character))
```
Υπάρχει ένας αριθμός βοηθητικών συναρτήσεων που μπορείτε να χρησιμοποιήσετε μέσα στη `select()`:
- `starts_with("abc")`: επιλέγει στήλες των οποίων τα ονόματα ξεκινούν με "abc".
- `ends_with("xyz")`: επιλέγει στήλες των οποίων τα ονόματα τελειώνουν σε "xyz".
- `contains("ijk")`: επιλέγει στήλες των οποίων τα ονόματα περιέχουν το "ijk".
- `Num_range("x", 1:3)`: επιλέγει τις στήλες `x1`, `x2` και `x3`.
Δείτε την εντολή `?select` για περισσότερες λεπτομέρειες.
Μόλις μάθετε για τις κανονικές εκφράσεις (το αντικείμενο στο @sec-regular-expressions), θα μπορείτε επίσης να χρησιμοποιήσετε την `matches()` για να επιλέξετε μεταβλητές που ταιριάζουν σε ένα μοτίβο.
Μπορείτε να μετονομάσετε τις μεταβλητές καθώς τις επιλέγετε με την `select()` χρησιμοποιώντας το `=`.
Το νέο όνομα εμφανίζεται στην αριστερή πλευρά του `=` και η παλιά μεταβλητή στη δεξιά πλευρά:
```{r}
flights |>
select(tail_num = tailnum)
```
### `rename()`
Εάν θέλετε να διατηρήσετε όλες τις υπάρχουσες μεταβλητές και απλώς να μετονομάσετε μερικές, μπορείτε να χρησιμοποιήσετε την `rename()` αντί για την `select()`:
```{r}
flights |>
rename(tail_num = tailnum)
```
Εάν έχετε ένα σωρό στήλες με ασυνεπή ονόματα, τις οποίες θα ήταν επώδυνο να διορθώσετε όλες με το χέρι, ανατρέξτε στην `janitor::clean_names()` η οποία παρέχει χρήσιμες δυνατότητες αυτοματοποιημένου καθαρισμού των δεδομένων.
### `relocate()`
Χρησιμοποιήστε την `relocate()` για να αλλάξετε θέση στις μεταβλητές.
Μπορεί να θέλετε να συλλέξετε σχετικές μεταβλητές μαζί ή να μετακινήσετε σημαντικές μεταβλητές στο μπροστινό μέρος.
Ως προεπιλογή η `relocate()` μετακινεί τις μεταβλητές στην αρχή του πλαισίου δεδομένων:
```{r}
flights |>
relocate(time_hour, air_time)
```
Μπορείτε επίσης να καθορίσετε το πού θα τοποθετήσετε τις στήλες χρησιμοποιώντας τα ορίσματα `.before` και `.after`, όπως ακριβώς στην `mutate()`:
```{r}
#| results: false
flights |>
relocate(year:dep_time, .after = time_hour)
flights |>
relocate(starts_with("arr"), .before = dep_time)
```
### Ασκήσεις
```{r}
#| eval: false
#| echo: false
# Κώδικας για έλεγχο δεδομένων, δεν χρησιμοποιείται στα αποτελέσματα που εμφανίζονται στο βιβλίο
flights <- flights |> mutate(
dep_time = hour * 60 + minute,
arr_time = (arr_time %/% 100) * 60 + (arr_time %% 100),
airtime2 = arr_time - dep_time,
dep_sched = dep_time + dep_delay
)
ggplot(flights, aes(x = dep_sched)) + geom_histogram(binwidth = 60)
ggplot(flights, aes(x = dep_sched %% 60)) + geom_histogram(binwidth = 1)
ggplot(flights, aes(x = air_time - airtime2)) + geom_histogram()
```
1. Συγκρίνετε τις μεταβλητές `dep_time`, `sched_dep_time`, και `dep_delay`.
Πώς θα περιμένατε να σχετίζονται αυτοί οι τρεις αριθμοί;
2. Βρείτε όσο το δυνατόν περισσότερους τρόπους για να επιλέξετε τις μεταβλητές `dep_time`, `dep_delay`, `arr_time`, και `arr_delay` από το `flights`
3. Τι συμβαίνει εάν καθορίσετε το όνομα της ίδιας μεταβλητής πολλές φορές σε μία κλήση της `select()`;
4. Τι κάνει η συνάρτηση `any_of()`; Γιατί μπορεί να είναι χρήσιμη σε συνδυασμό με το παρακάτω διάνυσμα;
```{r}
variables <- c("year", "month", "day", "dep_delay", "arr_delay")
```
5. Σας εκπλήσσει το αποτέλεσμα της εκτέλεσης του παρακάτω κώδικα; Πώς αντιμετωπίζουν τα κεφαλαία και τα πεζά από προεπιλογή οι βοηθητικές συναρτήσεις της `select()`; Πώς μπορείτε να αλλάξετε αυτήν την προεπιλογή;
```{r}
#| eval: false
flights |> select(contains("TIME"))
```
6. Μετονομάστε την `air_time` σε `air_time_min` για να υποδείξετε τις μονάδες μέτρησης και μετακινήστε την στην αρχή του πλαισίου δεδομένων.
7. Γιατί δεν λειτουργεί ο παρακάτω κώδικας και τι σημαίνει το σφάλμα;
```{r}
#| error: true
flights |>
select(tailnum) |>
arrange(arr_delay)
```
## Το pipe {#sec-the-pipe}
Παραπάνω σας δείξαμε απλά παραδείγματα του pipe, αλλά η πραγματική του δύναμη προκύπτει όταν αρχίσετε να συνδυάζετε πολλές συναρτήσεις μαζί.
Για παράδειγμα, φανταστείτε ότι θέλετε να βρείτε τις πιο γρήγορες πτήσεις προς το αεροδρόμιο IAH του Χιούστον: πρέπει να συνδυάσετε τις `filter()`, `mutate()`, `select()`, και `arrange()`:
```{r}
flights |>
filter(dest == "IAH") |>
mutate(speed = distance / air_time * 60) |>
select(year:day, dep_time, carrier, flight, speed) |>
arrange(desc(speed))
```
Παρόλο που αυτή η ροή έχει τέσσερα βήματα, είναι εύκολο να την διαβάσετε γρήγορα, μιας και οι συναρτήσεις εμφανίζονται στην αρχή κάθε γραμμής: ξεκινήστε με τα δεδομένα του `flights`, μετά φιλτράρετε, μετά δημιουργείστε, μετά επιλέξτε και μετά διατάξτε τα δεδομένα.
Τι θα γινόταν αν δεν είχαμε το pipe; Θα μπορούσαμε να ενσωματώσουμε κάθε κλήση συνάρτησης μέσα στην προηγούμενη κλήση:
```{r}
#| results: false
arrange(
select(
mutate(
filter(
flights,
dest == "IAH"
),
speed = distance / air_time * 60
),
year:day, dep_time, carrier, flight, speed
),
desc(speed)
)
```
Ή θα μπορούσαμε να δημιουργήσουμε ένα σωρό ενδιάμεσα αντικείμενα:
```{r}
#| results: false
flights1 <- filter(flights, dest == "IAH")
flights2 <- mutate(flights1, speed = distance / air_time * 60)
flights3 <- select(flights2, year:day, dep_time, carrier, flight, speed)
arrange(flights3, desc(speed))
```
Ενώ και οι δύο επιλογές έχουν τον χρόνο και τον τόπο τους, το pipe γενικά παράγει κώδικα ανάλυσης δεδομένων που είναι ευκολότερο να γραφτεί και να διαβαστεί.
Για να προσθέσετε το pipe στον κώδικά σας, συνιστούμε να χρησιμοποιήσετε την ενσωματωμένη συντόμευση πληκτρολογίου Ctrl/Cmd + Shift + M.
Θα χρειαστεί να κάνετε μία αλλαγή στις επιλογές του RStudio για να χρησιμοποιήσετε το `|>` αντί για το `%>%` όπως φαίνεται στο @fig-pipe-options, ενώ θα δούμε περισσότερα για το `%>%` σύντομα.
```{r}
#| label: fig-pipe-options
#| echo: false
#| fig-cap: |
#| Για να εισάγετε το `|>`, βεβαιωθείτε ότι η επιλογή
#| "Use native pipe operator" είναι ενεργοποιημένη.
#| fig-alt: |
#| Στιγμιότυπο οθόνης που δείχνει την επιλογή "Use native pipe operator"
#| η οποία μπορεί να βρεθεί στο πάνελ "Editing" της επιλογής "Code".
knitr::include_graphics("screenshots/rstudio-pipe-options.png")
```
::: callout-note
## magrittr
Εάν χρησιμοποιείτε το tidyverse για κάποιο καιρό, ίσως να είστε εξοικειωμένοι με το `%>%` που παρέχεται από το πακέτο **magrittr**.
Το πακέτο magrittr περιλαμβάνεται στο tidyverse, ώστε να μπορείτε να χρησιμοποιείτε το `%>%` κάθε φορά που φορτώνετε το tidyverse:
```{r}
#| eval: false
library(tidyverse)
mtcars %>%
group_by(cyl) %>%
summarize(n = n())
```
Για απλές περιπτώσεις, τα `|>` και `%>%` συμπεριφέρονται πανομοιότυπα.
Γιατί λοιπόν προτείνουμε τον βασικό pipe; Πρώτον, επειδή είναι μέρος του βασικού συνόλου συναρτήσεων της R, είναι πάντα διαθέσιμο για χρήση, ακόμα και όταν δεν χρησιμοποιείτε το tidyverse.
Δεύτερον, το `|>` είναι αρκετά πιο απλό από το `%>%`: στο διάστημα μεταξύ της εφεύρεσης του `%>%` το 2014 και της συμπερίληψης του `|>` στην έκδοση 4.1.0 της R το 2021, κερδίσαμε μία καλύτερη κατανόηση του pipe.
Αυτό επέτρεψε στην βασική εφαρμογή (`|>`) να απορρίψει σπάνια χρησιμοποιούμενα και λιγότερο σημαντικά χαρακτηριστικά.
:::
## Ομάδες
Μέχρι στιγμής έχετε μάθει για συναρτήσεις που λειτουργούν σε γραμμές και στήλες.
Η dplyr γίνεται ακόμα πιο ισχυρή όταν προσθέτετε τη δυνατότητα εργασίας με ομάδες.
Σε αυτήν την ενότητα, θα επικεντρωθούμε στις πιο σημαντικές συναρτήσεις: `group_by()`, `summarize()`, και την οικογένεια συναρτήσεων slice.
### `group_by()`
Χρησιμοποιήστε την `group_by()` για να διαιρέσετε το σύνολο δεδομένων σας σε ομάδες που βγάζουν νόημα για την ανάλυσή σας.
```{r}
flights |>
group_by(month)
```
Η `group_by()` δεν αλλάζει τα δεδομένα, αλλά, αν κοιτάξετε προσεκτικά την έξοδο, θα παρατηρήσετε ότι η έξοδος υποδεικνύει ότι είναι "ομαδοποιημένη με βάση" το μήνα (`Groups: month [12]`).
Αυτό σημαίνει ότι οι επόμενες συναρτήσεις θα εφαρμόζονται πλέον "ανά μήνα".
Η `group_by()` προσθέτει αυτό το ομαδοποιημένο χαρακτηριστικό (που αναφέρεται ως κλάση) στο πλαίσιο δεδομένων, το οποίο αλλάζει τη συμπεριφορά των επόμενων συναρτήσεων που εφαρμόζονται στα δεδομένα.
### `summarize()` {#sec-summarize}
Η πιο σημαντική ομαδοποιημένη λειτουργία είναι μία σύνοψη, η οποία, εάν χρησιμοποιηθεί για τον υπολογισμό ενός μόνο συνοπτικού στατιστικού, μειώνει το πλαίσιο δεδομένων ώστε να έχει μία μόνο γραμμή για κάθε ομάδα.
Στο πακέτο dplyr, αυτή η λειτουργία εκτελείται από την `summarize()`[^data-transform-3], όπως φαίνεται στο ακόλουθο παράδειγμα, το οποίο υπολογίζει τη μέση καθυστέρηση αναχώρησης ανά μήνα:
[^data-transform-3]: Ή `summarise()`, εαν προτιμάτε βρετανικά αγγλικά.
```{r}
flights |>
group_by(month) |>
summarize(
avg_delay = mean(dep_delay)
)
```
Ωχ!
Κάτι πήγε στραβά και όλα τα αποτελέσματά μας είναι `NA` (προφέρεται "N-A"), το σύμβολο R για τις ελλιπής τιμές.
Αυτό συνέβη επειδή ορισμένες από τις παρατηρούμενες πτήσεις είχαν κενές τιμές στη στήλη delay, και έτσι, όταν υπολογίσαμε τη μέση τιμή, συμπεριλαμβανομένων αυτών των τιμών, πήραμε ένα αποτέλεσμα `NA`.
Θα επανέλθουμε για να συζητήσουμε λεπτομερώς τις κενές τιμές στο @sec-missing-values, αλλά προς το παρόν θα πούμε στη συνάρτηση `mean()` να αγνοήσει όλες τις κενές τιμές θέτοντας το όρισμα `na.rm` ως `TRUE`:
```{r}
flights |>
group_by(month) |>
summarize(
avg_delay = mean(dep_delay, na.rm = TRUE)
)
```
Μπορείτε να δημιουργήσετε οποιονδήποτε αριθμό συνόψεων σε μία μόνο κλήση τις `summarize()`.
Θα μάθετε διάφορες χρήσιμες συνόψεις στα επόμενα κεφάλαια, αλλά μία πολύ χρήσιμη περίληψη είναι η `n()`, η οποία επιστρέφει τον αριθμό των γραμμών σε κάθε ομάδα:
```{r}
flights |>
group_by(month) |>
summarize(
avg_delay = mean(dep_delay, na.rm = TRUE),
n = n()
)
```
Οι μέσες τιμές και οι μετρήσεις μπορούν να σας βοηθήσουν να πάτε αρκετά μακρυά στην επιστήμη των δεδομένων!
### Οι συναρτήσεις της οικογένειας `slice_`
Υπάρχουν πέντε χρήσιμες συναρτήσεις που σας επιτρέπουν να εξάγετε συγκεκριμένες γραμμές μέσα σε κάθε ομάδα:
- Το `df |> slice_head(n = 1)` παίρνει την πρώτη γραμμή από κάθε ομάδα.
- Το `df |> slice_tail(n = 1)` παίρνει την τελευταία γραμμή σε κάθε ομάδα.
- Το `df |> slice_min(x, n = 1)` παίρνει τη γραμμή με τη μικρότερη τιμή στη στήλη `x`.
- Το `df |> slice_max(x, n = 1)` παίρνει τη σειρά με τη μεγαλύτερη τιμή στη στήλη `x`.
- Το `df |> slice_sample(n = 1)` παίρνει μία τυχαία γραμμή
Μπορείτε να αλλάξετε το `n` για να επιλέξετε περισσότερες από μία γραμμές ή αντί για το `n =`, ενώ μπορείτε να χρησιμοποιήσετε το `prop = 0.1` για να επιλέξετε (π.χ.) το 10% των γραμμών σε κάθε ομάδα.
Για παράδειγμα, ο παρακάτω κώδικας βρίσκει τις πτήσεις που καθυστερούν περισσότερο κατά την άφιξη τους σε κάθε προορισμό:
```{r}
flights |>
group_by(dest) |>
slice_max(arr_delay, n = 1) |>
relocate(dest)
```
Σημειώστε ότι υπάρχουν 105 προορισμοί, αλλά εδώ έχουμε 108 γραμμές.
Τι συμβαίνει λοιπόν; Η `slice_min()` και η `slice_max()` διατηρούν ισοδύναμες τιμές, επομένως το `n = 1` σημαίνει "δώσε όλες τις γραμμές με την υψηλότερη τιμή".
Εάν θέλετε ακριβώς μία γραμμή ανά ομάδα, μπορείτε να ορίσετε `with_ties = FALSE`.
Αυτό είναι παρόμοιο με τον υπολογισμό της μέγιστης καθυστέρησης με τη `summarize()`, αλλά λαμβάνετε ολόκληρη την αντίστοιχη γραμμή (ή τις γραμμές εάν υπάρχει ισοπαλία) αντί για το μεμονωμένο συνοπτικό στατιστικό.
### Ομαδοποίηση κατά πολλαπλές μεταβλητές
Μπορείτε να δημιουργήσετε ομάδες χρησιμοποιώντας περισσότερες από μία μεταβλητές.
Για παράδειγμα, θα μπορούσαμε να φτιάξουμε μία ομάδα για κάθε ημερομηνία.
```{r}
daily <- flights |>
group_by(year, month, day)
daily
```
Όταν συνοψίζετε ένα tibble ομαδοποιημένο με περισσότερες από μία μεταβλητές, κάθε σύνοψη αφαιρεί την τελευταία ομάδα.
Εκ των υστέρων, αυτός δεν ήταν ένας εξαιρετικός τρόπος για να λειτουργήσει αυτή η συνάρτηση, αλλά είναι δύσκολο να αλλάξει χωρίς να σπάσει τον υπάρχοντα κώδικα.
Για να είναι προφανές τι συμβαίνει, το πακέτο dplyr εμφανίζει ένα μήνυμα που σας λέει πώς μπορείτε να αλλάξετε αυτήν τη συμπεριφορά:
```{r}
daily_flights <- daily |>
summarize(n = n())
```
Εάν είστε ευχαριστημένοι με αυτήν τη συμπεριφορά, μπορείτε να το ζητήσετε ρητά για να αποκρυφθεί αυτό το μήνυμα:
```{r}
#| results: false
daily_flights <- daily |>
summarize(
n = n(),
.groups = "drop_last"
)
```
Εναλλακτικά, αλλάξτε την προεπιλεγμένη συμπεριφορά ορίζοντας μία διαφορετική τιμή, π.χ.
`"drop"` για απόρριψη όλων των ομάδων ή `"keep"`για διατήρηση των ίδιων ομάδων.
### Κατάργηση της ομαδοποίησης
Μπορεί επίσης να θέλετε να καταργήσετε την ομαδοποίηση από ένα πλαίσιο δεδομένων χωρίς τη χρήση της `summarize()`.
Αυτό μπορείτε να το κάνετε με την `ungroup()`.
```{r}
daily |>
ungroup()
```
Τώρα ας δούμε τι συμβαίνει όταν συνοψίζετε ένα μη ομαδοποιημένο πλαίσιο δεδομένων.
```{r}
daily |>
ungroup() |>
summarize(
avg_delay = mean(dep_delay, na.rm = TRUE),
flights = n()
)
```
Σαν αποτέλεσμα παίρνετε μία μόνο γραμμή επειδή το πακέτο dplyr αντιμετωπίζει όλες τις γραμμές σε ένα μη ομαδοποιημένο πλαίσιο δεδομένων σαν να ανήκουν σε μία ομάδα.
### `.by`
Η dplyr 1.1.0 περιλαμβάνει ένα νέο, πειραματικό συντακτικό για την ομαδοποίηση ανά λειτουργία, το όρισμα `.by`.
Οι `group_by()` και `ungroup()` δεν εξαφανίζονται, αλλά τώρα μπορείτε επίσης να χρησιμοποιήσετε το όρισμα `.by` για ομαδοποίηση μέσα σε μία λειτουργία:
```{r}
#| results: false
flights |>
summarize(
delay = mean(dep_delay, na.rm = TRUE),
n = n(),
.by = month
)
```
Ή εάν θέλετε να ομαδοποιήσετε κατά πολλές μεταβλητές:
```{r}
#| results: false
flights |>
summarize(
delay = mean(dep_delay, na.rm = TRUE),
n = n(),
.by = c(origin, dest)
)
```
Το `.by` λειτουργεί με όλα τις συναρτήσεις και έχει το πλεονέκτημα ότι δεν χρειάζεται να χρησιμοποιήσετε το όρισμα `.groups` για να αποκρύψετε το μήνυμα ομαδοποίησης ή την `ungroup()` όταν τελειώσετε.
Δεν επικεντρωθήκαμε σε αυτή τη σύνταξη σε αυτό το κεφάλαιο γιατί ήταν αρκετά νέα όταν γράφαμε το βιβλίο.
Θέλαμε όμως να το αναφέρουμε γιατί πιστεύουμε ότι έχει προοπτικές και είναι πιθανό να είναι αρκετά δημοφιλές.
Μπορείτε να μάθετε περισσότερα σχετικά με αυτό στο [dplyr 1.1.0 blog post](https://www.tidyverse.org/blog/2023/02/dplyr-1-1-0-per-operation-grouping/).
### Ασκήσεις
1. Ποια αεροπορική εταιρεία έχει τις χειρότερες μέσες καθυστερήσεις; Πρόκληση: μπορείτε να ξεχωρίσετε τις επιπτώσεις των κακών αεροδρομίων έναντι των κακών αερομεταφορέων; Γιατί/γιατί όχι; (Υπόδειξη: σκεφτείτε το `flights |> group_by(carrier, dest) |> summarize(n())`)
2. Βρείτε τις πτήσεις που καθυστερούν περισσότερο κατά την αναχώρηση από κάθε προορισμό.
3. Πώς ποικίλλουν οι καθυστερήσεις κατά τη διάρκεια της ημέρας.
Αποτυπώστε την απάντησή σας σε ένα διάγραμμα.
4. Τι θα συμβεί αν δώσετε αρνητικές τιμές στο όρισμα `n` μέσα στη `slice_min()` και τις σχετικές συναρτήσεις;
5. Εξηγήστε τι κάνει η `count()` όσον αφορά τις συναρτήσεις της dplyr που μόλις μάθατε.
Τι κάνει το όρισμα `sort` στην `count()`;
6. Ας υποθέσουμε ότι έχουμε το ακόλουθο μικροσκοπικό πλαίσιο δεδομένων:
```{r}
df <- tibble(
x = 1:5,
y = c("a", "b", "a", "a", "b"),
z = c("K", "K", "L", "L", "K")
)
```
a. Γράψτε πώς πιστεύετε ότι θα μοιάζει η έξοδος, στη συνέχεια ελέγξτε αν η σκέψη σας ήταν σωστή και περιγράψτε τι κάνει η `group_by()`.
```{r}
#| eval: false
df |>
group_by(y)
```
b. Γράψτε πώς πιστεύετε ότι θα μοιάζει η έξοδος, στη συνέχεια ελέγξτε αν η σκέψη σας ήταν σωστή και περιγράψτε τι κάνει η `arrange()`.
Σχολιάστε επίσης πώς διαφέρει από τη `group_by()` στο μέρος (a);
```{r}
#| eval: false
df |>
arrange(y)
```
c. Γράψτε πώς πιστεύετε ότι θα μοιάζει η έξοδος, στη συνέχεια ελέγξτε αν η σκέψη σας ήταν σωστή και περιγράψτε τι κάνει η ακόλουθη ροή.
```{r}
#| eval: false
df |>
group_by(y) |>
summarize(mean_x = mean(x))
```
d. Γράψτε πώς πιστεύετε ότι θα μοιάζει η έξοδος και, στη συνέχεια, ελέγξτε αν η σκέψη σας ήταν σωστή και περιγράψτε τι κάνει η ακόλουθη ροή.
Στη συνέχεια, σχολιάστε τι λέει το μήνυμα.
```{r}
#| eval: false
df |>
group_by(y, z) |>
summarize(mean_x = mean(x))
```
e. Γράψτε πώς πιστεύετε ότι θα μοιάζει η έξοδος και, στη συνέχεια, εελέγξτε αν η σκέψη σας ήταν σωστή και περιγράψτε τι κάνει η ακόλουθη ροή.
Σε τι διαφέρει η έξοδος από αυτή του μέρους (d).
```{r}
#| eval: false
df |>
group_by(y, z) |>
summarize(mean_x = mean(x), .groups = "drop")
```
f. Γράψτε πώς πιστεύετε ότι θα μοιάζουν τα αποτελέσματα, στη συνέχεια ελέγξτε αν η σκέψη σας ήταν σωστή και περιγράψτε τι κάνει κάθε ροή.
Πώς διαφέρουν οι έξοδοι των δύο ροών;
```{r}
#| eval: false
df |>
group_by(y, z) |>
summarize(mean_x = mean(x))
df |>
group_by(y, z) |>
mutate(mean_x = mean(x))
```
## Μελέτη περίπτωσης: συγκεντρωτικά στοιχεία και μέγεθος δείγματος {#sec-sample-size}
Κάθε φορά που κάνετε οποιαδήποτε σύνοψη, είναι πάντα καλή ιδέα να συμπεριλάβετε μία καταμέτρηση (`n()`).
Με αυτόν τον τρόπο, μπορείτε να διασφαλίσετε ότι δεν βγάζετε συμπεράσματα με βάση πολύ μικρές ποσότητες δεδομένων.
Θα το δείξουμε με δεδομένα σχετικά με το μπέιζμπολ από το πακέτο **Lahman**.
Συγκεκριμένα, θα συγκρίνουμε το ποσοστό των φορών που ένας παίκτης δέχεται ένα χτύπημα (`H`) με τον αριθμό των φορών που προσπαθεί να επαναφέρει την μπάλα στο παιχνίδι (`AB`):
```{r}
batters <- Lahman::Batting |>
group_by(playerID) |>
summarize(
performance = sum(H, na.rm = TRUE) / sum(AB, na.rm = TRUE),
n = sum(AB, na.rm = TRUE)
)
batters
```
Όταν σχεδιάζουμε την ικανότητα του παίκτη του μπέιζμπολ (μετρούμενη με τον μέσο όρο των χτυπημάτων, βάσει της μεταβλητής `performance`) σε σχέση με τον αριθμό των ευκαιριών να χτυπήσει την μπάλα (μετρούμενη με το πλήθος των ευκαιριών, `n`), παρατηρείτε δύο μοτίβα:
1. Η διακύμανση στις τιμές της μεταβλητής `performance` είναι μεγαλύτερη μεταξύ των παικτών με λιγότερες ευκαιρίες.
Το σχήμα αυτού του διαγράμματος είναι πολύ χαρακτηριστικό: κάθε φορά που σχεδιάζετε έναν μέσο όρο (ή άλλα συνοπτικά στατιστικά στοιχεία) έναντι του μεγέθους της ομάδας, θα παρατηρείται ότι η διακύμανση μειώνεται καθώς αυξάνεται το μέγεθος του δείγματος[^data-transform-4].
2. Υπάρχει μία θετική συσχέτιση μεταξύ της ικανότητας (`performance`) του παίκτη και των ευκαιριών να χτυπήσει την μπάλα (`n`), επειδή οι ομάδες θέλουν να δώσουν στους καλύτερους παίκτες τους τις περισσότερες ευκαιρίες για να χτυπήσουν την μπάλα.
[^data-transform-4]: \*βήχας\* ο νόμος των μεγάλων αριθμών \*βήχας\*.
```{r}
#| warning: false
#| fig-alt: |
#| Ένα διάγραμμα διασποράς του πλήθους επιδόσεων κτυπήματος έναντι των
#| ευκαιριών κτυπήματος που επικαλύπτεται με μια ομαλοποιημένη γραμμή.
#| Η μέση απόδοση αυξάνεται απότομα από 0,2 όταν το n είναι 1,
#| σε 0,25 όταν το n είναι ~1000. Η μέση απόδοση συνεχίζει να αυξάνεται
#| γραμμικά σε πολύ μικρότερη κλίση φτάνοντας το ~0,3 όταν το n είναι ~15.000.
batters |>
filter(n > 100) |>
ggplot(aes(x = n, y = performance)) +
geom_point(alpha = 1 / 10) +
geom_smooth(se = FALSE)
```
Σημειώστε το χρήσιμο μοτίβο για το συνδυασμό ggplot2 και dplyr.
Απλώς πρέπει να θυμάστε να μεταβείτε από το `|>`, για την επεξεργασία δεδομένων, στο `+` για την προσθήκη επιπέδων στο διάγραμμα σας.
Ο τρόπος που θα διατάξετε τα δεδομένα είναι επίσης σημαντικός.
Αν αφελώς ταξινομήσετε σύμφωνα με την `dec(performance)`, τα άτομα με τους καλύτερους μέσους όρους είναι σαφώς αυτοί που προσπάθησαν να βάλουν την μπάλα στο παιχνίδι πολύ λίγες φορές και έτυχε να χτυπήσουν, και δεν είναι απαραίτητα οι πιο ικανοί παίκτες:
```{r}
batters |>
arrange(desc(performance))
```
Μπορείτε να βρείτε μία καλή εξήγηση για αυτό το πρόβλημα και πώς να το ξεπεράσετε στα <http://varianceexplained.org/r/empirical_bayes_baseball/> και <https://www.evanmiller.org/how-not-to-sort-by-average-rating.html>.
## Σύνοψη
Σε αυτό το κεφάλαιο, μάθατε τα εργαλεία που παρέχει το πακέτο dplyr για την εργασία μας με πλαίσια δεδομένων.
Τα εργαλεία ομαδοποιούνται χονδρικά σε τρεις κατηγορίες: αυτά που χειρίζονται τις γραμμές (όπως οι `filter()` και `arrange()`, αυτά που χειρίζονται τις στήλες (όπως οι `select()` και `mutate()`) και αυτά που χειρίζονται ομάδες (όπως οι `group_by()` και `summarize()`).
Σε αυτό το κεφάλαιο, έχουμε επικεντρωθεί στα εργαλεία "ολόκληρου πλαισίου δεδομένων", αλλά δεν έχετε μάθει ακόμα πολλά για το τι μπορείτε να κάνετε με μεμονωμένες μεταβλητές.
Θα επανέλθουμε σε αυτό στο μέρος Μετασχηματισμός του βιβλίου, όπου κάθε κεφάλαιο θα σας παρέχει εργαλεία για έναν συγκεκριμένο τύπο μεταβλητής.
Στο επόμενο κεφάλαιο, θα επιστρέψουμε στη ροή εργασιών για να συζητήσουμε τη σημασία του τρόπου γραφής κώδικα, διατηρώντας τον κώδικά σας καλά οργανωμένο, ώστε να είναι εύκολο για εσάς και τους άλλους να διαβάσουν και να κατανοήσουν τον κώδικά σας.