-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsearch.json
More file actions
924 lines (924 loc) · 348 KB
/
Copy pathsearch.json
File metadata and controls
924 lines (924 loc) · 348 KB
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
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
[
{
"objectID": "problem-sets/problem-sets.html",
"href": "problem-sets/problem-sets.html",
"title": "Problem Sets",
"section": "",
"text": "Problem Sets\nThe bootcamp will not have problem sets.\nData and examples to work through will be provided for future sessions.",
"crumbs": [
"Assignments",
"Problem Sets"
]
},
{
"objectID": "index.html",
"href": "index.html",
"title": "Systems Immunology Bootcamp",
"section": "",
"text": "Instructor learning materials for data analysis module of Research Design and Analysis, 2025.\nThis material is prepared through collaboration between Chris Magnano, PhD (Core for Computational Biomedicine at HMS), Eren Ada, PhD (Department of Immunology, HMS) and Martin Hemberg, PhD (Department of Neurology and Immunology, HMS)."
},
{
"objectID": "index.html#schedule",
"href": "index.html#schedule",
"title": "Systems Immunology Bootcamp",
"section": "Schedule",
"text": "Schedule"
},
{
"objectID": "index.html#footnotes",
"href": "index.html#footnotes",
"title": "Systems Immunology Bootcamp",
"section": "Footnotes",
"text": "Footnotes\n\n\nSessions will be in-person only.↩︎"
},
{
"objectID": "resources/quarto-reports.html",
"href": "resources/quarto-reports.html",
"title": "Quarto and RMarkdown",
"section": "",
"text": "Quarto and RMarkdown\nR Markdown is a widely-used tool for creating automated, reproducible, and share-worthy outputs, such as reports. It can generate static or interactive outputs, in Word, pdf, html, powerpoint, and other formats.\nAn R Markdown script intersperces R code and text such that the script actually becomes your output document. You can create an entire formatted document, including narrative text (can be dynamic to change based on your data), tables, figures, bullets/numbers, bibliographies, etc.\nBelow is very upbeat 1 minute video defining R Markdown, taken from an official beginner’s guide by the creators of RStudio.\n\nThe guide is a great resource for understanding what R Markdown is.\nQuarto is the new R Markdown put out by Posit, the makers of RStudio (and formerly named RStudio Inc.). You can find a quick tutorial on Quarto here\nR for Data Science also has a great chapter on Quarto.\nThis entire website and all materials are written in Quarto.",
"crumbs": [
"Resources",
"Quarto and RMarkdown"
]
},
{
"objectID": "resources/getting-help.html",
"href": "resources/getting-help.html",
"title": "Where to get help",
"section": "",
"text": "Where to get help\n\nUse the built-in RStudio help interface to search for more information on R functions\n\n\n\n\n\nRStudio help interface.\n\n\n\n\nOne of the fastest ways to get help, is to use the RStudio help interface. This panel by default can be found at the lower right hand panel of RStudio. As seen in the screenshot, by typing the word “Mean”, RStudio tries to also give a number of suggestions that you might be interested in. The description is then shown in the display window.\n\n\nI know the name of the function I want to use, but I’m not sure how to use it\nIf you need help with a specific function, let’s say barplot(), you can type:\n\n?barplot\n\nIf you just need to remind yourself of the names of the arguments, you can use:\n\nargs(lm)\n\n\n\nI want to use a function that does X, there must be a function for it but I don’t know which one…\nIf you are looking for a function to do a particular task, you can use the help.search() function, which is called by the double question mark ??. However, this only looks through the installed packages for help pages with a match to your search request\n\n??kruskal\n\nIf you can’t find what you are looking for, you can use the rdocumentation.org website that searches through the help files across all packages available.\nFinally, a generic Google or internet search “R <task>” will often either send you to the appropriate package documentation or a helpful forum where someone else has already asked your question.\n\n\nI am stuck… I get an error message that I don’t understand\nStart by googling the error message. However, this doesn’t always work very well because often, package developers rely on the error catching provided by R. You end up with general error messages that might not be very helpful to diagnose a problem (e.g. “subscript out of bounds”). If the message is very generic, you might also include the name of the function or package you’re using in your query.\nHowever, you should check Stack Overflow. Search using the [r] tag. Most questions have already been answered, but the challenge is to use the right words in the search to find the answers:\nhttp://stackoverflow.com/questions/tagged/r\nThe Introduction to R can also be dense for people with little programming experience but it is a good place to understand the underpinnings of the R language.\nThe R FAQ is dense and technical but it is full of useful information.\n\n\nAsking for help\nThe key to receiving help from someone is for them to rapidly grasp your problem. You should make it as easy as possible to pinpoint where the issue might be.\nTry to use the correct words to describe your problem. For instance, a package is not the same thing as a library. Most people will understand what you meant, but others have really strong feelings about the difference in meaning. The key point is that it can make things confusing for people trying to help you. Be as precise as possible when describing your problem.\nIf possible, try to reduce what doesn’t work to a simple reproducible example. If you can reproduce the problem using a very small data frame instead of your 50000 rows and 10000 columns one, provide the small one with the description of your problem. When appropriate, try to generalise what you are doing so even people who are not in your field can understand the question. For instance instead of using a subset of your real dataset, create a small (3 columns, 5 rows) generic one. For more information on how to write a reproducible example see this article by Hadley Wickham.\nTo share an object with someone else, if it’s relatively small, you can use the function dput(). It will output R code that can be used to recreate the exact same object as the one in memory:\n\n## iris is an example data frame that comes with R and head() is a\n## function that returns the first part of the data frame\ndput(head(iris))\n\nstructure(list(Sepal.Length = c(5.1, 4.9, 4.7, 4.6, 5, 5.4), \n Sepal.Width = c(3.5, 3, 3.2, 3.1, 3.6, 3.9), Petal.Length = c(1.4, \n 1.4, 1.3, 1.5, 1.4, 1.7), Petal.Width = c(0.2, 0.2, 0.2, \n 0.2, 0.2, 0.4), Species = structure(c(1L, 1L, 1L, 1L, 1L, \n 1L), levels = c(\"setosa\", \"versicolor\", \"virginica\"), class = \"factor\")), row.names = c(NA, \n6L), class = \"data.frame\")\n\n\nIf the object is larger, provide either the raw file (i.e., your CSV file) with your script up to the point of the error (and after removing everything that is not relevant to your issue). Alternatively, in particular if your question is not related to a data frame, you can save any R object to a file[^export]:\n\nsaveRDS(iris, file=\"/tmp/iris.rds\")\n\nThe content of this file is however not human readable and cannot be posted directly on Stack Overflow. Instead, it can be sent to someone by email who can read it with the readRDS() command (here it is assumed that the downloaded file is in a Downloads folder in the user’s home directory):\n\nsome_data <- readRDS(file=\"~/Downloads/iris.rds\")\n\nLast, but certainly not least, always include the output of sessionInfo() as it provides critical information about your platform, the versions of R and the packages that you are using, and other information that can be very helpful to understand your problem.\n\nsessionInfo()\n\nR version 4.5.0 (2025-04-11)\nPlatform: aarch64-apple-darwin20\nRunning under: macOS Sequoia 15.6.1\n\nMatrix products: default\nBLAS: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRblas.0.dylib \nLAPACK: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.1\n\nlocale:\n[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8\n\ntime zone: America/New_York\ntzcode source: internal\n\nattached base packages:\n[1] stats graphics grDevices utils datasets methods base \n\nloaded via a namespace (and not attached):\n [1] htmlwidgets_1.6.4 compiler_4.5.0 fastmap_1.2.0 cli_3.6.5 \n [5] tools_4.5.0 htmltools_0.5.8.1 rmarkdown_2.29 knitr_1.50 \n [9] jsonlite_2.0.0 xfun_0.52 digest_0.6.37 rlang_1.1.6 \n[13] evaluate_1.0.3 \n\n\n\n\nWhere to ask for help?\n\nThe person sitting next to you. Don’t hesitate to talk to your neighbour during the workshop, compare your answers, and ask for help.\nThe instructors. We’re here to help you.\nYour friendly colleagues: if you know someone with more experience than you, they might be able and willing to help you.\nStack Overflow: if your question hasn’t been answered before and is well crafted, chances are you will get an answer in less than 5 min. Remember to follow their guidelines on how to ask a good question.\nThe R-help mailing list: it is read by a lot of people (including most of the R core team), a lot of people post to it, but the tone can be pretty dry, and it is not always very welcoming to new users. If your question is valid, you are likely to get an answer very fast but don’t expect that it will come with smiley faces. Also, here more than anywhere else, be sure to use correct vocabulary (otherwise you might get an answer pointing to the misuse of your words rather than answering your question). You will also have more success if your question is about a base function rather than a specific package.\nIf your question is about a specific package, see if there is a mailing list for it. Usually it’s included in the DESCRIPTION file of the package that can be accessed using packageDescription(\"name-of-package\"). You may also want to try to email the author of the package directly, or open an issue on the code repository (e.g., GitHub).\nThere are also some topic-specific mailing lists (GIS, phylogenetics, etc…), the complete list is here.\n\n\n\nMore resources\n\nThe Posting Guide for the R mailing lists.\nHow to ask for R help useful guidelines.\nThis blog post by Jon Skeet has quite comprehensive advice on how to ask programming questions.\nThe reprex package is very helpful to create reproducible examples when asking for help. The rOpenSci community call “How to ask questions so they get answered” (Github link and video recording) includes a presentation of the reprex package and of its philosophy.\n\n\nThe materials in this lesson have been adapted from the Introduction to data analysis with R and Bioconductor workshop, which is a part of the Carpentries Incubator. These are open access materials distributed under the terms of the Creative Commons Attribution license (CC BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.",
"crumbs": [
"Resources",
"Where to get help"
]
},
{
"objectID": "session-materials/session1/other-data-types.html",
"href": "session-materials/session1/other-data-types.html",
"title": "Other Base R Types",
"section": "",
"text": "Since factors are special vectors, the same rules for selecting values using indices apply.\n\nexpression <- factor(c(\"high\",\"low\",\"low\",\"medium\",\"high\",\"medium\",\"medium\",\"low\",\"low\",\"low\"))\n\nIn this vector we can imagine gene expression data has been stored as 3 categories or levels: low, medium, and high.\nLet’s extract the values of the factor with high expression:\n\nexpression[expression == \"high\"] ## This will only return those elements in the factor equal to \"high\"\n\n[1] high high\nLevels: high low medium\n\n\nUnder the hood, factors are stored as integer values in R. To view the integer assignments under the hood you can use str():\n\nstr(expression)\n\n Factor w/ 3 levels \"high\",\"low\",\"medium\": 1 2 2 3 1 3 3 2 2 2\n\n\nThe categories are referred to as “factor levels”. As we learned earlier, the levels in the expression factor were assigned integers alphabetically, with high=1, low=2, medium=3. However, it makes more sense for us if low=1, medium=2 and high=3. We can change the order of the categories by releveling the factor.\nTo relevel the categories, you can add the levels argument to the factor() function, and give it a vector with the categories listed in the required order:\n\nexpression <- factor(expression, levels=c(\"low\", \"medium\", \"high\")) # you can re-factor a factor \n\nNow we have a releveled factor with low as the lowest or first category, medium as the second and high as the third. This is reflected in the way they are listed in the output of str(), as well as in the numbering of which category is where in the factor.\n\nstr(expression)\n\n Factor w/ 3 levels \"low\",\"medium\",..: 3 1 1 2 3 2 2 1 1 1\n\n\n\nNote: Releveling often becomes necessary when you need a specific category in a factor to be the “base” category, i.e. category that is equal to 1. One example would be if you need the “control” to be the “base” in a given RNA-seq experiment.\n\n\n\n\nA data.frame is the de facto data structure for most tabular data and what we use for statistics and plotting. A data.frame is similar to a matrix in that it’s a collection of vectors of the same length and each vector represents a column. However, in a dataframe each vector can be of a different data type (e.g., characters, integers, factors).\nWe can create a dataframe by bringing vectors together to form the columns. We do this using the data.frame() function, and giving the function the different vectors we would like to bind together. This function will only work for vectors of the same length.\n\n# First let's make some accompanying name and expression data for our expression levels\ngene_names <- c(\"Asl\",\"Apod\",\"Cyp2d22\",\"Klk6\",\"Fcrls\",\"Slc2a4\",\"Exd2\",\"Gjc2\",\"Plp1\",\"Gnb4\")\n\n# Generating random data inline with the expression values\nnumeric_expression <- (as.numeric(expression) * 1000) + (rnorm(10) * 900)\n\n# Create a data frame and store it as a variable called 'df'\ndf <- data.frame(gene_names, expression, numeric_expression)\n\nWe can see that a new variable called df has been created in our Environment within a new section called Data. In the Environment, it specifies that df has 3 observations of 2 variables. What does that mean? In R, rows always come first, so it means that df has 3 rows and 2 columns. We can get additional information if we click on the blue circle with the white triangle in the middle next to df. It will display information about each of the columns in the data frame, giving information about what the data type is of each of the columns and the first few values of those columns.\nAnother handy feature in RStudio is that if we hover the cursor over the variable name in the Environment, df, it will turn into a pointing finger. If you click on df, it will open the data frame as it’s own tab next to the script editor. We can explore the table interactively within this window. To close, just click on the X on the tab.\nAs with any variable, we can print the values stored inside to the console if we type the variable’s name and run.\n\ndf\n\n gene_names expression numeric_expression\n1 Asl high 3735.0829\n2 Apod low -220.2704\n3 Cyp2d22 low 531.6932\n4 Klk6 medium 2747.9484\n5 Fcrls high 102.4606\n6 Slc2a4 medium 886.8212\n7 Exd2 medium 1131.1377\n8 Gjc2 low 1063.5488\n9 Plp1 low 2341.0070\n10 Gnb4 low 1010.7793\n\n\n\n\n\nLists are a data structure in R that can be perhaps a bit daunting at first, but soon become amazingly useful. A list is a data structure that can hold any number of any types of other data structures.\nIf you have variables of different data structures you wish to combine, you can put all of those into one list object by using the list() function and placing all the items you wish to combine within parentheses:\n\nage <- 102\nlist1 <- list(expression, df, age)\n\nWe see list1 appear within the Data section of our environment as a list of 3 components or variables. If we click on the blue circle with a triangle in the middle, it’s not quite as interpretable as it was for data frames.\nEssentially, each component is preceded by a colon. The first colon give the expression vector, the second colon precedes the df data frame, with the dollar signs indicating the different columns, the last colon gives the single value, age.\nLet’s type list1 and print to the console by running it.\n\nlist1\n\n[[1]]\n [1] high low low medium high medium medium low low low \nLevels: low medium high\n\n[[2]]\n gene_names expression numeric_expression\n1 Asl high 3735.0829\n2 Apod low -220.2704\n3 Cyp2d22 low 531.6932\n4 Klk6 medium 2747.9484\n5 Fcrls high 102.4606\n6 Slc2a4 medium 886.8212\n7 Exd2 medium 1131.1377\n8 Gjc2 low 1063.5488\n9 Plp1 low 2341.0070\n10 Gnb4 low 1010.7793\n\n[[3]]\n[1] 102\n\n\nThere are three components corresponding to the three different variables we passed in, and what you see is that structure of each is retained. Each component of a list is referenced based on the number position.\n———————————————————————-s–\nThe materials in this lesson have been adapted from work created by the HBC and Data Carpentry, as well as materials created by Laurent Gatto, Charlotte Soneson, Jenny Drnevich, Robert Castelo, and Kevin Rue-Albert. These are open access materials distributed under the terms of the Creative Commons Attribution license (CC BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.",
"crumbs": [
"In-Class Materials",
"Session 1",
"Other Base R Types"
]
},
{
"objectID": "session-materials/session1/other-data-types.html#categorical-data",
"href": "session-materials/session1/other-data-types.html#categorical-data",
"title": "Other Base R Types",
"section": "",
"text": "Since factors are special vectors, the same rules for selecting values using indices apply.\n\nexpression <- factor(c(\"high\",\"low\",\"low\",\"medium\",\"high\",\"medium\",\"medium\",\"low\",\"low\",\"low\"))\n\nIn this vector we can imagine gene expression data has been stored as 3 categories or levels: low, medium, and high.\nLet’s extract the values of the factor with high expression:\n\nexpression[expression == \"high\"] ## This will only return those elements in the factor equal to \"high\"\n\n[1] high high\nLevels: high low medium\n\n\nUnder the hood, factors are stored as integer values in R. To view the integer assignments under the hood you can use str():\n\nstr(expression)\n\n Factor w/ 3 levels \"high\",\"low\",\"medium\": 1 2 2 3 1 3 3 2 2 2\n\n\nThe categories are referred to as “factor levels”. As we learned earlier, the levels in the expression factor were assigned integers alphabetically, with high=1, low=2, medium=3. However, it makes more sense for us if low=1, medium=2 and high=3. We can change the order of the categories by releveling the factor.\nTo relevel the categories, you can add the levels argument to the factor() function, and give it a vector with the categories listed in the required order:\n\nexpression <- factor(expression, levels=c(\"low\", \"medium\", \"high\")) # you can re-factor a factor \n\nNow we have a releveled factor with low as the lowest or first category, medium as the second and high as the third. This is reflected in the way they are listed in the output of str(), as well as in the numbering of which category is where in the factor.\n\nstr(expression)\n\n Factor w/ 3 levels \"low\",\"medium\",..: 3 1 1 2 3 2 2 1 1 1\n\n\n\nNote: Releveling often becomes necessary when you need a specific category in a factor to be the “base” category, i.e. category that is equal to 1. One example would be if you need the “control” to be the “base” in a given RNA-seq experiment.\n\n\n\n\nA data.frame is the de facto data structure for most tabular data and what we use for statistics and plotting. A data.frame is similar to a matrix in that it’s a collection of vectors of the same length and each vector represents a column. However, in a dataframe each vector can be of a different data type (e.g., characters, integers, factors).\nWe can create a dataframe by bringing vectors together to form the columns. We do this using the data.frame() function, and giving the function the different vectors we would like to bind together. This function will only work for vectors of the same length.\n\n# First let's make some accompanying name and expression data for our expression levels\ngene_names <- c(\"Asl\",\"Apod\",\"Cyp2d22\",\"Klk6\",\"Fcrls\",\"Slc2a4\",\"Exd2\",\"Gjc2\",\"Plp1\",\"Gnb4\")\n\n# Generating random data inline with the expression values\nnumeric_expression <- (as.numeric(expression) * 1000) + (rnorm(10) * 900)\n\n# Create a data frame and store it as a variable called 'df'\ndf <- data.frame(gene_names, expression, numeric_expression)\n\nWe can see that a new variable called df has been created in our Environment within a new section called Data. In the Environment, it specifies that df has 3 observations of 2 variables. What does that mean? In R, rows always come first, so it means that df has 3 rows and 2 columns. We can get additional information if we click on the blue circle with the white triangle in the middle next to df. It will display information about each of the columns in the data frame, giving information about what the data type is of each of the columns and the first few values of those columns.\nAnother handy feature in RStudio is that if we hover the cursor over the variable name in the Environment, df, it will turn into a pointing finger. If you click on df, it will open the data frame as it’s own tab next to the script editor. We can explore the table interactively within this window. To close, just click on the X on the tab.\nAs with any variable, we can print the values stored inside to the console if we type the variable’s name and run.\n\ndf\n\n gene_names expression numeric_expression\n1 Asl high 3735.0829\n2 Apod low -220.2704\n3 Cyp2d22 low 531.6932\n4 Klk6 medium 2747.9484\n5 Fcrls high 102.4606\n6 Slc2a4 medium 886.8212\n7 Exd2 medium 1131.1377\n8 Gjc2 low 1063.5488\n9 Plp1 low 2341.0070\n10 Gnb4 low 1010.7793\n\n\n\n\n\nLists are a data structure in R that can be perhaps a bit daunting at first, but soon become amazingly useful. A list is a data structure that can hold any number of any types of other data structures.\nIf you have variables of different data structures you wish to combine, you can put all of those into one list object by using the list() function and placing all the items you wish to combine within parentheses:\n\nage <- 102\nlist1 <- list(expression, df, age)\n\nWe see list1 appear within the Data section of our environment as a list of 3 components or variables. If we click on the blue circle with a triangle in the middle, it’s not quite as interpretable as it was for data frames.\nEssentially, each component is preceded by a colon. The first colon give the expression vector, the second colon precedes the df data frame, with the dollar signs indicating the different columns, the last colon gives the single value, age.\nLet’s type list1 and print to the console by running it.\n\nlist1\n\n[[1]]\n [1] high low low medium high medium medium low low low \nLevels: low medium high\n\n[[2]]\n gene_names expression numeric_expression\n1 Asl high 3735.0829\n2 Apod low -220.2704\n3 Cyp2d22 low 531.6932\n4 Klk6 medium 2747.9484\n5 Fcrls high 102.4606\n6 Slc2a4 medium 886.8212\n7 Exd2 medium 1131.1377\n8 Gjc2 low 1063.5488\n9 Plp1 low 2341.0070\n10 Gnb4 low 1010.7793\n\n[[3]]\n[1] 102\n\n\nThere are three components corresponding to the three different variables we passed in, and what you see is that structure of each is retained. Each component of a list is referenced based on the number position.\n———————————————————————-s–\nThe materials in this lesson have been adapted from work created by the HBC and Data Carpentry, as well as materials created by Laurent Gatto, Charlotte Soneson, Jenny Drnevich, Robert Castelo, and Kevin Rue-Albert. These are open access materials distributed under the terms of the Creative Commons Attribution license (CC BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.",
"crumbs": [
"In-Class Materials",
"Session 1",
"Other Base R Types"
]
},
{
"objectID": "session-materials/session1/missing-data-and-generators.html",
"href": "session-materials/session1/missing-data-and-generators.html",
"title": "Missing Data and Generators",
"section": "",
"text": "As R was designed to analyze datasets, it includes the concept of missing data (which is uncommon in other programming languages). Missing data are represented in vectors as NA.\nWhen doing operations on numbers, most functions will return NA if the data you are working with include missing values. This feature makes it harder to overlook the cases where you are dealing with missing data. You can add the argument na.rm = TRUE to calculate the result while ignoring the missing values.\n\nheights <- c(2, 4, 4, NA, 6)\nmean(heights)\n\n[1] NA\n\nmax(heights)\n\n[1] NA\n\nmean(heights, na.rm = TRUE)\n\n[1] 4\n\nmax(heights, na.rm = TRUE)\n\n[1] 6\n\n\nIf your data include missing values, you may want to become familiar with the functions is.na(), na.omit(), and complete.cases(). See below for examples.\n\n## Extract those elements which are not missing values.\nheights[!is.na(heights)]\n\n[1] 2 4 4 6\n\n## Returns the object with incomplete cases removed.\n## The returned object is an atomic vector of type `\"numeric\"`\n## (or `\"double\"`).\nna.omit(heights)\n\n[1] 2 4 4 6\nattr(,\"na.action\")\n[1] 4\nattr(,\"class\")\n[1] \"omit\"\n\n## Extract those elements which are complete cases.\n## The returned object is an atomic vector of type `\"numeric\"`\n## (or `\"double\"`).\nheights[complete.cases(heights)]\n\n[1] 2 4 4 6\n\n\n\n\n\n\n\n\nChallenge:\n\n\n\n\nUsing this vector of heights in inches, create a new vector with the NAs removed.\n\n\nheights <- c(63, 69, 60, 65, NA, 68, 61, 70, 61, 59, 64, 69, 63, 63, NA, 72, 65, 64, 70, 63, 65)\n\n\nUse the function median() to calculate the median of the heights vector.\nUse R to figure out how many people in the set are taller than 67 inches.\n\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\nheights_no_na <- heights[!is.na(heights)]\n## or\nheights_no_na <- na.omit(heights)\n\n\nmedian(heights, na.rm = TRUE)\n\n[1] 64\n\n\n\nheights_above_67 <- heights_no_na[heights_no_na > 67]\nlength(heights_above_67)\n\n[1] 6",
"crumbs": [
"In-Class Materials",
"Session 1",
"Missing Data and Generators"
]
},
{
"objectID": "session-materials/session1/missing-data-and-generators.html#missing-data",
"href": "session-materials/session1/missing-data-and-generators.html#missing-data",
"title": "Missing Data and Generators",
"section": "",
"text": "As R was designed to analyze datasets, it includes the concept of missing data (which is uncommon in other programming languages). Missing data are represented in vectors as NA.\nWhen doing operations on numbers, most functions will return NA if the data you are working with include missing values. This feature makes it harder to overlook the cases where you are dealing with missing data. You can add the argument na.rm = TRUE to calculate the result while ignoring the missing values.\n\nheights <- c(2, 4, 4, NA, 6)\nmean(heights)\n\n[1] NA\n\nmax(heights)\n\n[1] NA\n\nmean(heights, na.rm = TRUE)\n\n[1] 4\n\nmax(heights, na.rm = TRUE)\n\n[1] 6\n\n\nIf your data include missing values, you may want to become familiar with the functions is.na(), na.omit(), and complete.cases(). See below for examples.\n\n## Extract those elements which are not missing values.\nheights[!is.na(heights)]\n\n[1] 2 4 4 6\n\n## Returns the object with incomplete cases removed.\n## The returned object is an atomic vector of type `\"numeric\"`\n## (or `\"double\"`).\nna.omit(heights)\n\n[1] 2 4 4 6\nattr(,\"na.action\")\n[1] 4\nattr(,\"class\")\n[1] \"omit\"\n\n## Extract those elements which are complete cases.\n## The returned object is an atomic vector of type `\"numeric\"`\n## (or `\"double\"`).\nheights[complete.cases(heights)]\n\n[1] 2 4 4 6\n\n\n\n\n\n\n\n\nChallenge:\n\n\n\n\nUsing this vector of heights in inches, create a new vector with the NAs removed.\n\n\nheights <- c(63, 69, 60, 65, NA, 68, 61, 70, 61, 59, 64, 69, 63, 63, NA, 72, 65, 64, 70, 63, 65)\n\n\nUse the function median() to calculate the median of the heights vector.\nUse R to figure out how many people in the set are taller than 67 inches.\n\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\nheights_no_na <- heights[!is.na(heights)]\n## or\nheights_no_na <- na.omit(heights)\n\n\nmedian(heights, na.rm = TRUE)\n\n[1] 64\n\n\n\nheights_above_67 <- heights_no_na[heights_no_na > 67]\nlength(heights_above_67)\n\n[1] 6",
"crumbs": [
"In-Class Materials",
"Session 1",
"Missing Data and Generators"
]
},
{
"objectID": "session-materials/session1/missing-data-and-generators.html#sec:genvec",
"href": "session-materials/session1/missing-data-and-generators.html#sec:genvec",
"title": "Missing Data and Generators",
"section": "Generating vectors",
"text": "Generating vectors\nThere exists some functions to generate vectors of different type. To generate a vector of numerics, one can use the numeric() constructor, providing the length of the output vector as parameter. The values will be initialised with 0.\n\nnumeric(3)\n\n[1] 0 0 0\n\nnumeric(10)\n\n [1] 0 0 0 0 0 0 0 0 0 0\n\n\nNote that if we ask for a vector of numerics of length 0, we obtain exactly that:\n\nnumeric(0)\n\nnumeric(0)\n\n\nThere are similar constructors for characters and logicals, named character() and logical() respectively.\n\n\n\n\n\n\nChallenge:\n\n\n\nWhat are the defaults for character and logical vectors?\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\ncharacter(2) ## the empty character\n\n[1] \"\" \"\"\n\nlogical(2) ## FALSE\n\n[1] FALSE FALSE\n\n\n\n\n\n\nThe materials in this lesson have been adapted from work created by the HBC and Data Carpentry, as well as materials created by Laurent Gatto, Charlotte Soneson, Jenny Drnevich, Robert Castelo, and Kevin Rue-Albert. These are open access materials distributed under the terms of the Creative Commons Attribution license (CC BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.",
"crumbs": [
"In-Class Materials",
"Session 1",
"Missing Data and Generators"
]
},
{
"objectID": "session-materials/sessions.html",
"href": "session-materials/sessions.html",
"title": "Sessions",
"section": "",
"text": "Sessions\nThis set of pages contains the in-class materials we will be using.\nYou can browse the materials for each Session on this website, but for class you need to download these materials and datasets.\n\nSession 1: Getting started with R\nDownload Session 1 Materials Here\n\n\nSession 2: Functions and Data Wrangling\nDownload Session 2 Materials Here\n\n\nSession 3: Data Exploration & Visualization\nDownload Session 4 Materials Here\n\n\nSession 4: Basic Statistics & t-tests\nDownload Session 4 Materials Here\n\n\nSession 5: Bulk RNA-seq\nDownload Session 5 Materials Here\n\n\nSession 6 & 7: scRNA-seq\nFor session 6 we will be working from the materials found here and data found here",
"crumbs": [
"In-Class Materials",
"Sessions"
]
},
{
"objectID": "session-materials/session5/de_analysis.html",
"href": "session-materials/session5/de_analysis.html",
"title": "Differential expression analysis with DESeq2",
"section": "",
"text": "A basic task in the analysis of RNA-seq count data is the detection of differentially expressed genes. The count data are presented as a table which reports, for each sample, the number of sequence fragments that have been assigned to each gene. An important analysis question is the quantification and statistical inference of systematic changes between conditions, as compared to within-condition variability.\nWe start by loading the DESeq2 package, a very popular method for analysing differential expression of bulk RNA-seq data.\n\n# This chunk ensures all necessary packages are installed and loaded.\n\n# List of CRAN packages\ncran_packages <- c(\"tidyverse\")\n\n# List of Bioconductor packages\nbioc_packages <- c(\"DESeq2\", \"airway\")\n\n# Check and install CRAN packages\nfor (pkg in cran_packages) {\n if (!require(pkg, character.only = TRUE, quietly = TRUE)) {\n install.packages(pkg, dependencies = TRUE, repos = \"https://cran.rstudio.com/\")\n }\n}\n\n# Check and install Bioconductor packages\nif (!requireNamespace(\"BiocManager\", quietly = TRUE)) {\n install.packages(\"BiocManager\", repos = \"https://cran.rstudio.com/\")\n}\n\nfor (pkg in bioc_packages) {\n if (!require(pkg, character.only = TRUE, quietly = TRUE)) {\n BiocManager::install(pkg, dependencies = TRUE)\n }\n}\n\n# Load all packages\ninvisible(lapply(c(cran_packages, bioc_packages), library, character.only = TRUE))\n\nDESeq2 requires count data like that in the SummarizedExperiment we have been working with.\nThe airway experimental data package contains an example dataset from an RNA-Seq experiment of read counts per gene for airway smooth muscles. These data are stored in a RangedSummarizedExperiment object which contains 8 different experimental samples and assays 64,102 gene transcripts.\n\n# Load the airway dataset (already loaded above in setup)\ndata(airway) # Load the example dataset\nse <- airway # Assign to shorter variable name\nse # Display the SummarizedExperiment object\n\nclass: RangedSummarizedExperiment \ndim: 63677 8 \nmetadata(1): ''\nassays(1): counts\nrownames(63677): ENSG00000000003 ENSG00000000005 ... ENSG00000273492\n ENSG00000273493\nrowData names(10): gene_id gene_name ... seq_coord_system symbol\ncolnames(8): SRR1039508 SRR1039509 ... SRR1039520 SRR1039521\ncolData names(9): SampleName cell ... Sample BioSample\n\nrowRanges(se) # Show information about genes/rows\n\nGRangesList object of length 63677:\n$ENSG00000000003\nGRanges object with 17 ranges and 2 metadata columns:\n seqnames ranges strand | exon_id exon_name\n <Rle> <IRanges> <Rle> | <integer> <character>\n [1] X 99883667-99884983 - | 667145 ENSE00001459322\n [2] X 99885756-99885863 - | 667146 ENSE00000868868\n [3] X 99887482-99887565 - | 667147 ENSE00000401072\n [4] X 99887538-99887565 - | 667148 ENSE00001849132\n [5] X 99888402-99888536 - | 667149 ENSE00003554016\n ... ... ... ... . ... ...\n [13] X 99890555-99890743 - | 667156 ENSE00003512331\n [14] X 99891188-99891686 - | 667158 ENSE00001886883\n [15] X 99891605-99891803 - | 667159 ENSE00001855382\n [16] X 99891790-99892101 - | 667160 ENSE00001863395\n [17] X 99894942-99894988 - | 667161 ENSE00001828996\n -------\n seqinfo: 722 sequences (1 circular) from an unspecified genome\n\n...\n<63676 more elements>\n\ncolData(se) # Show information about samples/columns\n\nDataFrame with 8 rows and 9 columns\n SampleName cell dex albut Run avgLength\n <factor> <factor> <factor> <factor> <factor> <integer>\nSRR1039508 GSM1275862 N61311 untrt untrt SRR1039508 126\nSRR1039509 GSM1275863 N61311 trt untrt SRR1039509 126\nSRR1039512 GSM1275866 N052611 untrt untrt SRR1039512 126\nSRR1039513 GSM1275867 N052611 trt untrt SRR1039513 87\nSRR1039516 GSM1275870 N080611 untrt untrt SRR1039516 120\nSRR1039517 GSM1275871 N080611 trt untrt SRR1039517 126\nSRR1039520 GSM1275874 N061011 untrt untrt SRR1039520 101\nSRR1039521 GSM1275875 N061011 trt untrt SRR1039521 98\n Experiment Sample BioSample\n <factor> <factor> <factor>\nSRR1039508 SRX384345 SRS508568 SAMN02422669\nSRR1039509 SRX384346 SRS508567 SAMN02422675\nSRR1039512 SRX384349 SRS508571 SAMN02422678\nSRR1039513 SRX384350 SRS508572 SAMN02422670\nSRR1039516 SRX384353 SRS508575 SAMN02422682\nSRR1039517 SRX384354 SRS508576 SAMN02422673\nSRR1039520 SRX384357 SRS508579 SAMN02422683\nSRR1039521 SRX384358 SRS508580 SAMN02422677\n\n\nThe package requires count data like that in the SummarizedExperiment we have been working with, in addition to a formula describing the experimental design. We use the cell line as a covariate, and dexamethazone treatment as the main factor that we are interested in.\n\ndds <- DESeqDataSet(se, design = ~ cell + dex)\ndds\n\nclass: DESeqDataSet \ndim: 63677 8 \nmetadata(2): '' version\nassays(1): counts\nrownames(63677): ENSG00000000003 ENSG00000000005 ... ENSG00000273492\n ENSG00000273493\nrowData names(10): gene_id gene_name ... seq_coord_system symbol\ncolnames(8): SRR1039508 SRR1039509 ... SRR1039520 SRR1039521\ncolData names(9): SampleName cell ... Sample BioSample\n\n\nThe dds object can be manipulated very much like a SummarizedExperiment (in fact: it is a SummarizedExperiment).\nThere are two reasons which make pre-filtering useful: by removing genes with only few reads across samples, we reduce the size of the dds data object, and thus increase the speed of the transformation and testing functions within DESeq2.\nHere we perform a minimal pre-filtering to keep only rows that have at least 10 reads total.\n\nkeep <- rowSums(counts(dds)) >= 10\ntable(keep)\n\nkeep\nFALSE TRUE \n41308 22369 \n\ndds <- dds[keep,]\n\nThe DESeq workflow is summarized by a single function call, which performs statistical analysis on the data in the dds object.\n\ndds <- DESeq(dds)\n\nestimating size factors\n\n\nestimating dispersions\n\n\ngene-wise dispersion estimates\n\n\nmean-dispersion relationship\n\n\nfinal dispersion estimates\n\n\nfitting model and testing\n\n\nA table summarizing measures of differential expression can be extracted from the object, and visualized or manipulated using commands we learned earlier.\n\nres <- results(dds)\nres\n\nlog2 fold change (MLE): dex untrt vs trt \nWald test p-value: dex untrt vs trt \nDataFrame with 22369 rows and 6 columns\n baseMean log2FoldChange lfcSE stat pvalue\n <numeric> <numeric> <numeric> <numeric> <numeric>\nENSG00000000003 708.5979 0.3812272 0.1007023 3.785685 1.53286e-04\nENSG00000000419 520.2963 -0.2068403 0.1121077 -1.845013 6.50356e-02\nENSG00000000457 237.1621 -0.0379542 0.1428231 -0.265743 7.90437e-01\nENSG00000000460 57.9324 0.0885314 0.2849344 0.310708 7.56023e-01\nENSG00000000971 5817.3108 -0.4264245 0.0888056 -4.801774 1.57267e-06\n... ... ... ... ... ...\nENSG00000273483 2.68955 -0.849208 1.253365 -0.6775424 0.498062\nENSG00000273485 1.28646 0.123614 1.588250 0.0778301 0.937963\nENSG00000273486 15.45244 0.150430 0.482098 0.3120312 0.755017\nENSG00000273487 8.16327 -1.045638 0.693057 -1.5087328 0.131367\nENSG00000273488 8.58437 -0.108945 0.632300 -0.1722990 0.863203\n padj\n <numeric>\nENSG00000000003 1.28920e-03\nENSG00000000419 1.94929e-01\nENSG00000000457 9.09901e-01\nENSG00000000460 8.92994e-01\nENSG00000000971 2.06392e-05\n... ...\nENSG00000273483 NA\nENSG00000273485 NA\nENSG00000273486 0.892518\nENSG00000273487 0.323299\nENSG00000273488 0.943414\n\n\nTask:\nUse the contrast argument of the results function to compare trt vs. untrt groups instead of untrt vs. trt (changes the direction of the fold change).\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\nres_trt_untrt <- results(dds, contrast = c(\"dex\",\"trt\",\"untrt\"))\n\n\n\n\n\n\nA useful illustration of differential expression results is to plot the fold change against the p-value in a volcano plot. This allows to inspect direction and magnitude (fold change) as well as the statistical significance (p-value) of the expression change.\n\nlibrary(ggplot2)\nggplot(as.data.frame(res), \n aes(x = log2FoldChange, y = -log10(padj))) + \n geom_point() +\n geom_hline(yintercept = -log10(0.05), col = \"red\") +\n geom_vline(xintercept = -1, col = \"red\") +\n geom_vline(xintercept = 1, col = \"red\")\n\nWarning: Removed 4337 rows containing missing values or values outside the scale range\n(`geom_point()`).\n\n\n\n\n\n\n\n\n\nWe can get more advanced with creating volcano plots, labeling cells with things like the\n\nlibrary(ggrepel) #This is a good library for displaying text without overlap\nlibrary(biomaRt, quietly = TRUE) #for ID mapping\n\nres_df <- as.data.frame(res)\nmart <- useDataset(\"hsapiens_gene_ensembl\", useMart(\"ensembl\"))\ngenes <- rownames(res_df)\ngene_map <- getBM(filters= \"ensembl_gene_id\", attributes= c(\"ensembl_gene_id\",\"hgnc_symbol\"),values=genes,mart= mart)\nind <- match(rownames(res_df), gene_map$ensembl_gene_id)\nres_df$gene <- gene_map$hgnc_symbol[ind]\n\nres_df <- mutate(res_df, sig = ((padj < 0.05) & abs(log2FoldChange) > 2)) \n ggplot(res_df, aes(x = log2FoldChange, y = -log10(padj), col=sig)) +\n geom_point() +\n geom_vline(xintercept=c(-2, 2), col=\"red\") +\n geom_hline(yintercept=-log10(0.05), col=\"red\") +\n geom_text_repel(data=filter(res_df, sig), aes(label=gene))\n\nWarning: Removed 4337 rows containing missing values or values outside the scale range\n(`geom_point()`).\n\n\nWarning: Removed 11 rows containing missing values or values outside the scale range\n(`geom_text_repel()`).\n\n\nWarning: ggrepel: 211 unlabeled data points (too many overlaps). Consider\nincreasing max.overlaps\n\n\n\n\n\n\n\n\n\n\n\n\nAnother useful illustration of differential expression results is to plot the fold changes as a function of the mean of the expression level (normalized counts) across samples in an MA plot.\nPoints will be colored if the adjusted p-value is less than a defined significance threshold (default: 0.1). Points which fall out of the window are plotted as open triangles pointing either up or down.\n\nplotMA(res)\n\n\n\n\n\n\n\n\nThe DESeq2 vignette also describes several other useful result exploration and data quality assessment plots.\nExercises\n\n\n\n\n\n\nBasic\n\n\n\n\nUse the coef function to examine the actual coefficients of the model.\nGet the number of genes considered significantly expressed at the alpha level of 0.1 (for the adjusted p value).\nNow see how many genes would be considered differentially expressed at an alpha level of 0.05 and a log2 fold change cutoff of at least 1. Note: Take a look again at the arguments of the results function. Are there any you should change?\n\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\n#1. Use the `coef` function to examine the actual coefficients of the model.\ncoef(dds)\n\n#2. \nsum(res$padj<0.1, na.rm = TRUE)\n\n#3. \nres_05_1 <- results(dds, alpha=0.05,lfcThreshold = 1)\nsum((res_05_1$padj<0.1)&(res_05_1$log2FoldChange>=1), na.rm = TRUE)\n\n\n\n\n\n\n\n\n\n\nAdvanced\n\n\n\nLet’s imagine that, instead of all being untreated, half of the samples had been treated with albuterol:\n\nfake_se <- se\n#Currently the factor only has the untrt level, so we need to add another\nlevels(colData(fake_se)$albut) <- c(levels(colData(fake_se)$albut), \"trt\")\ncolData(fake_se)$albut[c(1,3,4,8)] <- \"trt\"\n\nRemake the dds object such that the albut column is an additional covartiate in the experimental design.\nIf there is time, compare the number of significant results (for comparing cell and dex) when albut is and is not accounted for. Does it make a difference?\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\ndds_fake <- DESeqDataSet(fake_se, design = ~ albut + cell + dex)\nkeep <- rowSums(counts(dds_fake)) >= 4\ndds_fake <- dds_fake[keep,]\ndds_fake <- DESeq(dds_fake)\nres_fake <- results(dds_fake)\n\nsum(res$padj<0.1, na.rm = TRUE)\nsum(res_fake$padj<0.1, na.rm = TRUE)\n\n\n\n\n\n\n\n\n\n\nBonus\n\n\n\nTry installing the Glimma package from bioconductor. Use it to create an interactive multidimensional scaling (MDS) plot of your results.\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\nlibrary(Glimma)\nglimmaMDS(dds)\n\n\n\n\n\nThis lesson was adapted from materials created by Ludwig Geistlinger",
"crumbs": [
"In-Class Materials",
"Session 5",
"Differential expression analysis with DESeq2"
]
},
{
"objectID": "session-materials/session5/de_analysis.html#volcano-plot",
"href": "session-materials/session5/de_analysis.html#volcano-plot",
"title": "Differential expression analysis with DESeq2",
"section": "",
"text": "A useful illustration of differential expression results is to plot the fold change against the p-value in a volcano plot. This allows to inspect direction and magnitude (fold change) as well as the statistical significance (p-value) of the expression change.\n\nlibrary(ggplot2)\nggplot(as.data.frame(res), \n aes(x = log2FoldChange, y = -log10(padj))) + \n geom_point() +\n geom_hline(yintercept = -log10(0.05), col = \"red\") +\n geom_vline(xintercept = -1, col = \"red\") +\n geom_vline(xintercept = 1, col = \"red\")\n\nWarning: Removed 4337 rows containing missing values or values outside the scale range\n(`geom_point()`).\n\n\n\n\n\n\n\n\n\nWe can get more advanced with creating volcano plots, labeling cells with things like the\n\nlibrary(ggrepel) #This is a good library for displaying text without overlap\nlibrary(biomaRt, quietly = TRUE) #for ID mapping\n\nres_df <- as.data.frame(res)\nmart <- useDataset(\"hsapiens_gene_ensembl\", useMart(\"ensembl\"))\ngenes <- rownames(res_df)\ngene_map <- getBM(filters= \"ensembl_gene_id\", attributes= c(\"ensembl_gene_id\",\"hgnc_symbol\"),values=genes,mart= mart)\nind <- match(rownames(res_df), gene_map$ensembl_gene_id)\nres_df$gene <- gene_map$hgnc_symbol[ind]\n\nres_df <- mutate(res_df, sig = ((padj < 0.05) & abs(log2FoldChange) > 2)) \n ggplot(res_df, aes(x = log2FoldChange, y = -log10(padj), col=sig)) +\n geom_point() +\n geom_vline(xintercept=c(-2, 2), col=\"red\") +\n geom_hline(yintercept=-log10(0.05), col=\"red\") +\n geom_text_repel(data=filter(res_df, sig), aes(label=gene))\n\nWarning: Removed 4337 rows containing missing values or values outside the scale range\n(`geom_point()`).\n\n\nWarning: Removed 11 rows containing missing values or values outside the scale range\n(`geom_text_repel()`).\n\n\nWarning: ggrepel: 211 unlabeled data points (too many overlaps). Consider\nincreasing max.overlaps",
"crumbs": [
"In-Class Materials",
"Session 5",
"Differential expression analysis with DESeq2"
]
},
{
"objectID": "session-materials/session5/de_analysis.html#ma-plot",
"href": "session-materials/session5/de_analysis.html#ma-plot",
"title": "Differential expression analysis with DESeq2",
"section": "",
"text": "Another useful illustration of differential expression results is to plot the fold changes as a function of the mean of the expression level (normalized counts) across samples in an MA plot.\nPoints will be colored if the adjusted p-value is less than a defined significance threshold (default: 0.1). Points which fall out of the window are plotted as open triangles pointing either up or down.\n\nplotMA(res)\n\n\n\n\n\n\n\n\nThe DESeq2 vignette also describes several other useful result exploration and data quality assessment plots.\nExercises\n\n\n\n\n\n\nBasic\n\n\n\n\nUse the coef function to examine the actual coefficients of the model.\nGet the number of genes considered significantly expressed at the alpha level of 0.1 (for the adjusted p value).\nNow see how many genes would be considered differentially expressed at an alpha level of 0.05 and a log2 fold change cutoff of at least 1. Note: Take a look again at the arguments of the results function. Are there any you should change?\n\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\n#1. Use the `coef` function to examine the actual coefficients of the model.\ncoef(dds)\n\n#2. \nsum(res$padj<0.1, na.rm = TRUE)\n\n#3. \nres_05_1 <- results(dds, alpha=0.05,lfcThreshold = 1)\nsum((res_05_1$padj<0.1)&(res_05_1$log2FoldChange>=1), na.rm = TRUE)\n\n\n\n\n\n\n\n\n\n\nAdvanced\n\n\n\nLet’s imagine that, instead of all being untreated, half of the samples had been treated with albuterol:\n\nfake_se <- se\n#Currently the factor only has the untrt level, so we need to add another\nlevels(colData(fake_se)$albut) <- c(levels(colData(fake_se)$albut), \"trt\")\ncolData(fake_se)$albut[c(1,3,4,8)] <- \"trt\"\n\nRemake the dds object such that the albut column is an additional covartiate in the experimental design.\nIf there is time, compare the number of significant results (for comparing cell and dex) when albut is and is not accounted for. Does it make a difference?\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\ndds_fake <- DESeqDataSet(fake_se, design = ~ albut + cell + dex)\nkeep <- rowSums(counts(dds_fake)) >= 4\ndds_fake <- dds_fake[keep,]\ndds_fake <- DESeq(dds_fake)\nres_fake <- results(dds_fake)\n\nsum(res$padj<0.1, na.rm = TRUE)\nsum(res_fake$padj<0.1, na.rm = TRUE)\n\n\n\n\n\n\n\n\n\n\nBonus\n\n\n\nTry installing the Glimma package from bioconductor. Use it to create an interactive multidimensional scaling (MDS) plot of your results.\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\nlibrary(Glimma)\nglimmaMDS(dds)\n\n\n\n\n\nThis lesson was adapted from materials created by Ludwig Geistlinger",
"crumbs": [
"In-Class Materials",
"Session 5",
"Differential expression analysis with DESeq2"
]
},
{
"objectID": "session-materials/session2/reshaping-and-joining.html",
"href": "session-materials/session2/reshaping-and-joining.html",
"title": "Reshaping and Joining Data",
"section": "",
"text": "In the rna tibble, the rows contain expression values (the unit) that are associated with a combination of 2 other variables: gene and sample.\nAll the other columns correspond to variables describing either the sample (organism, age, sex, …) or the gene (gene_biotype, ENTREZ_ID, product, …). The variables that don’t change with genes or with samples will have the same value in all the rows.\n\nrna %>%\n arrange(gene)\n\n# A tibble: 32,428 × 19\n gene sample expression organism age sex infection strain time tissue\n <chr> <chr> <dbl> <chr> <dbl> <chr> <chr> <chr> <dbl> <chr> \n 1 AI504432 GSM25… 1230 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 2 AI504432 GSM25… 1085 Mus mus… 8 Fema… NonInfec… C57BL… 0 Cereb…\n 3 AI504432 GSM25… 969 Mus mus… 8 Fema… NonInfec… C57BL… 0 Cereb…\n 4 AI504432 GSM25… 1284 Mus mus… 8 Fema… Influenz… C57BL… 4 Cereb…\n 5 AI504432 GSM25… 966 Mus mus… 8 Male Influenz… C57BL… 4 Cereb…\n 6 AI504432 GSM25… 918 Mus mus… 8 Male Influenz… C57BL… 8 Cereb…\n 7 AI504432 GSM25… 985 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 8 AI504432 GSM25… 972 Mus mus… 8 Male NonInfec… C57BL… 0 Cereb…\n 9 AI504432 GSM25… 1000 Mus mus… 8 Fema… Influenz… C57BL… 4 Cereb…\n10 AI504432 GSM25… 816 Mus mus… 8 Male Influenz… C57BL… 4 Cereb…\n# ℹ 32,418 more rows\n# ℹ 9 more variables: mouse <dbl>, ENTREZID <dbl>, product <chr>,\n# ensembl_gene_id <chr>, external_synonym <chr>, chromosome_name <chr>,\n# gene_biotype <chr>, phenotype_description <chr>,\n# hsapiens_homolog_associated_gene_name <chr>\n\n\nThis structure is called a long-format, as one column contains all the values, and other column(s) list(s) the context of the value.\nIn certain cases, the long-format is not really “human-readable”, and another format, a wide-format is preferred, as a more compact way of representing the data. This is typically the case with gene expression values that scientists are used to look as matrices, were rows represent genes and columns represent samples.\nIn this format, it would therefore become straightforward to explore the relationship between the gene expression levels within, and between, the samples.\n\n\n# A tibble: 1,474 × 23\n gene GSM2545336 GSM2545337 GSM2545338 GSM2545339 GSM2545340 GSM2545341\n <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>\n 1 Asl 1170 361 400 586 626 988\n 2 Apod 36194 10347 9173 10620 13021 29594\n 3 Cyp2d22 4060 1616 1603 1901 2171 3349\n 4 Klk6 287 629 641 578 448 195\n 5 Fcrls 85 233 244 237 180 38\n 6 Slc2a4 782 231 248 265 313 786\n 7 Exd2 1619 2288 2235 2513 2366 1359\n 8 Gjc2 288 595 568 551 310 146\n 9 Plp1 43217 101241 96534 58354 53126 27173\n10 Gnb4 1071 1791 1867 1430 1355 798\n# ℹ 1,464 more rows\n# ℹ 16 more variables: GSM2545342 <dbl>, GSM2545343 <dbl>, GSM2545344 <dbl>,\n# GSM2545345 <dbl>, GSM2545346 <dbl>, GSM2545347 <dbl>, GSM2545348 <dbl>,\n# GSM2545349 <dbl>, GSM2545350 <dbl>, GSM2545351 <dbl>, GSM2545352 <dbl>,\n# GSM2545353 <dbl>, GSM2545354 <dbl>, GSM2545362 <dbl>, GSM2545363 <dbl>,\n# GSM2545380 <dbl>\n\n\nTo convert the gene expression values from rna into a wide-format, we need to create a new table where the values of the sample column would become the names of column variables.\nThe key point here is that we are still following a tidy data structure, but we have reshaped the data according to the observations of interest: expression levels per gene instead of recording them per gene and per sample.\nThe opposite transformation would be to transform column names into values of a new variable.\nWe can do both these of transformations with two tidyr functions, pivot_longer() and pivot_wider() (see here for details).\n\n\nLet’s select the first 3 columns of rna and use pivot_wider() to transform the data into a wide-format.\n\nrna_exp <- rna %>%\n select(gene, sample, expression)\nrna_exp\n\n# A tibble: 32,428 × 3\n gene sample expression\n <chr> <chr> <dbl>\n 1 Asl GSM2545336 1170\n 2 Apod GSM2545336 36194\n 3 Cyp2d22 GSM2545336 4060\n 4 Klk6 GSM2545336 287\n 5 Fcrls GSM2545336 85\n 6 Slc2a4 GSM2545336 782\n 7 Exd2 GSM2545336 1619\n 8 Gjc2 GSM2545336 288\n 9 Plp1 GSM2545336 43217\n10 Gnb4 GSM2545336 1071\n# ℹ 32,418 more rows\n\n\npivot_wider takes three main arguments:\n\nthe data to be transformed;\nthe names_from : the column whose values will become new column names;\nthe values_from: the column whose values will fill the new columns.\n\n\n\n\n\n\nWide pivot of the rna data.\n\n\n\n\n\nrna_wide <- rna_exp %>%\n pivot_wider(names_from = sample,\n values_from = expression)\nrna_wide\n\n# A tibble: 1,474 × 23\n gene GSM2545336 GSM2545337 GSM2545338 GSM2545339 GSM2545340 GSM2545341\n <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>\n 1 Asl 1170 361 400 586 626 988\n 2 Apod 36194 10347 9173 10620 13021 29594\n 3 Cyp2d22 4060 1616 1603 1901 2171 3349\n 4 Klk6 287 629 641 578 448 195\n 5 Fcrls 85 233 244 237 180 38\n 6 Slc2a4 782 231 248 265 313 786\n 7 Exd2 1619 2288 2235 2513 2366 1359\n 8 Gjc2 288 595 568 551 310 146\n 9 Plp1 43217 101241 96534 58354 53126 27173\n10 Gnb4 1071 1791 1867 1430 1355 798\n# ℹ 1,464 more rows\n# ℹ 16 more variables: GSM2545342 <dbl>, GSM2545343 <dbl>, GSM2545344 <dbl>,\n# GSM2545345 <dbl>, GSM2545346 <dbl>, GSM2545347 <dbl>, GSM2545348 <dbl>,\n# GSM2545349 <dbl>, GSM2545350 <dbl>, GSM2545351 <dbl>, GSM2545352 <dbl>,\n# GSM2545353 <dbl>, GSM2545354 <dbl>, GSM2545362 <dbl>, GSM2545363 <dbl>,\n# GSM2545380 <dbl>\n\n\nNote that by default, the pivot_wider() function will add NA for missing values.\nLet’s imagine that for some reason, we had some missing expression values for some genes in certain samples. In the following fictive example, the gene Cyp2d22 has only one expression value, in GSM2545338 sample.\n\nrna_with_missing_values\n\n# A tibble: 7 × 3\n gene sample expression\n <chr> <chr> <dbl>\n1 Asl GSM2545336 1170\n2 Apod GSM2545336 36194\n3 Asl GSM2545337 361\n4 Apod GSM2545337 10347\n5 Asl GSM2545338 400\n6 Apod GSM2545338 9173\n7 Cyp2d22 GSM2545338 1603\n\n\nBy default, the pivot_wider() function will add NA for missing values. This can be parameterised with the values_fill argument of the pivot_wider() function.\n\nrna_with_missing_values %>%\n pivot_wider(names_from = sample,\n values_from = expression)\n\n# A tibble: 3 × 4\n gene GSM2545336 GSM2545337 GSM2545338\n <chr> <dbl> <dbl> <dbl>\n1 Asl 1170 361 400\n2 Apod 36194 10347 9173\n3 Cyp2d22 NA NA 1603\n\nrna_with_missing_values %>%\n pivot_wider(names_from = sample,\n values_from = expression,\n values_fill = 0)\n\n# A tibble: 3 × 4\n gene GSM2545336 GSM2545337 GSM2545338\n <chr> <dbl> <dbl> <dbl>\n1 Asl 1170 361 400\n2 Apod 36194 10347 9173\n3 Cyp2d22 0 0 1603\n\n\n\n\n\nIn the opposite situation we are using the column names and turning them into a pair of new variables. One variable represents the column names as values, and the other variable contains the values previously associated with the column names.\npivot_longer() takes four main arguments:\n\nthe data to be transformed;\nthe names_to: the new column name we wish to create and populate with the current column names;\nthe values_to: the new column name we wish to create and populate with current values;\nthe names of the columns to be used to populate the names_to and values_to variables (or to drop).\n\n\n\n\n\n\nLong pivot of the rna data.\n\n\n\n\nTo recreate rna_long from rna_wide we would create a key called sample and value called expression and use all columns except gene for the key variable. Here we drop gene column with a minus sign.\nNotice how the new variable names are to be quoted here.\n\nrna_long <- rna_wide %>%\n pivot_longer(names_to = \"sample\",\n values_to = \"expression\",\n -gene)\nrna_long\n\n# A tibble: 32,428 × 3\n gene sample expression\n <chr> <chr> <dbl>\n 1 Asl GSM2545336 1170\n 2 Asl GSM2545337 361\n 3 Asl GSM2545338 400\n 4 Asl GSM2545339 586\n 5 Asl GSM2545340 626\n 6 Asl GSM2545341 988\n 7 Asl GSM2545342 836\n 8 Asl GSM2545343 535\n 9 Asl GSM2545344 586\n10 Asl GSM2545345 597\n# ℹ 32,418 more rows\n\n\nWe could also have used a specification for what columns to include. This can be useful if you have a large number of identifying columns, and it’s easier to specify what to gather than what to leave alone. Here the starts_with() function can help to retrieve sample names without having to list them all! Another possibility would be to use the : operator!\n\nrna_wide %>%\n pivot_longer(names_to = \"sample\",\n values_to = \"expression\",\n cols = starts_with(\"GSM\"))\n\n# A tibble: 32,428 × 3\n gene sample expression\n <chr> <chr> <dbl>\n 1 Asl GSM2545336 1170\n 2 Asl GSM2545337 361\n 3 Asl GSM2545338 400\n 4 Asl GSM2545339 586\n 5 Asl GSM2545340 626\n 6 Asl GSM2545341 988\n 7 Asl GSM2545342 836\n 8 Asl GSM2545343 535\n 9 Asl GSM2545344 586\n10 Asl GSM2545345 597\n# ℹ 32,418 more rows\n\nrna_wide %>%\n pivot_longer(names_to = \"sample\",\n values_to = \"expression\",\n GSM2545336:GSM2545380)\n\n# A tibble: 32,428 × 3\n gene sample expression\n <chr> <chr> <dbl>\n 1 Asl GSM2545336 1170\n 2 Asl GSM2545337 361\n 3 Asl GSM2545338 400\n 4 Asl GSM2545339 586\n 5 Asl GSM2545340 626\n 6 Asl GSM2545341 988\n 7 Asl GSM2545342 836\n 8 Asl GSM2545343 535\n 9 Asl GSM2545344 586\n10 Asl GSM2545345 597\n# ℹ 32,418 more rows\n\n\nNote that if we had missing values in the wide-format, the NA would be included in the new long format.\nRemember our previous fictive tibble containing missing values:\n\nrna_with_missing_values\n\n# A tibble: 7 × 3\n gene sample expression\n <chr> <chr> <dbl>\n1 Asl GSM2545336 1170\n2 Apod GSM2545336 36194\n3 Asl GSM2545337 361\n4 Apod GSM2545337 10347\n5 Asl GSM2545338 400\n6 Apod GSM2545338 9173\n7 Cyp2d22 GSM2545338 1603\n\nwide_with_NA <- rna_with_missing_values %>%\n pivot_wider(names_from = sample,\n values_from = expression)\nwide_with_NA\n\n# A tibble: 3 × 4\n gene GSM2545336 GSM2545337 GSM2545338\n <chr> <dbl> <dbl> <dbl>\n1 Asl 1170 361 400\n2 Apod 36194 10347 9173\n3 Cyp2d22 NA NA 1603\n\nwide_with_NA %>%\n pivot_longer(names_to = \"sample\",\n values_to = \"expression\",\n -gene)\n\n# A tibble: 9 × 3\n gene sample expression\n <chr> <chr> <dbl>\n1 Asl GSM2545336 1170\n2 Asl GSM2545337 361\n3 Asl GSM2545338 400\n4 Apod GSM2545336 36194\n5 Apod GSM2545337 10347\n6 Apod GSM2545338 9173\n7 Cyp2d22 GSM2545336 NA\n8 Cyp2d22 GSM2545337 NA\n9 Cyp2d22 GSM2545338 1603\n\n\nPivoting to wider and longer formats can be a useful way to balance out a dataset so every replicate has the same composition.\n\n\n\n\n\n\nQuestion\n\n\n\nStarting from the rna table, use the pivot_wider() function to create a wide-format table giving the gene expression levels in each mouse. Then use the pivot_longer() function to restore a long-format table.\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\nrna1 <- rna %>%\nselect(gene, mouse, expression) %>%\npivot_wider(names_from = mouse, values_from = expression)\nrna1\n\n# A tibble: 1,474 × 23\n gene `14` `9` `10` `15` `18` `6` `5` `11` `22` `13` `23`\n <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>\n 1 Asl 1170 361 400 586 626 988 836 535 586 597 938\n 2 Apod 36194 10347 9173 10620 13021 29594 24959 13668 13230 15868 27769\n 3 Cyp2d22 4060 1616 1603 1901 2171 3349 3122 2008 2254 2277 2985\n 4 Klk6 287 629 641 578 448 195 186 1101 537 567 327\n 5 Fcrls 85 233 244 237 180 38 68 375 199 177 89\n 6 Slc2a4 782 231 248 265 313 786 528 249 266 357 654\n 7 Exd2 1619 2288 2235 2513 2366 1359 1474 3126 2379 2173 1531\n 8 Gjc2 288 595 568 551 310 146 186 791 454 370 240\n 9 Plp1 43217 101241 96534 58354 53126 27173 28728 98658 61356 61647 38019\n10 Gnb4 1071 1791 1867 1430 1355 798 806 2437 1394 1554 960\n# ℹ 1,464 more rows\n# ℹ 11 more variables: `24` <dbl>, `8` <dbl>, `7` <dbl>, `1` <dbl>, `16` <dbl>,\n# `21` <dbl>, `4` <dbl>, `2` <dbl>, `20` <dbl>, `12` <dbl>, `19` <dbl>\n\nrna1 %>%\npivot_longer(names_to = \"mouse_id\", values_to = \"counts\", -gene)\n\n# A tibble: 32,428 × 3\n gene mouse_id counts\n <chr> <chr> <dbl>\n 1 Asl 14 1170\n 2 Asl 9 361\n 3 Asl 10 400\n 4 Asl 15 586\n 5 Asl 18 626\n 6 Asl 6 988\n 7 Asl 5 836\n 8 Asl 11 535\n 9 Asl 22 586\n10 Asl 13 597\n# ℹ 32,418 more rows\n\n\n\n\n\n\n\n\n\n\n\nQuestion\n\n\n\nSubset genes located on X and Y chromosomes from the rna data frame and spread the data frame with sex as columns, chromosome_name as rows, and the mean expression of genes located in each chromosome as the values, as in the following tibble:\n\n\n\n\n\n\n\n\n\nYou will need to summarise before reshaping!\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\nLet’s first calculate the mean expression level of X and Y linked genes from male and female samples…\n\n rna %>%\n filter(chromosome_name == \"Y\" | chromosome_name == \"X\") %>%\n group_by(sex, chromosome_name) %>%\n summarise(mean = mean(expression))\n\n`summarise()` has grouped output by 'sex'. You can override using the `.groups`\nargument.\n\n\n# A tibble: 4 × 3\n# Groups: sex [2]\n sex chromosome_name mean\n <chr> <chr> <dbl>\n1 Female X 3504.\n2 Female Y 3 \n3 Male X 2497.\n4 Male Y 2117.\n\n\nAnd pivot the table to wide format\n\nrna_1 <- rna %>%\n filter(chromosome_name == \"Y\" | chromosome_name == \"X\") %>%\n group_by(sex, chromosome_name) %>%\n summarise(mean = mean(expression)) %>%\n pivot_wider(names_from = sex,\n values_from = mean)\n\n`summarise()` has grouped output by 'sex'. You can override using the `.groups`\nargument.\n\nrna_1\n\n# A tibble: 2 × 3\n chromosome_name Female Male\n <chr> <dbl> <dbl>\n1 X 3504. 2497.\n2 Y 3 2117.\n\n\nNow take that data frame and transform it with pivot_longer() so each row is a unique chromosome_name by gender combination.\n\nrna_1 %>%\n pivot_longer(names_to = \"gender\",\n values_to = \"mean\",\n -chromosome_name)\n\n# A tibble: 4 × 3\n chromosome_name gender mean\n <chr> <chr> <dbl>\n1 X Female 3504.\n2 X Male 2497.\n3 Y Female 3 \n4 Y Male 2117.\n\n\n\n\n\n\n\n\n\n\n\nQuestion\n\n\n\nUse the rna dataset to create an expression matrix where each row represents the mean expression levels of genes and columns represent the different timepoints.\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\nLet’s first calculate the mean expression by gene and by time\n\nrna %>%\n group_by(gene, time) %>%\n summarise(mean_exp = mean(expression))\n\n`summarise()` has grouped output by 'gene'. You can override using the\n`.groups` argument.\n\n\n# A tibble: 4,422 × 3\n# Groups: gene [1,474]\n gene time mean_exp\n <chr> <dbl> <dbl>\n 1 AI504432 0 1034.\n 2 AI504432 4 1104.\n 3 AI504432 8 1014 \n 4 AW046200 0 155.\n 5 AW046200 4 152.\n 6 AW046200 8 81 \n 7 AW551984 0 238 \n 8 AW551984 4 302.\n 9 AW551984 8 342.\n10 Aamp 0 4603.\n# ℹ 4,412 more rows\n\n\nbefore using the pivot_wider() function\n\nrna_time <- rna %>%\n group_by(gene, time) %>%\n summarise(mean_exp = mean(expression)) %>%\n pivot_wider(names_from = time,\n values_from = mean_exp)\n\n`summarise()` has grouped output by 'gene'. You can override using the\n`.groups` argument.\n\nrna_time\n\n# A tibble: 1,474 × 4\n# Groups: gene [1,474]\n gene `0` `4` `8`\n <chr> <dbl> <dbl> <dbl>\n 1 AI504432 1034. 1104. 1014 \n 2 AW046200 155. 152. 81 \n 3 AW551984 238 302. 342. \n 4 Aamp 4603. 4870 4763. \n 5 Abca12 5.29 4.25 4.14\n 6 Abcc8 2576. 2609. 2292. \n 7 Abhd14a 591. 547. 432. \n 8 Abi2 4881. 4903. 4945. \n 9 Abi3bp 1175. 1061. 762. \n10 Abl2 2170. 2078. 2131. \n# ℹ 1,464 more rows\n\n\nNotice that this generates a tibble with some column names starting by a number. If we wanted to select the column corresponding to the timepoints, we could not use the column names directly… What happens when we select the column 4?\n\nrna %>%\n group_by(gene, time) %>%\n summarise(mean_exp = mean(expression)) %>%\n pivot_wider(names_from = time,\n values_from = mean_exp) %>%\n select(gene, 4)\n\n`summarise()` has grouped output by 'gene'. You can override using the\n`.groups` argument.\n\n\n# A tibble: 1,474 × 2\n# Groups: gene [1,474]\n gene `8`\n <chr> <dbl>\n 1 AI504432 1014 \n 2 AW046200 81 \n 3 AW551984 342. \n 4 Aamp 4763. \n 5 Abca12 4.14\n 6 Abcc8 2292. \n 7 Abhd14a 432. \n 8 Abi2 4945. \n 9 Abi3bp 762. \n10 Abl2 2131. \n# ℹ 1,464 more rows\n\n\nTo select the timepoint 4, we would have to quote the column name, with backticks “`”\n\nrna %>%\n group_by(gene, time) %>%\n summarise(mean_exp = mean(expression)) %>%\n pivot_wider(names_from = time,\n values_from = mean_exp) %>%\n select(gene, `4`)\n\n`summarise()` has grouped output by 'gene'. You can override using the\n`.groups` argument.\n\n\n# A tibble: 1,474 × 2\n# Groups: gene [1,474]\n gene `4`\n <chr> <dbl>\n 1 AI504432 1104. \n 2 AW046200 152. \n 3 AW551984 302. \n 4 Aamp 4870 \n 5 Abca12 4.25\n 6 Abcc8 2609. \n 7 Abhd14a 547. \n 8 Abi2 4903. \n 9 Abi3bp 1061. \n10 Abl2 2078. \n# ℹ 1,464 more rows\n\n\nAnother possibility would be to rename the column, choosing a name that doesn’t start by a number :\n\nrna %>%\n group_by(gene, time) %>%\n summarise(mean_exp = mean(expression)) %>%\n pivot_wider(names_from = time,\n values_from = mean_exp) %>%\n rename(\"time0\" = `0`, \"time4\" = `4`, \"time8\" = `8`) %>%\n select(gene, time4)\n\n`summarise()` has grouped output by 'gene'. You can override using the\n`.groups` argument.\n\n\n# A tibble: 1,474 × 2\n# Groups: gene [1,474]\n gene time4\n <chr> <dbl>\n 1 AI504432 1104. \n 2 AW046200 152. \n 3 AW551984 302. \n 4 Aamp 4870 \n 5 Abca12 4.25\n 6 Abcc8 2609. \n 7 Abhd14a 547. \n 8 Abi2 4903. \n 9 Abi3bp 1061. \n10 Abl2 2078. \n# ℹ 1,464 more rows\n\n\n\n\n\n\n\n\n\n\n\nQuestion\n\n\n\nUse the previous data frame containing mean expression levels per timepoint and create a new column containing fold-changes between timepoint 8 and timepoint 0, and fold-changes between timepoint 8 and timepoint 4. Convert this table into a long-format table gathering the fold-changes calculated.\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\nStarting from the rna_time tibble:\n\nrna_time\n\n# A tibble: 1,474 × 4\n# Groups: gene [1,474]\n gene `0` `4` `8`\n <chr> <dbl> <dbl> <dbl>\n 1 AI504432 1034. 1104. 1014 \n 2 AW046200 155. 152. 81 \n 3 AW551984 238 302. 342. \n 4 Aamp 4603. 4870 4763. \n 5 Abca12 5.29 4.25 4.14\n 6 Abcc8 2576. 2609. 2292. \n 7 Abhd14a 591. 547. 432. \n 8 Abi2 4881. 4903. 4945. \n 9 Abi3bp 1175. 1061. 762. \n10 Abl2 2170. 2078. 2131. \n# ℹ 1,464 more rows\n\n\nCalculate fold-changes:\n\nrna_time %>%\n mutate(time_8_vs_0 = `8` / `0`, time_8_vs_4 = `8` / `4`)\n\n# A tibble: 1,474 × 6\n# Groups: gene [1,474]\n gene `0` `4` `8` time_8_vs_0 time_8_vs_4\n <chr> <dbl> <dbl> <dbl> <dbl> <dbl>\n 1 AI504432 1034. 1104. 1014 0.981 0.918\n 2 AW046200 155. 152. 81 0.522 0.532\n 3 AW551984 238 302. 342. 1.44 1.13 \n 4 Aamp 4603. 4870 4763. 1.03 0.978\n 5 Abca12 5.29 4.25 4.14 0.784 0.975\n 6 Abcc8 2576. 2609. 2292. 0.889 0.878\n 7 Abhd14a 591. 547. 432. 0.731 0.791\n 8 Abi2 4881. 4903. 4945. 1.01 1.01 \n 9 Abi3bp 1175. 1061. 762. 0.649 0.719\n10 Abl2 2170. 2078. 2131. 0.982 1.03 \n# ℹ 1,464 more rows\n\n\nAnd use the pivot_longer() function:\n\nrna_time %>%\n mutate(time_8_vs_0 = `8` / `0`, time_8_vs_4 = `8` / `4`) %>%\n pivot_longer(names_to = \"comparisons\",\n values_to = \"Fold_changes\",\n time_8_vs_0:time_8_vs_4)\n\n# A tibble: 2,948 × 6\n# Groups: gene [1,474]\n gene `0` `4` `8` comparisons Fold_changes\n <chr> <dbl> <dbl> <dbl> <chr> <dbl>\n 1 AI504432 1034. 1104. 1014 time_8_vs_0 0.981\n 2 AI504432 1034. 1104. 1014 time_8_vs_4 0.918\n 3 AW046200 155. 152. 81 time_8_vs_0 0.522\n 4 AW046200 155. 152. 81 time_8_vs_4 0.532\n 5 AW551984 238 302. 342. time_8_vs_0 1.44 \n 6 AW551984 238 302. 342. time_8_vs_4 1.13 \n 7 Aamp 4603. 4870 4763. time_8_vs_0 1.03 \n 8 Aamp 4603. 4870 4763. time_8_vs_4 0.978\n 9 Abca12 5.29 4.25 4.14 time_8_vs_0 0.784\n10 Abca12 5.29 4.25 4.14 time_8_vs_4 0.975\n# ℹ 2,938 more rows",
"crumbs": [
"In-Class Materials",
"Session 2",
"Reshaping and Joining Data"
]
},
{
"objectID": "session-materials/session2/reshaping-and-joining.html#reshaping-data",
"href": "session-materials/session2/reshaping-and-joining.html#reshaping-data",
"title": "Reshaping and Joining Data",
"section": "",
"text": "In the rna tibble, the rows contain expression values (the unit) that are associated with a combination of 2 other variables: gene and sample.\nAll the other columns correspond to variables describing either the sample (organism, age, sex, …) or the gene (gene_biotype, ENTREZ_ID, product, …). The variables that don’t change with genes or with samples will have the same value in all the rows.\n\nrna %>%\n arrange(gene)\n\n# A tibble: 32,428 × 19\n gene sample expression organism age sex infection strain time tissue\n <chr> <chr> <dbl> <chr> <dbl> <chr> <chr> <chr> <dbl> <chr> \n 1 AI504432 GSM25… 1230 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 2 AI504432 GSM25… 1085 Mus mus… 8 Fema… NonInfec… C57BL… 0 Cereb…\n 3 AI504432 GSM25… 969 Mus mus… 8 Fema… NonInfec… C57BL… 0 Cereb…\n 4 AI504432 GSM25… 1284 Mus mus… 8 Fema… Influenz… C57BL… 4 Cereb…\n 5 AI504432 GSM25… 966 Mus mus… 8 Male Influenz… C57BL… 4 Cereb…\n 6 AI504432 GSM25… 918 Mus mus… 8 Male Influenz… C57BL… 8 Cereb…\n 7 AI504432 GSM25… 985 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 8 AI504432 GSM25… 972 Mus mus… 8 Male NonInfec… C57BL… 0 Cereb…\n 9 AI504432 GSM25… 1000 Mus mus… 8 Fema… Influenz… C57BL… 4 Cereb…\n10 AI504432 GSM25… 816 Mus mus… 8 Male Influenz… C57BL… 4 Cereb…\n# ℹ 32,418 more rows\n# ℹ 9 more variables: mouse <dbl>, ENTREZID <dbl>, product <chr>,\n# ensembl_gene_id <chr>, external_synonym <chr>, chromosome_name <chr>,\n# gene_biotype <chr>, phenotype_description <chr>,\n# hsapiens_homolog_associated_gene_name <chr>\n\n\nThis structure is called a long-format, as one column contains all the values, and other column(s) list(s) the context of the value.\nIn certain cases, the long-format is not really “human-readable”, and another format, a wide-format is preferred, as a more compact way of representing the data. This is typically the case with gene expression values that scientists are used to look as matrices, were rows represent genes and columns represent samples.\nIn this format, it would therefore become straightforward to explore the relationship between the gene expression levels within, and between, the samples.\n\n\n# A tibble: 1,474 × 23\n gene GSM2545336 GSM2545337 GSM2545338 GSM2545339 GSM2545340 GSM2545341\n <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>\n 1 Asl 1170 361 400 586 626 988\n 2 Apod 36194 10347 9173 10620 13021 29594\n 3 Cyp2d22 4060 1616 1603 1901 2171 3349\n 4 Klk6 287 629 641 578 448 195\n 5 Fcrls 85 233 244 237 180 38\n 6 Slc2a4 782 231 248 265 313 786\n 7 Exd2 1619 2288 2235 2513 2366 1359\n 8 Gjc2 288 595 568 551 310 146\n 9 Plp1 43217 101241 96534 58354 53126 27173\n10 Gnb4 1071 1791 1867 1430 1355 798\n# ℹ 1,464 more rows\n# ℹ 16 more variables: GSM2545342 <dbl>, GSM2545343 <dbl>, GSM2545344 <dbl>,\n# GSM2545345 <dbl>, GSM2545346 <dbl>, GSM2545347 <dbl>, GSM2545348 <dbl>,\n# GSM2545349 <dbl>, GSM2545350 <dbl>, GSM2545351 <dbl>, GSM2545352 <dbl>,\n# GSM2545353 <dbl>, GSM2545354 <dbl>, GSM2545362 <dbl>, GSM2545363 <dbl>,\n# GSM2545380 <dbl>\n\n\nTo convert the gene expression values from rna into a wide-format, we need to create a new table where the values of the sample column would become the names of column variables.\nThe key point here is that we are still following a tidy data structure, but we have reshaped the data according to the observations of interest: expression levels per gene instead of recording them per gene and per sample.\nThe opposite transformation would be to transform column names into values of a new variable.\nWe can do both these of transformations with two tidyr functions, pivot_longer() and pivot_wider() (see here for details).\n\n\nLet’s select the first 3 columns of rna and use pivot_wider() to transform the data into a wide-format.\n\nrna_exp <- rna %>%\n select(gene, sample, expression)\nrna_exp\n\n# A tibble: 32,428 × 3\n gene sample expression\n <chr> <chr> <dbl>\n 1 Asl GSM2545336 1170\n 2 Apod GSM2545336 36194\n 3 Cyp2d22 GSM2545336 4060\n 4 Klk6 GSM2545336 287\n 5 Fcrls GSM2545336 85\n 6 Slc2a4 GSM2545336 782\n 7 Exd2 GSM2545336 1619\n 8 Gjc2 GSM2545336 288\n 9 Plp1 GSM2545336 43217\n10 Gnb4 GSM2545336 1071\n# ℹ 32,418 more rows\n\n\npivot_wider takes three main arguments:\n\nthe data to be transformed;\nthe names_from : the column whose values will become new column names;\nthe values_from: the column whose values will fill the new columns.\n\n\n\n\n\n\nWide pivot of the rna data.\n\n\n\n\n\nrna_wide <- rna_exp %>%\n pivot_wider(names_from = sample,\n values_from = expression)\nrna_wide\n\n# A tibble: 1,474 × 23\n gene GSM2545336 GSM2545337 GSM2545338 GSM2545339 GSM2545340 GSM2545341\n <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>\n 1 Asl 1170 361 400 586 626 988\n 2 Apod 36194 10347 9173 10620 13021 29594\n 3 Cyp2d22 4060 1616 1603 1901 2171 3349\n 4 Klk6 287 629 641 578 448 195\n 5 Fcrls 85 233 244 237 180 38\n 6 Slc2a4 782 231 248 265 313 786\n 7 Exd2 1619 2288 2235 2513 2366 1359\n 8 Gjc2 288 595 568 551 310 146\n 9 Plp1 43217 101241 96534 58354 53126 27173\n10 Gnb4 1071 1791 1867 1430 1355 798\n# ℹ 1,464 more rows\n# ℹ 16 more variables: GSM2545342 <dbl>, GSM2545343 <dbl>, GSM2545344 <dbl>,\n# GSM2545345 <dbl>, GSM2545346 <dbl>, GSM2545347 <dbl>, GSM2545348 <dbl>,\n# GSM2545349 <dbl>, GSM2545350 <dbl>, GSM2545351 <dbl>, GSM2545352 <dbl>,\n# GSM2545353 <dbl>, GSM2545354 <dbl>, GSM2545362 <dbl>, GSM2545363 <dbl>,\n# GSM2545380 <dbl>\n\n\nNote that by default, the pivot_wider() function will add NA for missing values.\nLet’s imagine that for some reason, we had some missing expression values for some genes in certain samples. In the following fictive example, the gene Cyp2d22 has only one expression value, in GSM2545338 sample.\n\nrna_with_missing_values\n\n# A tibble: 7 × 3\n gene sample expression\n <chr> <chr> <dbl>\n1 Asl GSM2545336 1170\n2 Apod GSM2545336 36194\n3 Asl GSM2545337 361\n4 Apod GSM2545337 10347\n5 Asl GSM2545338 400\n6 Apod GSM2545338 9173\n7 Cyp2d22 GSM2545338 1603\n\n\nBy default, the pivot_wider() function will add NA for missing values. This can be parameterised with the values_fill argument of the pivot_wider() function.\n\nrna_with_missing_values %>%\n pivot_wider(names_from = sample,\n values_from = expression)\n\n# A tibble: 3 × 4\n gene GSM2545336 GSM2545337 GSM2545338\n <chr> <dbl> <dbl> <dbl>\n1 Asl 1170 361 400\n2 Apod 36194 10347 9173\n3 Cyp2d22 NA NA 1603\n\nrna_with_missing_values %>%\n pivot_wider(names_from = sample,\n values_from = expression,\n values_fill = 0)\n\n# A tibble: 3 × 4\n gene GSM2545336 GSM2545337 GSM2545338\n <chr> <dbl> <dbl> <dbl>\n1 Asl 1170 361 400\n2 Apod 36194 10347 9173\n3 Cyp2d22 0 0 1603\n\n\n\n\n\nIn the opposite situation we are using the column names and turning them into a pair of new variables. One variable represents the column names as values, and the other variable contains the values previously associated with the column names.\npivot_longer() takes four main arguments:\n\nthe data to be transformed;\nthe names_to: the new column name we wish to create and populate with the current column names;\nthe values_to: the new column name we wish to create and populate with current values;\nthe names of the columns to be used to populate the names_to and values_to variables (or to drop).\n\n\n\n\n\n\nLong pivot of the rna data.\n\n\n\n\nTo recreate rna_long from rna_wide we would create a key called sample and value called expression and use all columns except gene for the key variable. Here we drop gene column with a minus sign.\nNotice how the new variable names are to be quoted here.\n\nrna_long <- rna_wide %>%\n pivot_longer(names_to = \"sample\",\n values_to = \"expression\",\n -gene)\nrna_long\n\n# A tibble: 32,428 × 3\n gene sample expression\n <chr> <chr> <dbl>\n 1 Asl GSM2545336 1170\n 2 Asl GSM2545337 361\n 3 Asl GSM2545338 400\n 4 Asl GSM2545339 586\n 5 Asl GSM2545340 626\n 6 Asl GSM2545341 988\n 7 Asl GSM2545342 836\n 8 Asl GSM2545343 535\n 9 Asl GSM2545344 586\n10 Asl GSM2545345 597\n# ℹ 32,418 more rows\n\n\nWe could also have used a specification for what columns to include. This can be useful if you have a large number of identifying columns, and it’s easier to specify what to gather than what to leave alone. Here the starts_with() function can help to retrieve sample names without having to list them all! Another possibility would be to use the : operator!\n\nrna_wide %>%\n pivot_longer(names_to = \"sample\",\n values_to = \"expression\",\n cols = starts_with(\"GSM\"))\n\n# A tibble: 32,428 × 3\n gene sample expression\n <chr> <chr> <dbl>\n 1 Asl GSM2545336 1170\n 2 Asl GSM2545337 361\n 3 Asl GSM2545338 400\n 4 Asl GSM2545339 586\n 5 Asl GSM2545340 626\n 6 Asl GSM2545341 988\n 7 Asl GSM2545342 836\n 8 Asl GSM2545343 535\n 9 Asl GSM2545344 586\n10 Asl GSM2545345 597\n# ℹ 32,418 more rows\n\nrna_wide %>%\n pivot_longer(names_to = \"sample\",\n values_to = \"expression\",\n GSM2545336:GSM2545380)\n\n# A tibble: 32,428 × 3\n gene sample expression\n <chr> <chr> <dbl>\n 1 Asl GSM2545336 1170\n 2 Asl GSM2545337 361\n 3 Asl GSM2545338 400\n 4 Asl GSM2545339 586\n 5 Asl GSM2545340 626\n 6 Asl GSM2545341 988\n 7 Asl GSM2545342 836\n 8 Asl GSM2545343 535\n 9 Asl GSM2545344 586\n10 Asl GSM2545345 597\n# ℹ 32,418 more rows\n\n\nNote that if we had missing values in the wide-format, the NA would be included in the new long format.\nRemember our previous fictive tibble containing missing values:\n\nrna_with_missing_values\n\n# A tibble: 7 × 3\n gene sample expression\n <chr> <chr> <dbl>\n1 Asl GSM2545336 1170\n2 Apod GSM2545336 36194\n3 Asl GSM2545337 361\n4 Apod GSM2545337 10347\n5 Asl GSM2545338 400\n6 Apod GSM2545338 9173\n7 Cyp2d22 GSM2545338 1603\n\nwide_with_NA <- rna_with_missing_values %>%\n pivot_wider(names_from = sample,\n values_from = expression)\nwide_with_NA\n\n# A tibble: 3 × 4\n gene GSM2545336 GSM2545337 GSM2545338\n <chr> <dbl> <dbl> <dbl>\n1 Asl 1170 361 400\n2 Apod 36194 10347 9173\n3 Cyp2d22 NA NA 1603\n\nwide_with_NA %>%\n pivot_longer(names_to = \"sample\",\n values_to = \"expression\",\n -gene)\n\n# A tibble: 9 × 3\n gene sample expression\n <chr> <chr> <dbl>\n1 Asl GSM2545336 1170\n2 Asl GSM2545337 361\n3 Asl GSM2545338 400\n4 Apod GSM2545336 36194\n5 Apod GSM2545337 10347\n6 Apod GSM2545338 9173\n7 Cyp2d22 GSM2545336 NA\n8 Cyp2d22 GSM2545337 NA\n9 Cyp2d22 GSM2545338 1603\n\n\nPivoting to wider and longer formats can be a useful way to balance out a dataset so every replicate has the same composition.\n\n\n\n\n\n\nQuestion\n\n\n\nStarting from the rna table, use the pivot_wider() function to create a wide-format table giving the gene expression levels in each mouse. Then use the pivot_longer() function to restore a long-format table.\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\nrna1 <- rna %>%\nselect(gene, mouse, expression) %>%\npivot_wider(names_from = mouse, values_from = expression)\nrna1\n\n# A tibble: 1,474 × 23\n gene `14` `9` `10` `15` `18` `6` `5` `11` `22` `13` `23`\n <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>\n 1 Asl 1170 361 400 586 626 988 836 535 586 597 938\n 2 Apod 36194 10347 9173 10620 13021 29594 24959 13668 13230 15868 27769\n 3 Cyp2d22 4060 1616 1603 1901 2171 3349 3122 2008 2254 2277 2985\n 4 Klk6 287 629 641 578 448 195 186 1101 537 567 327\n 5 Fcrls 85 233 244 237 180 38 68 375 199 177 89\n 6 Slc2a4 782 231 248 265 313 786 528 249 266 357 654\n 7 Exd2 1619 2288 2235 2513 2366 1359 1474 3126 2379 2173 1531\n 8 Gjc2 288 595 568 551 310 146 186 791 454 370 240\n 9 Plp1 43217 101241 96534 58354 53126 27173 28728 98658 61356 61647 38019\n10 Gnb4 1071 1791 1867 1430 1355 798 806 2437 1394 1554 960\n# ℹ 1,464 more rows\n# ℹ 11 more variables: `24` <dbl>, `8` <dbl>, `7` <dbl>, `1` <dbl>, `16` <dbl>,\n# `21` <dbl>, `4` <dbl>, `2` <dbl>, `20` <dbl>, `12` <dbl>, `19` <dbl>\n\nrna1 %>%\npivot_longer(names_to = \"mouse_id\", values_to = \"counts\", -gene)\n\n# A tibble: 32,428 × 3\n gene mouse_id counts\n <chr> <chr> <dbl>\n 1 Asl 14 1170\n 2 Asl 9 361\n 3 Asl 10 400\n 4 Asl 15 586\n 5 Asl 18 626\n 6 Asl 6 988\n 7 Asl 5 836\n 8 Asl 11 535\n 9 Asl 22 586\n10 Asl 13 597\n# ℹ 32,418 more rows\n\n\n\n\n\n\n\n\n\n\n\nQuestion\n\n\n\nSubset genes located on X and Y chromosomes from the rna data frame and spread the data frame with sex as columns, chromosome_name as rows, and the mean expression of genes located in each chromosome as the values, as in the following tibble:\n\n\n\n\n\n\n\n\n\nYou will need to summarise before reshaping!\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\nLet’s first calculate the mean expression level of X and Y linked genes from male and female samples…\n\n rna %>%\n filter(chromosome_name == \"Y\" | chromosome_name == \"X\") %>%\n group_by(sex, chromosome_name) %>%\n summarise(mean = mean(expression))\n\n`summarise()` has grouped output by 'sex'. You can override using the `.groups`\nargument.\n\n\n# A tibble: 4 × 3\n# Groups: sex [2]\n sex chromosome_name mean\n <chr> <chr> <dbl>\n1 Female X 3504.\n2 Female Y 3 \n3 Male X 2497.\n4 Male Y 2117.\n\n\nAnd pivot the table to wide format\n\nrna_1 <- rna %>%\n filter(chromosome_name == \"Y\" | chromosome_name == \"X\") %>%\n group_by(sex, chromosome_name) %>%\n summarise(mean = mean(expression)) %>%\n pivot_wider(names_from = sex,\n values_from = mean)\n\n`summarise()` has grouped output by 'sex'. You can override using the `.groups`\nargument.\n\nrna_1\n\n# A tibble: 2 × 3\n chromosome_name Female Male\n <chr> <dbl> <dbl>\n1 X 3504. 2497.\n2 Y 3 2117.\n\n\nNow take that data frame and transform it with pivot_longer() so each row is a unique chromosome_name by gender combination.\n\nrna_1 %>%\n pivot_longer(names_to = \"gender\",\n values_to = \"mean\",\n -chromosome_name)\n\n# A tibble: 4 × 3\n chromosome_name gender mean\n <chr> <chr> <dbl>\n1 X Female 3504.\n2 X Male 2497.\n3 Y Female 3 \n4 Y Male 2117.\n\n\n\n\n\n\n\n\n\n\n\nQuestion\n\n\n\nUse the rna dataset to create an expression matrix where each row represents the mean expression levels of genes and columns represent the different timepoints.\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\nLet’s first calculate the mean expression by gene and by time\n\nrna %>%\n group_by(gene, time) %>%\n summarise(mean_exp = mean(expression))\n\n`summarise()` has grouped output by 'gene'. You can override using the\n`.groups` argument.\n\n\n# A tibble: 4,422 × 3\n# Groups: gene [1,474]\n gene time mean_exp\n <chr> <dbl> <dbl>\n 1 AI504432 0 1034.\n 2 AI504432 4 1104.\n 3 AI504432 8 1014 \n 4 AW046200 0 155.\n 5 AW046200 4 152.\n 6 AW046200 8 81 \n 7 AW551984 0 238 \n 8 AW551984 4 302.\n 9 AW551984 8 342.\n10 Aamp 0 4603.\n# ℹ 4,412 more rows\n\n\nbefore using the pivot_wider() function\n\nrna_time <- rna %>%\n group_by(gene, time) %>%\n summarise(mean_exp = mean(expression)) %>%\n pivot_wider(names_from = time,\n values_from = mean_exp)\n\n`summarise()` has grouped output by 'gene'. You can override using the\n`.groups` argument.\n\nrna_time\n\n# A tibble: 1,474 × 4\n# Groups: gene [1,474]\n gene `0` `4` `8`\n <chr> <dbl> <dbl> <dbl>\n 1 AI504432 1034. 1104. 1014 \n 2 AW046200 155. 152. 81 \n 3 AW551984 238 302. 342. \n 4 Aamp 4603. 4870 4763. \n 5 Abca12 5.29 4.25 4.14\n 6 Abcc8 2576. 2609. 2292. \n 7 Abhd14a 591. 547. 432. \n 8 Abi2 4881. 4903. 4945. \n 9 Abi3bp 1175. 1061. 762. \n10 Abl2 2170. 2078. 2131. \n# ℹ 1,464 more rows\n\n\nNotice that this generates a tibble with some column names starting by a number. If we wanted to select the column corresponding to the timepoints, we could not use the column names directly… What happens when we select the column 4?\n\nrna %>%\n group_by(gene, time) %>%\n summarise(mean_exp = mean(expression)) %>%\n pivot_wider(names_from = time,\n values_from = mean_exp) %>%\n select(gene, 4)\n\n`summarise()` has grouped output by 'gene'. You can override using the\n`.groups` argument.\n\n\n# A tibble: 1,474 × 2\n# Groups: gene [1,474]\n gene `8`\n <chr> <dbl>\n 1 AI504432 1014 \n 2 AW046200 81 \n 3 AW551984 342. \n 4 Aamp 4763. \n 5 Abca12 4.14\n 6 Abcc8 2292. \n 7 Abhd14a 432. \n 8 Abi2 4945. \n 9 Abi3bp 762. \n10 Abl2 2131. \n# ℹ 1,464 more rows\n\n\nTo select the timepoint 4, we would have to quote the column name, with backticks “`”\n\nrna %>%\n group_by(gene, time) %>%\n summarise(mean_exp = mean(expression)) %>%\n pivot_wider(names_from = time,\n values_from = mean_exp) %>%\n select(gene, `4`)\n\n`summarise()` has grouped output by 'gene'. You can override using the\n`.groups` argument.\n\n\n# A tibble: 1,474 × 2\n# Groups: gene [1,474]\n gene `4`\n <chr> <dbl>\n 1 AI504432 1104. \n 2 AW046200 152. \n 3 AW551984 302. \n 4 Aamp 4870 \n 5 Abca12 4.25\n 6 Abcc8 2609. \n 7 Abhd14a 547. \n 8 Abi2 4903. \n 9 Abi3bp 1061. \n10 Abl2 2078. \n# ℹ 1,464 more rows\n\n\nAnother possibility would be to rename the column, choosing a name that doesn’t start by a number :\n\nrna %>%\n group_by(gene, time) %>%\n summarise(mean_exp = mean(expression)) %>%\n pivot_wider(names_from = time,\n values_from = mean_exp) %>%\n rename(\"time0\" = `0`, \"time4\" = `4`, \"time8\" = `8`) %>%\n select(gene, time4)\n\n`summarise()` has grouped output by 'gene'. You can override using the\n`.groups` argument.\n\n\n# A tibble: 1,474 × 2\n# Groups: gene [1,474]\n gene time4\n <chr> <dbl>\n 1 AI504432 1104. \n 2 AW046200 152. \n 3 AW551984 302. \n 4 Aamp 4870 \n 5 Abca12 4.25\n 6 Abcc8 2609. \n 7 Abhd14a 547. \n 8 Abi2 4903. \n 9 Abi3bp 1061. \n10 Abl2 2078. \n# ℹ 1,464 more rows\n\n\n\n\n\n\n\n\n\n\n\nQuestion\n\n\n\nUse the previous data frame containing mean expression levels per timepoint and create a new column containing fold-changes between timepoint 8 and timepoint 0, and fold-changes between timepoint 8 and timepoint 4. Convert this table into a long-format table gathering the fold-changes calculated.\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\nStarting from the rna_time tibble:\n\nrna_time\n\n# A tibble: 1,474 × 4\n# Groups: gene [1,474]\n gene `0` `4` `8`\n <chr> <dbl> <dbl> <dbl>\n 1 AI504432 1034. 1104. 1014 \n 2 AW046200 155. 152. 81 \n 3 AW551984 238 302. 342. \n 4 Aamp 4603. 4870 4763. \n 5 Abca12 5.29 4.25 4.14\n 6 Abcc8 2576. 2609. 2292. \n 7 Abhd14a 591. 547. 432. \n 8 Abi2 4881. 4903. 4945. \n 9 Abi3bp 1175. 1061. 762. \n10 Abl2 2170. 2078. 2131. \n# ℹ 1,464 more rows\n\n\nCalculate fold-changes:\n\nrna_time %>%\n mutate(time_8_vs_0 = `8` / `0`, time_8_vs_4 = `8` / `4`)\n\n# A tibble: 1,474 × 6\n# Groups: gene [1,474]\n gene `0` `4` `8` time_8_vs_0 time_8_vs_4\n <chr> <dbl> <dbl> <dbl> <dbl> <dbl>\n 1 AI504432 1034. 1104. 1014 0.981 0.918\n 2 AW046200 155. 152. 81 0.522 0.532\n 3 AW551984 238 302. 342. 1.44 1.13 \n 4 Aamp 4603. 4870 4763. 1.03 0.978\n 5 Abca12 5.29 4.25 4.14 0.784 0.975\n 6 Abcc8 2576. 2609. 2292. 0.889 0.878\n 7 Abhd14a 591. 547. 432. 0.731 0.791\n 8 Abi2 4881. 4903. 4945. 1.01 1.01 \n 9 Abi3bp 1175. 1061. 762. 0.649 0.719\n10 Abl2 2170. 2078. 2131. 0.982 1.03 \n# ℹ 1,464 more rows\n\n\nAnd use the pivot_longer() function:\n\nrna_time %>%\n mutate(time_8_vs_0 = `8` / `0`, time_8_vs_4 = `8` / `4`) %>%\n pivot_longer(names_to = \"comparisons\",\n values_to = \"Fold_changes\",\n time_8_vs_0:time_8_vs_4)\n\n# A tibble: 2,948 × 6\n# Groups: gene [1,474]\n gene `0` `4` `8` comparisons Fold_changes\n <chr> <dbl> <dbl> <dbl> <chr> <dbl>\n 1 AI504432 1034. 1104. 1014 time_8_vs_0 0.981\n 2 AI504432 1034. 1104. 1014 time_8_vs_4 0.918\n 3 AW046200 155. 152. 81 time_8_vs_0 0.522\n 4 AW046200 155. 152. 81 time_8_vs_4 0.532\n 5 AW551984 238 302. 342. time_8_vs_0 1.44 \n 6 AW551984 238 302. 342. time_8_vs_4 1.13 \n 7 Aamp 4603. 4870 4763. time_8_vs_0 1.03 \n 8 Aamp 4603. 4870 4763. time_8_vs_4 0.978\n 9 Abca12 5.29 4.25 4.14 time_8_vs_0 0.784\n10 Abca12 5.29 4.25 4.14 time_8_vs_4 0.975\n# ℹ 2,938 more rows",
"crumbs": [
"In-Class Materials",
"Session 2",
"Reshaping and Joining Data"
]
},
{
"objectID": "session-materials/session2/reshaping-and-joining.html#joining-tables",
"href": "session-materials/session2/reshaping-and-joining.html#joining-tables",
"title": "Reshaping and Joining Data",
"section": "Joining tables",
"text": "Joining tables\nIn many real life situations, data are spread across multiple tables. Usually this occurs because different types of information are collected from different sources.\nIt may be desirable for some analyses to combine data from two or more tables into a single data frame based on a column that would be common to all the tables.\nThe dplyr package provides a set of join functions for combining two data frames based on matches within specified columns. Here, we provide a short introduction to joins. The Data Transformation Cheat Sheet also provides a short overview on table joins.\nWe are going to illustrate join using a small table, rna_mini that we will create by subsetting the original rna table, keeping only 3 columns and 10 lines.\n\nrna_mini <- rna %>%\n select(gene, sample, expression) %>%\n head(10)\nrna_mini\n\n# A tibble: 10 × 3\n gene sample expression\n <chr> <chr> <dbl>\n 1 Asl GSM2545336 1170\n 2 Apod GSM2545336 36194\n 3 Cyp2d22 GSM2545336 4060\n 4 Klk6 GSM2545336 287\n 5 Fcrls GSM2545336 85\n 6 Slc2a4 GSM2545336 782\n 7 Exd2 GSM2545336 1619\n 8 Gjc2 GSM2545336 288\n 9 Plp1 GSM2545336 43217\n10 Gnb4 GSM2545336 1071\n\n\nThe second table, annot1, contains 2 columns, gene and gene_description.\n\nannot1 <- read_csv(file = \"data/annot1.csv\")\nannot1\n\n# A tibble: 10 × 2\n gene gene_description \n <chr> <chr> \n 1 Cyp2d22 cytochrome P450, family 2, subfamily d, polypeptide 22 [Source:MGI S…\n 2 Klk6 kallikrein related-peptidase 6 [Source:MGI Symbol;Acc:MGI:1343166] \n 3 Fcrls Fc receptor-like S, scavenger receptor [Source:MGI Symbol;Acc:MGI:19…\n 4 Plp1 proteolipid protein (myelin) 1 [Source:MGI Symbol;Acc:MGI:97623] \n 5 Exd2 exonuclease 3'-5' domain containing 2 [Source:MGI Symbol;Acc:MGI:192…\n 6 Apod apolipoprotein D [Source:MGI Symbol;Acc:MGI:88056] \n 7 Gnb4 guanine nucleotide binding protein (G protein), beta 4 [Source:MGI S…\n 8 Slc2a4 solute carrier family 2 (facilitated glucose transporter), member 4 …\n 9 Asl argininosuccinate lyase [Source:MGI Symbol;Acc:MGI:88084] \n10 Gjc2 gap junction protein, gamma 2 [Source:MGI Symbol;Acc:MGI:2153060] \n\n\nWe now want to join these two tables into a single one containing all variables using the full_join() function from the dplyr package. The function will automatically find the common variable to match columns from the first and second table. In this case, gene is the common variable. Such variables are called keys. Keys are used to match observations across different tables.\n\nfull_join(rna_mini, annot1)\n\nJoining with `by = join_by(gene)`\n\n\n# A tibble: 10 × 4\n gene sample expression gene_description \n <chr> <chr> <dbl> <chr> \n 1 Asl GSM2545336 1170 argininosuccinate lyase [Source:MGI Symbol;Acc…\n 2 Apod GSM2545336 36194 apolipoprotein D [Source:MGI Symbol;Acc:MGI:88…\n 3 Cyp2d22 GSM2545336 4060 cytochrome P450, family 2, subfamily d, polype…\n 4 Klk6 GSM2545336 287 kallikrein related-peptidase 6 [Source:MGI Sym…\n 5 Fcrls GSM2545336 85 Fc receptor-like S, scavenger receptor [Source…\n 6 Slc2a4 GSM2545336 782 solute carrier family 2 (facilitated glucose t…\n 7 Exd2 GSM2545336 1619 exonuclease 3'-5' domain containing 2 [Source:…\n 8 Gjc2 GSM2545336 288 gap junction protein, gamma 2 [Source:MGI Symb…\n 9 Plp1 GSM2545336 43217 proteolipid protein (myelin) 1 [Source:MGI Sym…\n10 Gnb4 GSM2545336 1071 guanine nucleotide binding protein (G protein)…\n\n\nIn real life, gene annotations are sometimes labelled differently.\nThe annot2 table is exactly the same than annot1 except that the variable containing gene names is labelled differently.\n\nannot2 <- read_csv(file = \"data/annot2.csv\")\n\nIn case none of the variable names match, we can set manually the variables to use for the matching. These variables can be set using the by argument, as shown below with rna_mini and annot2 tables.\n\nfull_join(rna_mini, annot2, by = c(\"gene\" = \"external_gene_name\"))\n\n# A tibble: 10 × 4\n gene sample expression description \n <chr> <chr> <dbl> <chr> \n 1 Asl GSM2545336 1170 argininosuccinate lyase [Source:MGI Symbol;Acc…\n 2 Apod GSM2545336 36194 apolipoprotein D [Source:MGI Symbol;Acc:MGI:88…\n 3 Cyp2d22 GSM2545336 4060 cytochrome P450, family 2, subfamily d, polype…\n 4 Klk6 GSM2545336 287 kallikrein related-peptidase 6 [Source:MGI Sym…\n 5 Fcrls GSM2545336 85 Fc receptor-like S, scavenger receptor [Source…\n 6 Slc2a4 GSM2545336 782 solute carrier family 2 (facilitated glucose t…\n 7 Exd2 GSM2545336 1619 exonuclease 3'-5' domain containing 2 [Source:…\n 8 Gjc2 GSM2545336 288 gap junction protein, gamma 2 [Source:MGI Symb…\n 9 Plp1 GSM2545336 43217 proteolipid protein (myelin) 1 [Source:MGI Sym…\n10 Gnb4 GSM2545336 1071 guanine nucleotide binding protein (G protein)…\n\n\nAs can be seen above, the variable name of the first table is retained in the joined one.\n\n\n\n\n\n\nChallenge:\n\n\n\nLoad in the file annot3.csv. Using the full_join() function, join tables rna_mini and annot3. What has happened for genes Klk6, mt-Tf, mt-Rnr1, mt-Tv, mt-Rnr2, and mt-Tl1 ?\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\nannot3 <- read_csv(\"data/annot3.csv\")\nfull_join(rna_mini, annot3)\n\n# A tibble: 15 × 4\n gene sample expression gene_description \n <chr> <chr> <dbl> <chr> \n 1 Asl GSM2545336 1170 argininosuccinate lyase [Source:MGI Symbol;Acc…\n 2 Apod GSM2545336 36194 apolipoprotein D [Source:MGI Symbol;Acc:MGI:88…\n 3 Cyp2d22 GSM2545336 4060 cytochrome P450, family 2, subfamily d, polype…\n 4 Klk6 GSM2545336 287 <NA> \n 5 Fcrls GSM2545336 85 Fc receptor-like S, scavenger receptor [Source…\n 6 Slc2a4 GSM2545336 782 solute carrier family 2 (facilitated glucose t…\n 7 Exd2 GSM2545336 1619 exonuclease 3'-5' domain containing 2 [Source:…\n 8 Gjc2 GSM2545336 288 gap junction protein, gamma 2 [Source:MGI Symb…\n 9 Plp1 GSM2545336 43217 proteolipid protein (myelin) 1 [Source:MGI Sym…\n10 Gnb4 GSM2545336 1071 guanine nucleotide binding protein (G protein)…\n11 mt-Tf <NA> NA mitochondrially encoded tRNA phenylalanine [So…\n12 mt-Rnr1 <NA> NA mitochondrially encoded 12S rRNA [Source:MGI S…\n13 mt-Tv <NA> NA mitochondrially encoded tRNA valine [Source:MG…\n14 mt-Rnr2 <NA> NA mitochondrially encoded 16S rRNA [Source:MGI S…\n15 mt-Tl1 <NA> NA mitochondrially encoded tRNA leucine 1 [Source…\n\n\nGenes Klk6 is only present in rna_mini, while genes mt-Tf, mt-Rnr1, mt-Tv, mt-Rnr2, and mt-Tl1 are only present in annot3 table. Their respective values for the variables of the table have been encoded as missing.",
"crumbs": [
"In-Class Materials",
"Session 2",
"Reshaping and Joining Data"
]
},
{
"objectID": "session-materials/session2/reshaping-and-joining.html#exporting-data",
"href": "session-materials/session2/reshaping-and-joining.html#exporting-data",
"title": "Reshaping and Joining Data",
"section": "Exporting data",
"text": "Exporting data\nNow that you have learned how to use dplyr to extract information from or summarise your raw data, you may want to export these new data sets to share them with your collaborators or for archival.\nSimilar to the read_csv() function used for reading CSV files into R, there is a write_csv() function that generates CSV files from data frames.\nBefore using write_csv(), we are going to create a new folder, data_output, in our working directory that will store this generated dataset. We don’t want to write generated datasets in the same directory as our raw data. It’s good practice to keep them separate. The data folder should only contain the raw, unaltered data, and should be left alone to make sure we don’t delete or modify it. In contrast, our script will generate the contents of the data_output directory, so even if the files it contains are deleted, we can always re-generate them.\nLet’s use write_csv() to save the rna_wide table that we have created previously.\n\nwrite_csv(rna_wide, file = \"data_output/rna_wide.csv\")\n\n\nThe materials in this lesson have been adapted from work created by the HBC and Data Carpentry, as well as materials created by Laurent Gatto, Charlotte Soneson, Jenny Drnevich, Robert Castelo, and Kevin Rue-Albert. These are open access materials distributed under the terms of the Creative Commons Attribution license (CC BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.",
"crumbs": [
"In-Class Materials",
"Session 2",
"Reshaping and Joining Data"
]
},
{
"objectID": "session-materials/session2/loops.html",
"href": "session-materials/session2/loops.html",
"title": "For loops",
"section": "",
"text": "Loops are a fundamental structure for repetition in programming. for loops perform the same action for each item in a list of things. The basic syntax is:\n\nfor (item in list_of_items) {\n do_something(item)\n}\n\nWe can create a vector on the fly to loop a particular number of times:\n\nfor (i in 1:5){\n print(i)\n}\n\n[1] 1\n[1] 2\n[1] 3\n[1] 4\n[1] 5\n\n\nOr use a pre-existing vector or list.\n\nvolumes = c(1.6, 3, 8)\nfor (volume in volumes){\n mass <- 2.65 * volume ^ 0.9\n print(mass)\n}\n\n[1] 4.045329\n[1] 7.12287\n[1] 17.21975\n\n\nWe also might want to loop over indices so we can access multiple vectors.\n\nas <- c(2.65, 1.28, 3.29)\nbs <- c(0.9, 1.1, 1.2)\nvolumes = c(1.6, 3, 8)\nmasses <- vector(mode=\"numeric\", length=length(volumes))\nfor (i in 1:length(volumes)){\n mass <- as[i] * volumes[i] ^ bs[i]\n masses[i] <- mass\n}\nmasses\n\n[1] 4.045329 4.285913 39.893660\n\n\nWe can use functions inside loops. For example, let’s take a function that returns an estimated mass if the volume > 5 and NA if it’s not.\n\nest_mass <- function(volume, a, b){\n if (volume > 5) {\n mass <- a * volume ^ b\n } else {\n mass <- NA\n }\n return(mass)\n}\n\nWe can then call the function to populate a vector item by item.\n\nmasses <- vector(mode=\"numeric\", length=length(volumes))\nfor (i in 1:length(volumes)){\n mass <- est_mass(volumes[i], as[i], bs[i])\n masses[i] <- mass\n}\nmasses\n\n[1] NA NA 39.89366\n\n\nTo note, this is the for loop equivalent of an mapply statement.\n\nmasses_apply <- mapply(est_mass, volumes, as, bs)\nmasses_apply\n\n[1] NA NA 39.89366",
"crumbs": [
"In-Class Materials",
"Session 2",
"For loops"
]
},
{
"objectID": "session-materials/session2/loops.html#for-loops",
"href": "session-materials/session2/loops.html#for-loops",
"title": "For loops",
"section": "",
"text": "Loops are a fundamental structure for repetition in programming. for loops perform the same action for each item in a list of things. The basic syntax is:\n\nfor (item in list_of_items) {\n do_something(item)\n}\n\nWe can create a vector on the fly to loop a particular number of times:\n\nfor (i in 1:5){\n print(i)\n}\n\n[1] 1\n[1] 2\n[1] 3\n[1] 4\n[1] 5\n\n\nOr use a pre-existing vector or list.\n\nvolumes = c(1.6, 3, 8)\nfor (volume in volumes){\n mass <- 2.65 * volume ^ 0.9\n print(mass)\n}\n\n[1] 4.045329\n[1] 7.12287\n[1] 17.21975\n\n\nWe also might want to loop over indices so we can access multiple vectors.\n\nas <- c(2.65, 1.28, 3.29)\nbs <- c(0.9, 1.1, 1.2)\nvolumes = c(1.6, 3, 8)\nmasses <- vector(mode=\"numeric\", length=length(volumes))\nfor (i in 1:length(volumes)){\n mass <- as[i] * volumes[i] ^ bs[i]\n masses[i] <- mass\n}\nmasses\n\n[1] 4.045329 4.285913 39.893660\n\n\nWe can use functions inside loops. For example, let’s take a function that returns an estimated mass if the volume > 5 and NA if it’s not.\n\nest_mass <- function(volume, a, b){\n if (volume > 5) {\n mass <- a * volume ^ b\n } else {\n mass <- NA\n }\n return(mass)\n}\n\nWe can then call the function to populate a vector item by item.\n\nmasses <- vector(mode=\"numeric\", length=length(volumes))\nfor (i in 1:length(volumes)){\n mass <- est_mass(volumes[i], as[i], bs[i])\n masses[i] <- mass\n}\nmasses\n\n[1] NA NA 39.89366\n\n\nTo note, this is the for loop equivalent of an mapply statement.\n\nmasses_apply <- mapply(est_mass, volumes, as, bs)\nmasses_apply\n\n[1] NA NA 39.89366",
"crumbs": [
"In-Class Materials",
"Session 2",
"For loops"
]
},
{
"objectID": "session-materials/session2/loops.html#in-r-we-often-want-to-use-apply-statements-as-opposed-to-explicitly-writing-loops.",
"href": "session-materials/session2/loops.html#in-r-we-often-want-to-use-apply-statements-as-opposed-to-explicitly-writing-loops.",
"title": "For loops",
"section": "In R we often want to use apply statements as opposed to explicitly writing loops.",
"text": "In R we often want to use apply statements as opposed to explicitly writing loops.\nThe materials in this lesson have been adapted from work created by the (HBC)](http://bioinformatics.sph.harvard.edu/) and Data Carpentry (http://datacarpentry.org/), as well as materials created by Laurent Gatto, Charlotte Soneson, Jenny Drnevich, Robert Castelo, and Kevin Rue-Albert. These are open access materials distributed under the terms of the Creative Commons Attribution license (CC BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.",
"crumbs": [
"In-Class Materials",
"Session 2",
"For loops"
]
},
{
"objectID": "session-materials/session3/Intro-data-viz.html",
"href": "session-materials/session3/Intro-data-viz.html",
"title": "Session 3: Data Visualization in R",
"section": "",
"text": "In this session, we will explore the ways to create, manipulate, and export variety of plots such as histograms, boxplots, line plots, and scatter plots.\nR is one of the most versatile platform for data visualization and it can create almost any type of graph. Although there are several ways to create graphs in R, we will particularly focus on “ggplot2” package which is arguably one of the most used package in R.\nFor this lesson, you will need the `new_metadata` data frame. Load it into your environment as follows:\n\n## load the new_metadata data frame into your environment from a .RData object\nload(\"data/new_metadata.RData\")\n\nNext, let’s check if it was successfully loaded into the environment:\n\n# this data frame should have 12 rows and 5 columns\nView(new_metadata)\n\nWhen we are working with large sets of numbers it can be useful to display that information graphically to gain more insight. In this lesson we will be plotting with the popular Bioconductor package [`ggplot2`](http://docs.ggplot2.org/).\nThe `ggplot2` syntax takes some getting used to, but once you get it, you will find it’s extremely powerful and flexible. We will start with drawing a simple x-y scatterplot of `samplemeans` versus `age_in_days` from the `new_metadata` data frame. Please note that `ggplot2` expects a dataframe or a tibble (the Tidyverse version of a dataframe) as input.\nLet’s start by loading the `ggplot2` library:\n\nlibrary(ggplot2)\n\nThe `ggplot()` function is used to **initialize the basic graph structure**, then we add to it. The basic idea is that you specify different parts of the plot using additional functions one after the other and combine them into a “code chunk” using the `+` operator; the functions in the resulting code chunk are called layers.\nLet’s start:\n\nload(\"data/new_metadata.RData\")\nggplot(new_metadata) # what happens? \n\n\n\n\n\n\n\n\nYou get an blank plot, because you need to specify additional layers using the + operator.\nThe geom (geometric) object is the layer that specifies what kind of plot we want to draw. A plot must have at least one geom ; there is no upper limit. Examples include:\n- points (geom_point, geom_jitter for scatter plots, dot plots, etc)\n- lines (geom_line, for time series, trend lines, etc)\n- boxplot (geom_boxplot, for, well, boxplots!)\nLet’s add a “geom” layer to our plot using the + operator, and since we want a scatter plot so we will use geom_point().\n\n#ggplot(new_metadata) +\n # geom_point() # note what happens here\n\nWhy do we get an error? Is the error message easy to decipher?\nWe get an error because each type of geom usually has a required set of aesthetics to be set. “Aesthetics” are set with the aes() function and can be set either nested within geom_point() (applies only to that layer) or within ggplot() (applies to the whole plot).\nThe aes() function has many different arguments, and all of those arguments take columns from the original data frame as input. It can be used to specify many plot elements including the following:\n- position (i.e., on the x and y axes)\n- color (“outside” color)\n- fill (“inside” color)\n- shape (of points)\n- linetype\n- size\nTo start, we will specify x- and y-axis since geom_point requires the most basic information about a scatterplot, i.e. what you want to plot on the x and y axes. All of the other plot elements mentioned above are optional.\n\nggplot(new_metadata) +\n geom_point(aes(x = age_in_days, y= samplemeans))\n\n\n\n\n\n\n\n\nNow that we have the required aesthetics, let’s add some extras like color to the plot. We can color the points on the plot based on the genotype column** within aes(). You will notice that there are a default set of colors that will be used so we do not have to specify. Note that the legend has been conveniently plotted for us.\n\nggplot(new_metadata) +\n geom_point(aes(x = age_in_days, y= samplemeans, color = genotype)) \n\n\n\n\n\n\n\n\nLet’s try to have both celltype and genotype represented on the plot. To do this we can assign the shape argument in aes() the celltype column, so that each celltype is plotted with a different shaped data point.\n\nggplot(new_metadata) +\n geom_point(aes(x = age_in_days, y= samplemeans, color = genotype,\n shape=celltype)) \n\n\n\n\n\n\n\n\nThe data points are quite small. We can adjust the size of the data points within the geom_point() layer, but it should not be within aes() since we are not mapping it to a column in the input data frame, instead we are just specifying a number.\n\nggplot(new_metadata) +\n geom_point(aes(x = age_in_days, y= samplemeans, color = genotype,\n shape=celltype), size=2.25) \n\n\n\n\n\n\n\n\nThe labels on the x- and y-axis are also quite small and hard to read. To change their size, we need to add an additional theme layer. The ggplot2 theme system handles non-data plot elements such as:\n- Axis label aesthetics\n- Plot background\n- Facet label background\n- Legend appearance\nThere are built-in themes we can use (i.e. theme_bw()) that mostly change the background/foreground colors, by adding it as additional layer. Or we can adjust specific elements of the current default theme by adding the theme() layer and passing in arguments for the things we wish to change. Or we can use both.\nLet’s add a layer theme_bw().\n\nggplot(new_metadata) +\n geom_point(aes(x = age_in_days, y= samplemeans, color = genotype,\n shape=celltype), size=3.0) +\n theme_bw() \n\n\n\n\n\n\n\n\nDo the axis labels or the tick labels get any larger by changing themes?\nNo, they don’t. But, we can add arguments using theme() to change the size of axis labels ourselves. Since we will be adding this layer “on top”, or after theme_bw(), any features we change will override what is set by the theme_bw() layer.\nLet’s increase the size of both the axes titles to be 1.5 times the default size. When modifying the size of text the rel() function is commonly used to specify a change relative to the default.\n\nggplot(new_metadata) +\n geom_point(aes(x = age_in_days, y= samplemeans, color = genotype,\n shape=celltype), size=2.25) +\n theme_bw() +\n theme(axis.title = element_text(size=rel(1.5)))",
"crumbs": [
"In-Class Materials",
"Session 3",
"Session 3: Data Visualization in R"
]
},
{
"objectID": "session-materials/session3/Intro-data-viz.html#introduction",
"href": "session-materials/session3/Intro-data-viz.html#introduction",
"title": "Session 3: Data Visualization in R",
"section": "",
"text": "In this session, we will explore the ways to create, manipulate, and export variety of plots such as histograms, boxplots, line plots, and scatter plots.\nR is one of the most versatile platform for data visualization and it can create almost any type of graph. Although there are several ways to create graphs in R, we will particularly focus on “ggplot2” package which is arguably one of the most used package in R.\nFor this lesson, you will need the `new_metadata` data frame. Load it into your environment as follows:\n\n## load the new_metadata data frame into your environment from a .RData object\nload(\"data/new_metadata.RData\")\n\nNext, let’s check if it was successfully loaded into the environment:\n\n# this data frame should have 12 rows and 5 columns\nView(new_metadata)\n\nWhen we are working with large sets of numbers it can be useful to display that information graphically to gain more insight. In this lesson we will be plotting with the popular Bioconductor package [`ggplot2`](http://docs.ggplot2.org/).\nThe `ggplot2` syntax takes some getting used to, but once you get it, you will find it’s extremely powerful and flexible. We will start with drawing a simple x-y scatterplot of `samplemeans` versus `age_in_days` from the `new_metadata` data frame. Please note that `ggplot2` expects a dataframe or a tibble (the Tidyverse version of a dataframe) as input.\nLet’s start by loading the `ggplot2` library:\n\nlibrary(ggplot2)\n\nThe `ggplot()` function is used to **initialize the basic graph structure**, then we add to it. The basic idea is that you specify different parts of the plot using additional functions one after the other and combine them into a “code chunk” using the `+` operator; the functions in the resulting code chunk are called layers.\nLet’s start:\n\nload(\"data/new_metadata.RData\")\nggplot(new_metadata) # what happens? \n\n\n\n\n\n\n\n\nYou get an blank plot, because you need to specify additional layers using the + operator.\nThe geom (geometric) object is the layer that specifies what kind of plot we want to draw. A plot must have at least one geom ; there is no upper limit. Examples include:\n- points (geom_point, geom_jitter for scatter plots, dot plots, etc)\n- lines (geom_line, for time series, trend lines, etc)\n- boxplot (geom_boxplot, for, well, boxplots!)\nLet’s add a “geom” layer to our plot using the + operator, and since we want a scatter plot so we will use geom_point().\n\n#ggplot(new_metadata) +\n # geom_point() # note what happens here\n\nWhy do we get an error? Is the error message easy to decipher?\nWe get an error because each type of geom usually has a required set of aesthetics to be set. “Aesthetics” are set with the aes() function and can be set either nested within geom_point() (applies only to that layer) or within ggplot() (applies to the whole plot).\nThe aes() function has many different arguments, and all of those arguments take columns from the original data frame as input. It can be used to specify many plot elements including the following:\n- position (i.e., on the x and y axes)\n- color (“outside” color)\n- fill (“inside” color)\n- shape (of points)\n- linetype\n- size\nTo start, we will specify x- and y-axis since geom_point requires the most basic information about a scatterplot, i.e. what you want to plot on the x and y axes. All of the other plot elements mentioned above are optional.\n\nggplot(new_metadata) +\n geom_point(aes(x = age_in_days, y= samplemeans))\n\n\n\n\n\n\n\n\nNow that we have the required aesthetics, let’s add some extras like color to the plot. We can color the points on the plot based on the genotype column** within aes(). You will notice that there are a default set of colors that will be used so we do not have to specify. Note that the legend has been conveniently plotted for us.\n\nggplot(new_metadata) +\n geom_point(aes(x = age_in_days, y= samplemeans, color = genotype)) \n\n\n\n\n\n\n\n\nLet’s try to have both celltype and genotype represented on the plot. To do this we can assign the shape argument in aes() the celltype column, so that each celltype is plotted with a different shaped data point.\n\nggplot(new_metadata) +\n geom_point(aes(x = age_in_days, y= samplemeans, color = genotype,\n shape=celltype)) \n\n\n\n\n\n\n\n\nThe data points are quite small. We can adjust the size of the data points within the geom_point() layer, but it should not be within aes() since we are not mapping it to a column in the input data frame, instead we are just specifying a number.\n\nggplot(new_metadata) +\n geom_point(aes(x = age_in_days, y= samplemeans, color = genotype,\n shape=celltype), size=2.25) \n\n\n\n\n\n\n\n\nThe labels on the x- and y-axis are also quite small and hard to read. To change their size, we need to add an additional theme layer. The ggplot2 theme system handles non-data plot elements such as:\n- Axis label aesthetics\n- Plot background\n- Facet label background\n- Legend appearance\nThere are built-in themes we can use (i.e. theme_bw()) that mostly change the background/foreground colors, by adding it as additional layer. Or we can adjust specific elements of the current default theme by adding the theme() layer and passing in arguments for the things we wish to change. Or we can use both.\nLet’s add a layer theme_bw().\n\nggplot(new_metadata) +\n geom_point(aes(x = age_in_days, y= samplemeans, color = genotype,\n shape=celltype), size=3.0) +\n theme_bw() \n\n\n\n\n\n\n\n\nDo the axis labels or the tick labels get any larger by changing themes?\nNo, they don’t. But, we can add arguments using theme() to change the size of axis labels ourselves. Since we will be adding this layer “on top”, or after theme_bw(), any features we change will override what is set by the theme_bw() layer.\nLet’s increase the size of both the axes titles to be 1.5 times the default size. When modifying the size of text the rel() function is commonly used to specify a change relative to the default.\n\nggplot(new_metadata) +\n geom_point(aes(x = age_in_days, y= samplemeans, color = genotype,\n shape=celltype), size=2.25) +\n theme_bw() +\n theme(axis.title = element_text(size=rel(1.5)))",
"crumbs": [
"In-Class Materials",
"Session 3",
"Session 3: Data Visualization in R"
]
},
{
"objectID": "session-materials/session3/Intro-data-viz.html#histograms",
"href": "session-materials/session3/Intro-data-viz.html#histograms",
"title": "Session 3: Data Visualization in R",
"section": "Histograms",
"text": "Histograms\nTo plot a histogram we require another type of geometric object called geom_histogram, which requires a statistical transformation. Some plot types (such as scatterplots) do not require transformations, each point is plotted at x and y coordinates equal to the original value. Other plots, such as boxplots, histograms, prediction lines etc. need to be transformed. Usually these objects have has a default statistic for the transformation, but that can be changed via the stat_bin argument.\nLet’s plot a histogram of sample mean expression in our data:\n\nggplot(new_metadata) +\n geom_histogram(aes(x = samplemeans))\n\n`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.\n\n\n\n\n\n\n\n\n\nYou will notice that even though the histogram is plotted, R gives a warning message stat_bin() using bins = 30. Pick better value with binwidth. These are the transformations we discussed. Apparently the default is not good enough.\nLet’s change the binwidth values. How does the plot differ?\n\nggplot(new_metadata) +\n geom_histogram(aes(x = samplemeans), stat = \"bin\", binwidth=0.8)\n\n\n\n\n\n\n\n\n> NOTE: You can use the example(\"geom_point\") function here to explore a multitude of different aesthetics and layers that can be added to your plot. As you scroll through the different plots, take note of how the code is modified. You can use this with any of the different geometric object layers available in ggplot2 to learn how you can easily modify your plots!",
"crumbs": [
"In-Class Materials",
"Session 3",
"Session 3: Data Visualization in R"
]
},
{
"objectID": "session-materials/session3/Exercise-1.html",
"href": "session-materials/session3/Exercise-1.html",
"title": "Exercise-1",
"section": "",
"text": "Let’s return to our scatterplot:\n\nlibrary(ggplot2)\nload(\"data/new_metadata.RData\")\n\nggplot(new_metadata) +\n geom_point(aes(x = age_in_days, y= samplemeans, color = genotype,\n shape=celltype), size=2.25) +\n theme_bw() +\n theme(axis.title = element_text(size=rel(1.5))) \n\n\n\n\n\n\n\n\n\n\n1. The current axis label text defaults to what we gave as input to geom_point (i.e the column headers). We can change this by adding additional layers called xlab() and ylab() for the x- and y-axis, respectively. Add these layers to the current plot such that the x-axis is labeled “Age (days)” and the y-axis is labeled “Mean expression”.\n2. Use the ggtitle layer to add a plot title of your choice.\n3. Add the following new layer to the code chunk `theme(plot.title=element_text(hjust=0.5))`.\n- What does it change?\n- How many theme() layers can be added to a ggplot code chunk, in your estimation?\n\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\nggplot(new_metadata) +\n geom_boxplot(aes(x = genotype, y = samplemeans, fill = celltype)) +\n ggtitle(\"Genotype differences in average gene expression\") +\n xlab(\"Genotype\") +\n ylab(\"Mean expression\") +\n theme_bw() +\n theme(axis.title = element_text(size = rel(1.25))) +\n theme(plot.title = element_text(hjust = 0.5, size = rel(1.5)))\n\n\n\n\n\n\n\n\nIt centers and increases the size of the plot title. You can add unlimited theme() layers.\n\n\n\n\n\n\n\n\n\n\n\nAdvanced\n\n\n\nWhen publishing, it is helpful to ensure all plots have similar formatting. To do this we can create a custom function with our preferences for the theme. Create a function called `personal_theme` which takes no arguments and\n- calls one of the ggplot2 themes such as theme_bw()\n- sets the title text size to size=rel(1.5)\n- sets the axis text size (you can use axis.title)\nOnce you have your function, call it to change your histogram’s theme.\n\n\n\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\npersonal_theme <- function(){\n theme_minimal() +\n theme(axis.title=element_text(size=rel(1.5))) +\n theme(plot.title=element_text(size=rel(1.5), hjust=0.5)) \n}\n\nggplot(new_metadata) +\n geom_histogram(aes(x = samplemeans), stat = \"bin\", binwidth=0.8) +\n personal_theme()\n\n\n\n\n\n\n\n\n\n\nThe materials in this lesson have been adapted from work created by the (HBC)\\](http://bioinformatics.sph.harvard.edu/) and Data Carpentry (http://datacarpentry.org/). These are open access materials distributed under the terms of the [Creative Commons Attribution license](https://creativecommons.org/licenses/by/4.0/) (CC BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.",
"crumbs": [
"In-Class Materials",
"Session 3",
"Exercise-1"
]
},
{
"objectID": "session-materials/session3/Exercise-1.html#section",
"href": "session-materials/session3/Exercise-1.html#section",
"title": "Exercise-1",
"section": "",
"text": "Solution\n\n\n\n\nggplot(new_metadata) +\n geom_boxplot(aes(x = genotype, y = samplemeans, fill = celltype)) +\n ggtitle(\"Genotype differences in average gene expression\") +\n xlab(\"Genotype\") +\n ylab(\"Mean expression\") +\n theme_bw() +\n theme(axis.title = element_text(size = rel(1.25))) +\n theme(plot.title = element_text(hjust = 0.5, size = rel(1.5)))\n\n\n\n\n\n\n\n\nIt centers and increases the size of the plot title. You can add unlimited theme() layers.",
"crumbs": [
"In-Class Materials",
"Session 3",
"Exercise-1"
]
},
{
"objectID": "session-materials/session3/Exercise-1.html#section-1",
"href": "session-materials/session3/Exercise-1.html#section-1",
"title": "Exercise-1",
"section": "",
"text": "Advanced\n\n\n\nWhen publishing, it is helpful to ensure all plots have similar formatting. To do this we can create a custom function with our preferences for the theme. Create a function called `personal_theme` which takes no arguments and\n- calls one of the ggplot2 themes such as theme_bw()\n- sets the title text size to size=rel(1.5)\n- sets the axis text size (you can use axis.title)\nOnce you have your function, call it to change your histogram’s theme.",
"crumbs": [
"In-Class Materials",
"Session 3",
"Exercise-1"
]
},
{
"objectID": "session-materials/session3/Exercise-1.html#section-2",
"href": "session-materials/session3/Exercise-1.html#section-2",
"title": "Exercise-1",
"section": "",
"text": "Solution\n\n\n\n\npersonal_theme <- function(){\n theme_minimal() +\n theme(axis.title=element_text(size=rel(1.5))) +\n theme(plot.title=element_text(size=rel(1.5), hjust=0.5)) \n}\n\nggplot(new_metadata) +\n geom_histogram(aes(x = samplemeans), stat = \"bin\", binwidth=0.8) +\n personal_theme()\n\n\n\n\n\n\n\n\n\n\nThe materials in this lesson have been adapted from work created by the (HBC)\\](http://bioinformatics.sph.harvard.edu/) and Data Carpentry (http://datacarpentry.org/). These are open access materials distributed under the terms of the [Creative Commons Attribution license](https://creativecommons.org/licenses/by/4.0/) (CC BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.",
"crumbs": [
"In-Class Materials",
"Session 3",
"Exercise-1"
]
},
{
"objectID": "session-materials/session4/basic-stats.html",
"href": "session-materials/session4/basic-stats.html",
"title": "Session 4: Foundational Statistics for t-tests",
"section": "",
"text": "In biomedical research, we can rarely study an entire population (e.g., all patients with a disease). Instead, we work with a sample (a subset of that population). Our goal is to use that sample to make an inference about the population.\nFor example, in immunology, we might want to know if a vaccine increases antibody titers in the general population. We can’t test everyone, so we study a sample of subjects and use statistics to make inferences about the broader population.",
"crumbs": [
"In-Class Materials",
"Session 4",
"Session 4: Foundational Statistics for t-tests"
]
},
{
"objectID": "session-materials/session4/basic-stats.html#central-tendency-mean-vs.-median",
"href": "session-materials/session4/basic-stats.html#central-tendency-mean-vs.-median",
"title": "Session 4: Foundational Statistics for t-tests",
"section": "Central Tendency: Mean vs. Median",
"text": "Central Tendency: Mean vs. Median\n\nMean: The average value (sum divided by count). It is sensitive to outliers.\nMedian: The middle value (50th percentile). It is robust to outliers.\n\nLet’s see how outliers affect both measures using antibody titer data:\n\n\nCode\n# To better illustrate the concept, we'll use a smaller sample size (n=15)\n# and a more extreme outlier. This will make the effect on the mean more dramatic.\n# rnorm(n, mean, sd) generates n random numbers from a normal distribution.\n# A normal distribution is the classic \"bell curve\" - most values cluster around \n# the mean, with fewer values at the extremes. It's very common in biology.\nnormal_titers <- rnorm(n = 15, mean = 105, sd = 8) # Generate 15 normal antibody titers\noutlier_titers <- c(normal_titers, 500) # Add one extreme outlier (500) to the dataset\n\n# Compare mean and median using a robust dplyr approach\nbind_rows( # Combine datasets\n tibble(type = \"Without Outlier\", titer = normal_titers), # Create dataset without outlier\n tibble(type = \"With Outlier\", titer = outlier_titers) # Create dataset with outlier\n) |>\n group_by(type) |> # Group by dataset type\n summarise( # Calculate summary statistics\n Mean = mean(titer), # Calculate mean for each group\n Median = median(titer), # Calculate median for each group\n .groups = \"drop\" # Remove grouping\n ) |>\n knitr::kable(digits = 1) # Create formatted table with 1 decimal place\n\n\n\n\n\ntype\nMean\nMedian\n\n\n\n\nWith Outlier\n133.3\n108.1\n\n\nWithout Outlier\n108.9\n107.9\n\n\n\n\n\nCode\n# Visualize both distributions with mean and median markers\nplot_df <- bind_rows(\n tibble(titer = normal_titers, type = \"Without Outlier\"),\n tibble(titer = outlier_titers, type = \"With Outlier\")\n)\n\nmeans_df <- plot_df |>\n dplyr::group_by(type) |>\n dplyr::summarise(xintercept = mean(titer), .groups = \"drop\")\n\nmedians_df <- plot_df |>\n dplyr::group_by(type) |>\n dplyr::summarise(xintercept = median(titer), .groups = \"drop\")\n\nggplot(plot_df, aes(x = titer)) +\n geom_histogram(binwidth = 5, fill = \"steelblue\", alpha = 0.7, color = \"black\") + \n geom_vline(data = means_df, aes(xintercept = xintercept, color = \"Mean\")) +\n geom_vline(data = medians_df, aes(xintercept = xintercept, color = \"Median\")) +\n facet_wrap(~type, scales = \"free_x\") +\n scale_color_manual(name = \"Statistic\", values = c(\"Mean\" = \"red\", \"Median\" = \"blue\")) +\n labs(title = \"Effect of Outliers on Mean vs Median\",\n x = \"Antibody Titer (AU)\",\n y = \"Count\") +\n theme_minimal()\n\n\n\n\n\n\n\n\n\nCode\n# A density plot shows a smoothed version of the distribution.\nggplot(plot_df, aes(x = titer)) +\n geom_density(fill = \"steelblue\", alpha = 0.7, color = \"black\") +\n geom_vline(data = means_df, aes(xintercept = xintercept, color = \"Mean\")) +\n geom_vline(data = medians_df, aes(xintercept = xintercept, color = \"Median\")) +\n facet_wrap(~type, scales = \"free_x\") +\n scale_color_manual(name = \"Statistic\", values = c(\"Mean\" = \"red\", \"Median\" = \"blue\")) +\n labs(title = \"Effect of Outliers on Mean vs Median (Density View)\",\n x = \"Antibody Titer (AU)\",\n y = \"Density\") +\n theme_minimal()\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nNote\n\n\n\nNotice how the mean is pulled toward the outlier, while the median stays stable. When your data has outliers or is skewed, the median is often a better measure of central tendency.",
"crumbs": [
"In-Class Materials",
"Session 4",
"Session 4: Foundational Statistics for t-tests"
]
},
{
"objectID": "session-materials/session4/basic-stats.html#variability-standard-deviation-sd-vs.-standard-error-se",
"href": "session-materials/session4/basic-stats.html#variability-standard-deviation-sd-vs.-standard-error-se",
"title": "Session 4: Foundational Statistics for t-tests",
"section": "Variability: Standard Deviation (SD) vs. Standard Error (SE)",
"text": "Variability: Standard Deviation (SD) vs. Standard Error (SE)\nThis is one of the most common points of confusion in statistics. They measure different things:\n\nStandard Deviation (SD): Measures the variability of individual observations in your sample. A large SD means your data points are very spread out from the mean.\nStandard Error (SE): Measures the precision of your sample mean. It tells you how much you would expect the mean to vary if you repeated your experiment with new samples.\n\nThe formula for SE makes the distinction clear: SE = SD / sqrt(n). The SE gets smaller as your sample size increases because larger samples give more precise estimates of the population mean.\nLet’s calculate both for our antibody titer data:\n\n\nCode\n# Calculate SD and SE for both datasets\nbind_rows(\n tibble(data = \"Without Outlier\", titers = normal_titers),\n tibble(data = \"With Outlier\", titers = outlier_titers)\n) |>\n group_by(data) |>\n summarise(\n n = n(),\n mean_titer = mean(titers),\n sd_titer = sd(titers),\n se = sd(titers) / sqrt(n())\n ) |>\n knitr::kable(digits = 1)\n\n\n\n\n\ndata\nn\nmean_titer\nsd_titer\nse\n\n\n\n\nWith Outlier\n16\n133.3\n98.1\n24.5\n\n\nWithout Outlier\n15\n108.9\n8.2\n2.1\n\n\n\n\n\n\n\n\n\n\n\nImportant\n\n\n\nKey points about SD vs SE:\n\nSD describes the spread in your data (variability between observations)\nSE describes the precision of your mean estimate\nSE = SD/√n, so it gets smaller with larger sample sizes\nSD does not systematically change with sample size",
"crumbs": [
"In-Class Materials",
"Session 4",
"Session 4: Foundational Statistics for t-tests"
]
},
{
"objectID": "session-materials/session4/t-tests-and-viz.html",
"href": "session-materials/session4/t-tests-and-viz.html",
"title": "Session 4: t-tests and Visualization",
"section": "",
"text": "This document walks through performing, interpreting, and visualizing t-tests using simulated immunology data. We will cover:\n\nChecking Assumptions: Ensuring the t-test is appropriate\nUnpaired t-test: Comparing antibody titers between Control and Vaccine groups\nPaired t-test: Comparing Pre- and Post-vaccination titers\nVisualization: Creating publication-quality figures\n\n\n\nCode\n# Load required packages for data manipulation, visualization, and statistical testing\nlibrary(tidyverse) # For data manipulation (dplyr) and visualization (ggplot2)\nlibrary(car) # For Levene's test (variance equality)\n\n# Set seed to ensure reproducible random number generation\n# This means anyone running this code will get the exact same results\nset.seed(42)\n\n# Generate example immunology data for demonstration\n# Unpaired data: Control vs Vaccine groups (different subjects)\nn_control <- 20 # Number of control subjects\nn_vax <- 20 # Number of vaccinated subjects\ncontrol_titer <- rnorm(n_control, mean = 200, sd = 60) # Control group antibody titers\nvax_titer <- rnorm(n_vax, mean = 320, sd = 80) # Vaccine group: higher mean, more variable\n\n# Combine into a tidy data frame for analysis\ndat_unpaired <- tibble(\n group = rep(c(\"Control\",\"Vaccine\"), c(n_control, n_vax)), # Group labels\n titer = c(control_titer, vax_titer) # All titer measurements\n)\n\n# Paired data: Pre vs Post vaccination (same subjects measured twice)\nn <- 20 # Number of subjects\npre <- rnorm(n, mean = 200, sd = 60) # Baseline titers before vaccination\ntrue_boost <- rnorm(n, mean = 120, sd = 40) # Individual vaccine response varies\npost <- pre + true_boost # Post-vaccination = baseline + boost\n\n# Create paired data frame\ndat_paired <- tibble(\n id = seq_len(n), # Subject identifier\n pre = pre, # Pre-vaccination titers\n post = post, # Post-vaccination titers\n diff = post - pre # Change for each subject\n)",
"crumbs": [
"In-Class Materials",
"Session 4",
"Session 4: t-tests and Visualization"
]
},
{
"objectID": "session-materials/session4/t-tests-and-viz.html#checking-assumptions",
"href": "session-materials/session4/t-tests-and-viz.html#checking-assumptions",
"title": "Session 4: t-tests and Visualization",
"section": "Checking Assumptions",
"text": "Checking Assumptions\nBefore running any t-test, we must verify that our data meets the required assumptions. Violating these assumptions can lead to incorrect conclusions.\nA t-test has three main assumptions:\n\nIndependence: Observations in one group are independent of the other (and within groups)\nNormality: Data in each group are approximately normally distributed\n\nHomogeneity of Variances: The two groups have similar variances (for Student’s t-test)\n\nLet’s check each assumption systematically.\n\n1. Independence Check\nIndependence means that: - Each subject’s measurement doesn’t influence another subject’s measurement - Observations within each group are independent - The two groups are independent of each other\nIn our case, this is satisfied by our experimental design (different subjects in each group, no repeated measures).\n\n\n2. Normality Check\nQ-Q plots (quantile-quantile plots) help us assess if our data follows a normal distribution: - If data is normal, points should fall close to the diagonal line - Systematic deviations suggest non-normality - Small deviations are usually acceptable, especially with moderate sample sizes\n\n\nCode\n# Create Q-Q plots to check normality assumption for each group\nggplot(dat_unpaired, aes(sample = titer, color = group)) + # Map titer to sample quantiles, color by group\n stat_qq() + # Add Q-Q points\n stat_qq_line() + # Add reference line for perfect normality\n facet_wrap(~group, scales = \"free\") + # Separate plot for each group\n labs(title = \"Q-Q Plots for Normality Check\", # Add informative title\n subtitle = \"Points should fall close to the line if data is normal\") + \n theme_minimal() # Clean theme\n\n\n\n\n\n\n\n\n\nInterpretation: The points fall close to the line for both groups, suggesting the normality assumption is reasonable. Minor deviations at the extremes are common and acceptable.\n\n\n3. Homogeneity of Variances Check\nWe need to check if the two groups have similar variances. We can do this visually and with a formal test.\n\nVisual Check: Compare Spreads\n\n\nCode\n# Visual comparison of variances using boxplots\nggplot(dat_unpaired, aes(x = group, y = titer, fill = group)) + # Map group to x, titer to y\n geom_boxplot(alpha = 0.7) + # Create boxplots to compare spreads\n geom_jitter(width = 0.2, alpha = 0.6) + # Add individual points\n labs(title = \"Comparing Variances Between Groups\",\n subtitle = \"Look for similar box heights and whisker lengths\",\n x = \"Group\", y = \"Antibody Titer (AU)\") +\n theme_minimal()\n\n\n\n\n\n\n\n\n\nCode\n# Calculate and display group variances\ndat_unpaired |> # Start with our data\n group_by(group) |> # Group by Control vs Vaccine\n summarise( # Calculate variance statistics\n n = n(), # Sample size\n variance = var(titer), # Variance\n sd = sd(titer), # Standard deviation\n .groups = \"drop\" # Remove grouping\n ) |>\n knitr::kable(digits = 1, caption = \"Variance Comparison\") # Create formatted table\n\n\n\nVariance Comparison\n\n\ngroup\nn\nvariance\nsd\n\n\n\n\nControl\n20\n6202.8\n78.8\n\n\nVaccine\n20\n7880.5\n88.8\n\n\n\n\n\n\n\nFormal Test: Levene’s Test\nLevene’s test formally tests for equal variances: - H₀: Variances are equal - Ha: Variances are different - If p > 0.05, we don’t reject H₀ (variances likely equal)\n\n\nCode\n# Levene's test for equality of variances\nlevene_result <- leveneTest(titer ~ group, data = dat_unpaired) # Test variance equality\n\n\nWarning in leveneTest.default(y = y, group = group, ...): group coerced to\nfactor.\n\n\nCode\nlevene_result # Display results\n\n\nLevene's Test for Homogeneity of Variance (center = median)\n Df F value Pr(>F)\ngroup 1 0.3088 0.5817\n 38 \n\n\nDecision Rule: - If variances are equal (Levene’s p > 0.05): Can use Student’s t-test - If variances are unequal (Levene’s p ≤ 0.05): Should use Welch’s t-test - Default recommendation: Use Welch’s t-test (it works well in both cases)",
"crumbs": [
"In-Class Materials",
"Session 4",
"Session 4: t-tests and Visualization"
]
},
{
"objectID": "session-materials/session4/t-tests-and-viz.html#performing-the-t-test",
"href": "session-materials/session4/t-tests-and-viz.html#performing-the-t-test",
"title": "Session 4: t-tests and Visualization",
"section": "Performing the t-test",
"text": "Performing the t-test\nNow we’ll run the actual t-test. We’ll use Welch’s t-test (the default in R) because it’s more robust and doesn’t assume equal variances.\n\nUnderstanding t-test Types\n\nStudent’s t-test (var.equal = TRUE): Assumes equal variances\nWelch’s t-test (var.equal = FALSE, default): Doesn’t assume equal variances\nWelch’s test is generally preferred as it performs well even when variances are equal\n\n\n\nCode\n# Welch's two-sample t-test (default, safer choice)\nunpaired_test <- t.test(titer ~ group, data = dat_unpaired) # Perform Welch's t-test\nunpaired_test # Display full results\n\n\n\n Welch Two Sample t-test\n\ndata: titer by group\nt = -3.2712, df = 37.468, p-value = 0.002303\nalternative hypothesis: true difference in means between group Control and group Vaccine is not equal to 0\n95 percent confidence interval:\n -140.55004 -33.06087\nsample estimates:\nmean in group Control mean in group Vaccine \n 211.5152 298.3207 \n\n\n\n\nUnderstanding the Output\nLet’s break down each part of the t-test output:\n\nt: The t-statistic (how many standard errors the difference is from zero)\ndf: Degrees of freedom (affects the p-value calculation)\np-value: Probability of seeing this result (or more extreme) if H₀ were true\n95% confidence interval: Range of plausible values for the true difference\nsample estimates: Mean values for each group\n\n\n\nCalculate Effect Size (Cohen’s d)\nEffect size tells us how meaningful the difference is, not just whether it’s statistically significant.\n\n\nCode\n# Calculate Cohen's d manually for unpaired data\ngroup_stats <- dat_unpaired |> # Start with our data\n group_by(group) |> # Group by Control vs Vaccine\n summarise( # Calculate group statistics\n n = n(), # Sample size\n mean = mean(titer), # Group mean\n sd = sd(titer), # Group standard deviation\n .groups = \"drop\" # Remove grouping\n )\n\n# Extract values for Cohen's d calculation\ncontrol_stats <- group_stats |> filter(group == \"Control\") # Control group statistics\nvaccine_stats <- group_stats |> filter(group == \"Vaccine\") # Vaccine group statistics\n\n# Calculate pooled standard deviation\npooled_sd <- sqrt(((control_stats$n - 1) * control_stats$sd^2 + \n (vaccine_stats$n - 1) * vaccine_stats$sd^2) / \n (control_stats$n + vaccine_stats$n - 2)) # Pooled SD formula\n\n# Calculate Cohen's d\ncohens_d <- (vaccine_stats$mean - control_stats$mean) / pooled_sd # Effect size calculation\n\ncat(\"Cohen's d =\", round(cohens_d, 2)) # Display result\n\n\nCohen's d = 1.03\n\n\nCode\ncat(\"\\nInterpretation:\", \n if(abs(cohens_d) < 0.2) \"Negligible effect\" else\n if(abs(cohens_d) < 0.5) \"Small effect\" else \n if(abs(cohens_d) < 0.8) \"Medium effect\" else \"Large effect\") # Interpret magnitude\n\n\n\nInterpretation: Large effect",
"crumbs": [
"In-Class Materials",
"Session 4",
"Session 4: t-tests and Visualization"
]
},
{
"objectID": "session-materials/session4/t-tests-and-viz.html#checking-assumptions-1",
"href": "session-materials/session4/t-tests-and-viz.html#checking-assumptions-1",
"title": "Session 4: t-tests and Visualization",
"section": "Checking Assumptions",
"text": "Checking Assumptions\nFor paired data, the key assumptions are:\n\nIndependence of pairs: Each subject’s change is independent of others\nNormality of differences: The paired differences should be normally distributed\n\nThe most important check is normality of the differences (not the original measurements).\n\n\nCode\n# Check normality of the paired differences\nggplot(dat_paired, aes(sample = diff)) + # Use differences for Q-Q plot\n stat_qq() + # Add Q-Q points\n stat_qq_line() + # Add reference line\n labs(title = \"Q-Q Plot of Paired Differences\",\n subtitle = \"Differences should be normally distributed for paired t-test\",\n x = \"Theoretical Quantiles\", y = \"Sample Quantiles\") +\n theme_minimal() # Clean theme\n\n\n\n\n\n\n\n\n\nCode\n# Also create a histogram of differences for additional visual check\nggplot(dat_paired, aes(x = diff)) + # Plot distribution of differences\n geom_histogram(bins = 8, fill = \"steelblue\", alpha = 0.7, color = \"black\") + # Create histogram\n geom_vline(xintercept = 0, color = \"red\", linetype = \"dashed\", size = 1) + # Add reference line at zero\n labs(title = \"Distribution of Paired Differences\",\n subtitle = \"Red line shows 'no change' (difference = 0)\",\n x = \"Change in Titer (Post - Pre)\", y = \"Count\") +\n theme_minimal() # Clean theme\n\n\nWarning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.\nℹ Please use `linewidth` instead.\n\n\n\n\n\n\n\n\n\nInterpretation: The Q-Q plot shows the differences are approximately normally distributed, so the paired t-test is appropriate.",
"crumbs": [
"In-Class Materials",
"Session 4",
"Session 4: t-tests and Visualization"
]
},
{
"objectID": "session-materials/session4/t-tests-and-viz.html#performing-the-paired-t-test",
"href": "session-materials/session4/t-tests-and-viz.html#performing-the-paired-t-test",
"title": "Session 4: t-tests and Visualization",
"section": "Performing the Paired t-test",
"text": "Performing the Paired t-test\nThe paired t-test essentially asks: “Is the mean difference significantly different from zero?”\n\n\nCode\n# Paired t-test comparing pre and post measurements\npaired_test <- t.test(dat_paired$post, dat_paired$pre, paired = TRUE) # Perform paired t-test\npaired_test # Display results\n\n\n\n Paired t-test\n\ndata: dat_paired$post and dat_paired$pre\nt = 17.384, df = 19, p-value = 4.005e-13\nalternative hypothesis: true mean difference is not equal to 0\n95 percent confidence interval:\n 111.2026 141.6455\nsample estimates:\nmean difference \n 126.424 \n\n\nCode\n# Alternative syntax using the difference directly\n# paired_test_alt <- t.test(dat_paired$diff, mu = 0) # Test if mean difference = 0\n\n\n\nUnderstanding Paired t-test Output\n\nt: t-statistic for the mean difference\ndf: Degrees of freedom (n - 1 pairs)\np-value: Probability of observing this mean difference (or larger) if true difference = 0\n95% confidence interval: Range of plausible values for the true mean difference\nmean of differences: Average change across all subjects\n\n\n\nCalculate Effect Size for Paired Data (Cohen’s dz)\nFor paired data, we use Cohen’s dz (standardized mean difference):\n\n\nCode\n# Calculate Cohen's dz for paired data\nmean_diff <- mean(dat_paired$diff) # Mean of differences\nsd_diff <- sd(dat_paired$diff) # SD of differences\ncohens_dz <- mean_diff / sd_diff # Cohen's dz formula\n\ncat(\"Cohen's dz =\", round(cohens_dz, 2)) # Display result\n\n\nCohen's dz = 3.89\n\n\nCode\ncat(\"\\nInterpretation:\", \n if(abs(cohens_dz) < 0.2) \"Negligible effect\" else\n if(abs(cohens_dz) < 0.5) \"Small effect\" else \n if(abs(cohens_dz) < 0.8) \"Medium effect\" else \"Large effect\") # Interpret magnitude\n\n\n\nInterpretation: Large effect\n\n\nCode\n# Additional summary of the changes\ncat(\"\\n\\nSummary of Changes:\")\n\n\n\n\nSummary of Changes:\n\n\nCode\ncat(\"\\n- Average change:\", round(mean_diff, 1), \"AU\")\n\n\n\n- Average change: 126.4 AU\n\n\nCode\ncat(\"\\n- 95% CI for change:\", round(paired_test$conf.int[1], 1), \"to\", round(paired_test$conf.int[2], 1), \"AU\")\n\n\n\n- 95% CI for change: 111.2 to 141.6 AU\n\n\nCode\nsubjects_improved <- sum(dat_paired$diff > 0) # Count subjects who improved\ncat(\"\\n- Subjects who improved:\", subjects_improved, \"out of\", nrow(dat_paired), \n paste0(\"(\", round(100*subjects_improved/nrow(dat_paired), 1), \"%)\")) # Percentage who improved\n\n\n\n- Subjects who improved: 20 out of 20 (100%)",
"crumbs": [
"In-Class Materials",
"Session 4",
"Session 4: t-tests and Visualization"
]
},
{
"objectID": "session-materials/session4/t-tests-and-viz.html#unpaired-data-figure",
"href": "session-materials/session4/t-tests-and-viz.html#unpaired-data-figure",
"title": "Session 4: t-tests and Visualization",
"section": "Unpaired Data Figure",
"text": "Unpaired Data Figure\n\n\nCode\n# Create a publication-ready plot for unpaired data\np_unpaired <- ggplot(dat_unpaired, aes(x = group, y = titer, fill = group)) + # Map aesthetics\n # Add violin plots to show distribution shape\n geom_violin(width = 0.9, alpha = 0.3, trim = FALSE) + # Show full distribution shape\n # Add boxplots to show quartiles\n geom_boxplot(width = 0.2, outlier.shape = NA, alpha = 0.7) + # Show median and quartiles\n # Add individual data points\n geom_jitter(width = 0.1, alpha = 0.6, size = 1.5) + # Show all individual points\n # Add mean points with error bars (95% CI)\n stat_summary(fun.data = mean_cl_normal, geom = \"errorbar\", # Add 95% CI error bars\n width = 0.1, size = 1, color = \"black\") +\n stat_summary(fun = mean, geom = \"point\", size = 3, color = \"black\", # Add mean points\n shape = 18) + # Diamond shape for means\n # Customize colors\n scale_fill_manual(values = c(\"Control\" = \"#E69F00\", \"Vaccine\" = \"#0072B2\")) + # Colorblind-friendly colors\n # Add labels and formatting\n labs(\n title = \"Vaccination Significantly Increases Antibody Titers\",\n subtitle = paste0(\"Welch's t-test: t(\", round(unpaired_test$parameter,1), \") = \", \n round(unpaired_test$statistic,2), \", p \", \n ifelse(unpaired_test$p.value < 0.001, \"< 0.001\", \n paste(\"=\", round(unpaired_test$p.value, 3))),\n \", Cohen's d = \", round(cohens_d, 2)),\n x = NULL,\n y = \"Antibody Titer (AU)\",\n caption = \"Error bars show 95% CI of the mean. Diamonds indicate group means.\"\n ) +\n theme_minimal(base_size = 12) + # Clean theme with readable text\n theme(\n legend.position = \"none\", # Remove redundant legend\n plot.title = element_text(size = 14, face = \"bold\"), # Larger title\n plot.subtitle = element_text(size = 11), # Subtitle formatting\n plot.caption = element_text(size = 9, color = \"gray50\") # Small caption\n )\n\np_unpaired # Display the plot",
"crumbs": [
"In-Class Materials",
"Session 4",
"Session 4: t-tests and Visualization"
]
},
{
"objectID": "session-materials/session4/t-tests-and-viz.html#paired-data-figure",
"href": "session-materials/session4/t-tests-and-viz.html#paired-data-figure",
"title": "Session 4: t-tests and Visualization",
"section": "Paired Data Figure",
"text": "Paired Data Figure\n\n\nCode\n# Prepare data for paired visualization\ndat_paired_long <- dat_paired |> # Start with paired data\n pivot_longer(c(pre, post), names_to = \"time\", values_to = \"titer\") |> # Convert to long format\n mutate(time = factor(time, levels = c(\"pre\", \"post\"), # Ensure proper ordering\n labels = c(\"Pre-vaccination\", \"Post-vaccination\"))) # Add descriptive labels\n\n# Create publication-ready paired plot\np_paired <- ggplot(dat_paired_long, aes(x = time, y = titer)) + # Map time to x, titer to y\n # Add individual subject lines (spaghetti plot)\n geom_line(aes(group = id), alpha = 0.3, color = \"gray60\", size = 0.5) + # Connect paired measurements\n # Add individual points\n geom_point(aes(color = time), size = 2, alpha = 0.7) + # Color points by timepoint\n # Add summary statistics\n stat_summary(fun = mean, geom = \"point\", size = 4, color = \"black\", # Add mean points\n shape = 18) + # Diamond shape\n stat_summary(fun.data = mean_cl_normal, geom = \"errorbar\", # Add 95% CI error bars\n width = 0.1, size = 1.2, color = \"black\") +\n # Connect the means with a thick line\n stat_summary(fun = mean, geom = \"line\", group = 1, # Connect mean points\n size = 2, color = \"black\", alpha = 0.8) +\n # Customize colors\n scale_color_manual(values = c(\"Pre-vaccination\" = \"#CC79A7\", \n \"Post-vaccination\" = \"#009E73\"), # Colorblind-friendly\n name = \"Timepoint\") +\n # Add labels and formatting\n labs(\n title = \"Antibody Titers Increase After Vaccination\",\n subtitle = paste0(\"Paired t-test: t(\", paired_test$parameter, \") = \", \n round(paired_test$statistic,2), \", p \",\n ifelse(paired_test$p.value < 0.001, \"< 0.001\", \n paste(\"=\", round(paired_test$p.value, 3))),\n \", Cohen's dz = \", round(cohens_dz, 2)),\n x = NULL,\n y = \"Antibody Titer (AU)\",\n caption = paste0(\"Each gray line connects measurements from one subject (n=\", nrow(dat_paired), \n \"). Black diamonds show means ± 95% CI.\")\n ) +\n theme_minimal(base_size = 12) + # Clean theme\n theme(\n plot.title = element_text(size = 14, face = \"bold\"), # Larger title\n plot.subtitle = element_text(size = 11), # Subtitle formatting\n plot.caption = element_text(size = 9, color = \"gray50\"), # Small caption\n legend.position = \"bottom\" # Legend at bottom\n )\n\np_paired # Display the plot",
"crumbs": [
"In-Class Materials",
"Session 4",
"Session 4: t-tests and Visualization"
]
},
{
"objectID": "session-materials/session4/t-tests-and-viz.html#example-results-paragraphs",
"href": "session-materials/session4/t-tests-and-viz.html#example-results-paragraphs",
"title": "Session 4: t-tests and Visualization",
"section": "Example Results Paragraphs",
"text": "Example Results Paragraphs\n\nFor the Unpaired Analysis:\n\nVaccination Effect on Antibody Titers: Vaccination significantly increased antibody titers compared to controls. The vaccine group (M = 298.3 AU, SD = 88.8) had substantially higher titers than the control group (M = 211.5 AU, SD = 78.8). A Welch’s t-test confirmed this difference was statistically significant, t(37.5) = 3.27, p = 0.002, 95% CI [-140.6, -33.1], representing a large effect size (Cohen’s d = 1.03).\n\n\n\nFor the Paired Analysis:\n\nPre-Post Vaccination Changes: Antibody titers increased significantly following vaccination. The mean increase was 126.4 AU (SD = 32.5), 95% CI [111.2, 141.6]. A paired t-test confirmed this change was statistically significant, t(19) = 17.38, p < 0.001, representing a very large effect (Cohen’s dz = 3.89). All but 0 participants (100%) showed increased titers post-vaccination.",
"crumbs": [
"In-Class Materials",
"Session 4",
"Session 4: t-tests and Visualization"
]
},
{
"objectID": "session-materials/session4/t-tests-and-viz.html#when-assumptions-are-violated",
"href": "session-materials/session4/t-tests-and-viz.html#when-assumptions-are-violated",
"title": "Session 4: t-tests and Visualization",
"section": "When Assumptions Are Violated",
"text": "When Assumptions Are Violated\nIf your data doesn’t meet t-test assumptions, consider these alternatives:\n\nNon-parametric Tests\n\n\nCode\n# If normality is severely violated, use Wilcoxon tests\n\n# For unpaired data: Mann-Whitney U test (Wilcoxon rank-sum)\nwilcox_unpaired <- wilcox.test(titer ~ group, data = dat_unpaired) # Non-parametric alternative\ncat(\"Wilcoxon rank-sum test p-value:\", round(wilcox_unpaired$p.value, 4))\n\n\nWilcoxon rank-sum test p-value: 0.0056\n\n\nCode\n# For paired data: Wilcoxon signed-rank test\nwilcox_paired <- wilcox.test(dat_paired$post, dat_paired$pre, paired = TRUE) # Non-parametric alternative\ncat(\"\\nWilcoxon signed-rank test p-value:\", round(wilcox_paired$p.value, 4))\n\n\n\nWilcoxon signed-rank test p-value: 0\n\n\n\n\nGuidelines for Choosing Tests\n\nNormal data, equal variances: Student’s t-test\nNormal data, unequal variances: Welch’s t-test (default recommendation)\nNon-normal data: Wilcoxon tests\nSmall samples (n < 15): Consider non-parametric tests\nWhen in doubt: Use Welch’s t-test (robust and widely applicable)",
"crumbs": [
"In-Class Materials",
"Session 4",
"Session 4: t-tests and Visualization"
]
},
{
"objectID": "session-materials/session4/t-tests-and-viz.html#key-takeaways",
"href": "session-materials/session4/t-tests-and-viz.html#key-takeaways",
"title": "Session 4: t-tests and Visualization",
"section": "Key Takeaways",
"text": "Key Takeaways\n\nAlways check assumptions before running t-tests\nUse Welch’s t-test as the default for unpaired comparisons\nReport effect sizes alongside p-values\nInclude confidence intervals for all estimates\nVisualize your data to tell the complete story\nConsider practical significance, not just statistical significance",
"crumbs": [
"In-Class Materials",
"Session 4",
"Session 4: t-tests and Visualization"
]
},
{
"objectID": "session-materials/session4/t-tests-and-viz.html#practice-exercises",
"href": "session-materials/session4/t-tests-and-viz.html#practice-exercises",
"title": "Session 4: t-tests and Visualization",
"section": "Practice Exercises",
"text": "Practice Exercises\n\nAssumption Checking: What would you do if the Q-Q plot showed strong departures from normality?\nEffect Size Interpretation: A study reports Cohen’s d = 0.3. How would you interpret this in practical terms?\nStudy Design: When would you choose a paired vs. unpaired design for an immunology study?\n\n\n\n\n\n\n\nTip\n\n\n\nPractice Challenge: Try modifying the simulation parameters in the setup chunk. What happens to the p-values and effect sizes when you: - Reduce the sample sizes? - Make the group means closer together? - Increase the standard deviations?",
"crumbs": [
"In-Class Materials",
"Session 4",
"Session 4: t-tests and Visualization"
]
},
{
"objectID": "session-materials/session4/t-tests-and-viz.html#next-steps",
"href": "session-materials/session4/t-tests-and-viz.html#next-steps",
"title": "Session 4: t-tests and Visualization",
"section": "Next Steps",
"text": "Next Steps\n\nLearn about multiple comparisons and ANOVA for comparing >2 groups\nExplore more advanced visualization techniques\nStudy power analysis for sample size planning\nLearn about regression analysis for continuous predictors",
"crumbs": [
"In-Class Materials",
"Session 4",
"Session 4: t-tests and Visualization"
]
},
{
"objectID": "session-materials/session4/immunology-simulation.html",
"href": "session-materials/session4/immunology-simulation.html",
"title": "Session 4: Simulating Immunology Data",
"section": "",
"text": "In statistics, it is incredibly helpful to work with data where we know the underlying truth. Simulation allows us to create such datasets. We can set the true means and standard deviations for different groups and then use statistical tests to see if they can correctly identify these differences. This is a powerful way to build intuition for how statistical methods work.\nIn this section, we will simulate antibody titer data for two common experimental designs:\n\nUnpaired Design: Comparing two independent groups (e.g., control mice vs. vaccinated mice).\nPaired Design: Comparing two related measurements from the same subjects (e.g., antibody titers before and after vaccination).\n\n\n\nCode\n# This chunk ensures all necessary packages are installed and loaded.\n\n# List of required packages\npackages <- c(\"tidyverse\", \"here\") # a smaller set for this document\n\n# Check if packages are installed, install if not\nfor (pkg in packages) {\n if (!require(pkg, character.only = TRUE)) {\n install.packages(pkg, dependencies = TRUE, repos = \"https://cran.rstudio.com/\")\n }\n}\n\n# Load all packages\ninvisible(lapply(packages, library, character.only = TRUE))\n\n# Use set.seed() to make our random data generation reproducible.\n# Anyone who runs this code with the same seed will get the exact same \"random\" numbers.\nset.seed(42)\n\n\n\n\nHere, we’ll simulate data for two separate groups of mice. We expect the vaccinated group to have a higher average antibody titer. We will also make their titers slightly more variable.\n\n\nCode\n# Realistic scenario: We're measuring IgG antibodies against influenza \n# after seasonal flu vaccination. Typical baseline titers range 50-400 AU,\n# with 2-4 fold increases expected post-vaccination.\n\n# Define the parameters for our simulation\nn_control <- 20 # Number of control subjects\nn_vax <- 20 # Number of vaccinated subjects\n\n# Generate random data from a normal distribution with rnorm()\n# Arguments: n = sample size, mean = center of distribution, sd = spread\ncontrol_titer <- rnorm(n_control, mean = 200, sd = 60) # Control group titers\nvax_titer <- rnorm(n_vax, mean = 320, sd = 80) # Vaccine group: higher mean, larger variance\n\n# Combine the data into a single, tidy data frame (tibble)\ndat_unpaired <- tibble(\n group = rep(c(\"Control\",\"Vaccine\"), c(n_control, n_vax)), # Create group labels\n titer = c(control_titer, vax_titer) # Combine all titer values\n)\n\n# A quick peek at the first few rows\nhead(dat_unpaired) # Display first 6 rows of the data\n\n\n# A tibble: 6 × 2\n group titer\n <chr> <dbl>\n1 Control 282.\n2 Control 166.\n3 Control 222.\n4 Control 238.\n5 Control 224.\n6 Control 194.\n\n\n\n\nBefore any formal testing, it’s crucial to explore the data visually. This helps us check for outliers, see the data’s distribution, and get a feel for the differences between groups.\n\n\nCode\n# Option 1: Side-by-side histograms for clearer comparison\nggplot(dat_unpaired, aes(x = titer, fill = group)) + # Map titer to x-axis, group to fill color\n geom_histogram(bins = 12, alpha = 0.8, color = \"black\") + # Solid histograms with black borders\n facet_wrap(~group, ncol = 2) + # Separate panels for each group\n scale_fill_manual(values = c(\"Control\" = \"#E69F00\", \"Vaccine\" = \"#0072B2\")) + # Colorblind-friendly colors\n labs(title = \"Distribution of Antibody Titers by Group\", \n x = \"Antibody titer (AU)\", y = \"Count\") + # Add labels\n theme_minimal(base_size = 14) + # Clean theme with larger text\n theme(legend.position = \"none\") # Remove redundant legend\n\n\n\n\n\n\n\n\n\nCode\n# Option 2: Density plots for smooth comparison (better for overlapping)\nggplot(dat_unpaired, aes(x = titer, fill = group, color = group)) + # Map aesthetics\n geom_density(alpha = 0.3, size = 1.2) + # Semi-transparent density curves\n scale_fill_manual(values = c(\"Control\" = \"#E69F00\", \"Vaccine\" = \"#0072B2\")) + # Fill colors\n scale_color_manual(values = c(\"Control\" = \"#E69F00\", \"Vaccine\" = \"#0072B2\")) + # Line colors\n labs(title = \"Density Comparison of Antibody Titers\", \n x = \"Antibody titer (AU)\", y = \"Density\",\n subtitle = \"Smooth curves show the shape of each distribution\") + # Add informative subtitle\n theme_minimal(base_size = 14) + # Clean theme\n theme(legend.title = element_blank()) # Remove legend title\n\n\nWarning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.\nℹ Please use `linewidth` instead.\n\n\n\n\n\n\n\n\n\nCode\n# A violin plot combined with a boxplot and jittered raw data points is even better.\n# It shows the density, summary stats (median, IQR), and individual data points all at once.\nggplot(dat_unpaired, aes(group, titer, fill = group)) + # Map group to x-axis, titer to y-axis\n geom_violin(width = 0.9, alpha = 0.3) + # Show distribution shape\n geom_boxplot(width = 0.2, outlier.shape = NA) + # Add quartiles (hide outliers to avoid duplication)\n geom_jitter(width = 0.08, alpha = 0.6) + # Show individual data points with small random spread\n # We also add a point indicating the mean for each group\n stat_summary(fun = mean, geom = \"point\", size = 3, color = \"black\") + # Add mean as black dot\n labs(title = \"Antibody Titers: Control vs. Vaccine\", x = NULL, y = \"Antibody titer (AU)\") + # Add labels\n theme_minimal(base_size = 14) # Clean theme\n\n\n\n\n\n\n\n\n\nFrom these plots, we can clearly see that:\n\nSide-by-side histograms make it easy to compare the shapes and centers of each distribution\nDensity plots provide smooth curves that clearly show the Vaccine group has higher titers on average\nThe Vaccine group’s distribution is wider (more spread out), confirming our simulation parameters\nBoth visualizations complement each other: histograms show actual counts, density plots show smooth shapes\n\n\n\n\nVisualizations give us a qualitative sense; summary statistics provide the quantitative details for each group.\n\n\nCode\nsummary_unpaired <- dat_unpaired |> # Start with our data\n group_by(group) |> # Group by Control vs Vaccine\n summarise( # Calculate summary statistics for each group\n n = n(), # Sample size\n mean = mean(titer), # Average titer\n median = median(titer), # Middle value (50th percentile)\n sd = sd(titer), # Standard deviation (spread of data)\n iqr = IQR(titer), # Interquartile range (25th to 75th percentile)\n se = sd/sqrt(n), # Standard error of the mean\n ci_low = mean - 1.96*se, # Lower bound of 95% CI\n ci_high = mean + 1.96*se, # Upper bound of 95% CI\n .groups = \"drop\" # Remove grouping for next operations\n )\n\nsummary_unpaired # Display the results table\n\n\n# A tibble: 2 × 9\n group n mean median sd iqr se ci_low ci_high\n <chr> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>\n1 Control 20 212. 209. 78.8 95.3 17.6 177. 246.\n2 Vaccine 20 298. 297. 88.8 102. 19.9 259. 337.\n\n\nThis table gives us precise estimates for the mean, spread (SD), and uncertainty of the mean (SE and 95% CI) for each group.\n\n\n\n\nIn a paired design, we measure the same subject multiple times. This is powerful because it controls for inter-individual variability - some people naturally have higher/lower titers, but we’re interested in the CHANGE within each person. We’ll simulate data where each “mouse” has a baseline titer, and vaccination provides a “boost”.\n\n\nCode\n# Number of subjects (same animals measured twice)\nn <- 20\n\n# Simulate baseline (pre-vaccination) titers for each subject\npre <- rnorm(n, mean = 200, sd = 60) # Baseline antibody levels\n# Simulate the \"boost\" from the vaccine, which also has some variability\ntrue_boost <- rnorm(n, mean = 120, sd = 40) # Individual vaccine response varies\n# The post-vaccination titer is the pre-titer plus the boost\npost <- pre + true_boost # Add boost to baseline for each subject\n\n# Create a tidy tibble, making sure to include a unique ID for each subject\ndat_paired <- tibble(\n id = seq_len(n), # Subject identifier (1, 2, 3, ...)\n pre, # Pre-vaccination titers\n post, # Post-vaccination titers\n # The paired t-test is based on the differences, so it's useful to calculate it\n diff = post - pre # Calculate the change for each subject\n)\n\nhead(dat_paired) # Display first 6 rows\n\n\n# A tibble: 6 × 4\n id pre post diff\n <int> <dbl> <dbl> <dbl>\n1 1 178. 306. 127. \n2 2 245. 389. 143. \n3 3 156. 332. 176. \n4 4 118. 209. 90.9\n5 5 226. 398. 172. \n6 6 151. 285. 133. \n\n\n\n\nFor paired data, we want to see the change within each subject.\n\n\nCode\n# A \"spaghetti plot\" (named for its resemblance to strands of spaghetti) \n# is perfect for paired data. Each line connects measurements from the same subject,\n# letting us see individual responses alongside the overall pattern.\ndat_paired |> # Start with paired data\n pivot_longer(c(pre, post), names_to = \"time\", values_to = \"titer\") |> # Convert to long format for plotting\n mutate(time = factor(time, levels = c(\"pre\", \"post\"))) |> # Ensure proper order: pre first, then post\n ggplot(aes(x = time, y = titer, group = id)) + # Map time to x, titer to y, group by subject ID\n geom_line(alpha = 0.5) + # Draw lines connecting each subject's measurements\n geom_point() + # Add points at each measurement\n labs(title = \"Pre- vs. Post-Vaccination Titers\", x = NULL, y = \"Antibody titer (AU)\") + # Add labels\n theme_minimal(base_size = 14) # Clean theme\n\n\n\n\n\n\n\n\n\nCode\n# Since the paired t-test analyzes the differences, we should inspect their distribution.\n# The differences tell us the \"boost\" each subject got from vaccination.\n# A positive difference means titers increased; negative means decreased.\n\n# Histogram of differences with better binning and reference lines\nggplot(dat_paired, aes(x = diff)) + # Plot the differences\n geom_histogram(bins = 10, fill = \"steelblue\", alpha = 0.7, color = \"black\") + # More bins for better shape\n geom_vline(xintercept = 0, color = \"red\", linetype = \"dashed\", size = 1) + # Reference line at \"no change\"\n geom_vline(xintercept = mean(dat_paired$diff), color = \"darkgreen\", \n linetype = \"solid\", size = 1) + # Line at mean difference\n labs(title = \"Distribution of Titer Differences (Post - Pre)\", \n x = \"Change in Titer (AU)\", y = \"Count\",\n subtitle = \"Red dashed line = no change, Green line = mean change\") + # Informative labels\n theme_minimal(base_size = 14) # Clean theme\n\n\n\n\n\n\n\n\n\nCode\n# Alternative: Density plot with rug plot to show individual values\nggplot(dat_paired, aes(x = diff)) + # Plot the differences\n geom_density(fill = \"steelblue\", alpha = 0.5, color = \"darkblue\", size = 1) + # Smooth density curve\n geom_rug(alpha = 0.6, color = \"darkblue\") + # Show individual data points as tick marks\n geom_vline(xintercept = 0, color = \"red\", linetype = \"dashed\", size = 1) + # Reference line at \"no change\"\n geom_vline(xintercept = mean(dat_paired$diff), color = \"darkgreen\", \n linetype = \"solid\", size = 1) + # Line at mean difference\n labs(title = \"Smooth Distribution of Titer Changes\", \n x = \"Change in Titer (AU)\", y = \"Density\",\n subtitle = \"Tick marks show individual subject changes\") + # Informative labels\n theme_minimal(base_size = 14) # Clean theme\n\n\n\n\n\n\n\n\n\nCode\n# A Q-Q plot helps us check if the differences are normally distributed.\nggplot(dat_paired, aes(sample = diff)) + # Use differences for normality check\n stat_qq() + # Add quantile-quantile points\n stat_qq_line() + # Add reference line for normal distribution\n labs(title = \"Q-Q Plot of Titer Differences\", y = \"Sample Quantiles (diff)\") + # Add labels\n theme_minimal(base_size = 14) # Clean theme\n\n\n\n\n\n\n\n\n\nFrom these visualizations, we can see that:\n\nSpaghetti plot: Shows an increase for nearly every subject, with clear individual trajectories\nHistogram with reference lines: The differences are centered around ~120 AU (green line), well above zero (red dashed line)\nDensity plot with rug: Provides a smooth view of the distribution shape, with tick marks showing individual changes\nQ-Q plot: Confirms the differences are roughly normally distributed (appropriate for paired t-test)\n\nThe multiple visualization approaches give us confidence that vaccination consistently increases antibody titers across subjects.\n\n\n\nThe key question in a paired analysis is about the mean difference. Is it significantly different from zero?\n\n\nCode\nsummary_paired <- dat_paired |> # Start with paired data\n summarise( # Calculate summary statistics for differences\n n = n(), # Number of subject pairs\n mean_diff = mean(diff), # Average change (boost) across all subjects\n median_diff = median(diff), # Middle value of changes\n sd_diff = sd(diff), # Standard deviation of changes\n se_diff = sd_diff/sqrt(n), # Standard error of the mean difference\n ci_low = mean_diff - 1.96*se_diff, # Lower bound of 95% CI for mean difference\n ci_high = mean_diff + 1.96*se_diff # Upper bound of 95% CI for mean difference\n )\nsummary_paired # Display the results\n\n\n# A tibble: 1 × 7\n n mean_diff median_diff sd_diff se_diff ci_low ci_high\n <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>\n1 20 130. 141. 34.3 7.66 115. 145.\n\n\nThe summary shows an average increase (mean_diff) of about 121.5 AU, and the 95% CI for this mean difference does not include zero, giving us a strong indication that the increase is statistically significant.",
"crumbs": [
"In-Class Materials",
"Session 4",
"Session 4: Simulating Immunology Data"
]
},
{
"objectID": "session-materials/session4/immunology-simulation.html#unpaired-design-control-vs.-vaccine",
"href": "session-materials/session4/immunology-simulation.html#unpaired-design-control-vs.-vaccine",
"title": "Session 4: Simulating Immunology Data",
"section": "",
"text": "Here, we’ll simulate data for two separate groups of mice. We expect the vaccinated group to have a higher average antibody titer. We will also make their titers slightly more variable.\n\n\nCode\n# Realistic scenario: We're measuring IgG antibodies against influenza \n# after seasonal flu vaccination. Typical baseline titers range 50-400 AU,\n# with 2-4 fold increases expected post-vaccination.\n\n# Define the parameters for our simulation\nn_control <- 20 # Number of control subjects\nn_vax <- 20 # Number of vaccinated subjects\n\n# Generate random data from a normal distribution with rnorm()\n# Arguments: n = sample size, mean = center of distribution, sd = spread\ncontrol_titer <- rnorm(n_control, mean = 200, sd = 60) # Control group titers\nvax_titer <- rnorm(n_vax, mean = 320, sd = 80) # Vaccine group: higher mean, larger variance\n\n# Combine the data into a single, tidy data frame (tibble)\ndat_unpaired <- tibble(\n group = rep(c(\"Control\",\"Vaccine\"), c(n_control, n_vax)), # Create group labels\n titer = c(control_titer, vax_titer) # Combine all titer values\n)\n\n# A quick peek at the first few rows\nhead(dat_unpaired) # Display first 6 rows of the data\n\n\n# A tibble: 6 × 2\n group titer\n <chr> <dbl>\n1 Control 282.\n2 Control 166.\n3 Control 222.\n4 Control 238.\n5 Control 224.\n6 Control 194.\n\n\n\n\nBefore any formal testing, it’s crucial to explore the data visually. This helps us check for outliers, see the data’s distribution, and get a feel for the differences between groups.\n\n\nCode\n# Option 1: Side-by-side histograms for clearer comparison\nggplot(dat_unpaired, aes(x = titer, fill = group)) + # Map titer to x-axis, group to fill color\n geom_histogram(bins = 12, alpha = 0.8, color = \"black\") + # Solid histograms with black borders\n facet_wrap(~group, ncol = 2) + # Separate panels for each group\n scale_fill_manual(values = c(\"Control\" = \"#E69F00\", \"Vaccine\" = \"#0072B2\")) + # Colorblind-friendly colors\n labs(title = \"Distribution of Antibody Titers by Group\", \n x = \"Antibody titer (AU)\", y = \"Count\") + # Add labels\n theme_minimal(base_size = 14) + # Clean theme with larger text\n theme(legend.position = \"none\") # Remove redundant legend\n\n\n\n\n\n\n\n\n\nCode\n# Option 2: Density plots for smooth comparison (better for overlapping)\nggplot(dat_unpaired, aes(x = titer, fill = group, color = group)) + # Map aesthetics\n geom_density(alpha = 0.3, size = 1.2) + # Semi-transparent density curves\n scale_fill_manual(values = c(\"Control\" = \"#E69F00\", \"Vaccine\" = \"#0072B2\")) + # Fill colors\n scale_color_manual(values = c(\"Control\" = \"#E69F00\", \"Vaccine\" = \"#0072B2\")) + # Line colors\n labs(title = \"Density Comparison of Antibody Titers\", \n x = \"Antibody titer (AU)\", y = \"Density\",\n subtitle = \"Smooth curves show the shape of each distribution\") + # Add informative subtitle\n theme_minimal(base_size = 14) + # Clean theme\n theme(legend.title = element_blank()) # Remove legend title\n\n\nWarning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.\nℹ Please use `linewidth` instead.\n\n\n\n\n\n\n\n\n\nCode\n# A violin plot combined with a boxplot and jittered raw data points is even better.\n# It shows the density, summary stats (median, IQR), and individual data points all at once.\nggplot(dat_unpaired, aes(group, titer, fill = group)) + # Map group to x-axis, titer to y-axis\n geom_violin(width = 0.9, alpha = 0.3) + # Show distribution shape\n geom_boxplot(width = 0.2, outlier.shape = NA) + # Add quartiles (hide outliers to avoid duplication)\n geom_jitter(width = 0.08, alpha = 0.6) + # Show individual data points with small random spread\n # We also add a point indicating the mean for each group\n stat_summary(fun = mean, geom = \"point\", size = 3, color = \"black\") + # Add mean as black dot\n labs(title = \"Antibody Titers: Control vs. Vaccine\", x = NULL, y = \"Antibody titer (AU)\") + # Add labels\n theme_minimal(base_size = 14) # Clean theme\n\n\n\n\n\n\n\n\n\nFrom these plots, we can clearly see that:\n\nSide-by-side histograms make it easy to compare the shapes and centers of each distribution\nDensity plots provide smooth curves that clearly show the Vaccine group has higher titers on average\nThe Vaccine group’s distribution is wider (more spread out), confirming our simulation parameters\nBoth visualizations complement each other: histograms show actual counts, density plots show smooth shapes\n\n\n\n\nVisualizations give us a qualitative sense; summary statistics provide the quantitative details for each group.\n\n\nCode\nsummary_unpaired <- dat_unpaired |> # Start with our data\n group_by(group) |> # Group by Control vs Vaccine\n summarise( # Calculate summary statistics for each group\n n = n(), # Sample size\n mean = mean(titer), # Average titer\n median = median(titer), # Middle value (50th percentile)\n sd = sd(titer), # Standard deviation (spread of data)\n iqr = IQR(titer), # Interquartile range (25th to 75th percentile)\n se = sd/sqrt(n), # Standard error of the mean\n ci_low = mean - 1.96*se, # Lower bound of 95% CI\n ci_high = mean + 1.96*se, # Upper bound of 95% CI\n .groups = \"drop\" # Remove grouping for next operations\n )\n\nsummary_unpaired # Display the results table\n\n\n# A tibble: 2 × 9\n group n mean median sd iqr se ci_low ci_high\n <chr> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>\n1 Control 20 212. 209. 78.8 95.3 17.6 177. 246.\n2 Vaccine 20 298. 297. 88.8 102. 19.9 259. 337.\n\n\nThis table gives us precise estimates for the mean, spread (SD), and uncertainty of the mean (SE and 95% CI) for each group.",
"crumbs": [
"In-Class Materials",
"Session 4",
"Session 4: Simulating Immunology Data"
]
},
{
"objectID": "session-materials/session4/immunology-simulation.html#paired-design-pre--vs.-post-vaccination",
"href": "session-materials/session4/immunology-simulation.html#paired-design-pre--vs.-post-vaccination",
"title": "Session 4: Simulating Immunology Data",
"section": "",
"text": "In a paired design, we measure the same subject multiple times. This is powerful because it controls for inter-individual variability - some people naturally have higher/lower titers, but we’re interested in the CHANGE within each person. We’ll simulate data where each “mouse” has a baseline titer, and vaccination provides a “boost”.\n\n\nCode\n# Number of subjects (same animals measured twice)\nn <- 20\n\n# Simulate baseline (pre-vaccination) titers for each subject\npre <- rnorm(n, mean = 200, sd = 60) # Baseline antibody levels\n# Simulate the \"boost\" from the vaccine, which also has some variability\ntrue_boost <- rnorm(n, mean = 120, sd = 40) # Individual vaccine response varies\n# The post-vaccination titer is the pre-titer plus the boost\npost <- pre + true_boost # Add boost to baseline for each subject\n\n# Create a tidy tibble, making sure to include a unique ID for each subject\ndat_paired <- tibble(\n id = seq_len(n), # Subject identifier (1, 2, 3, ...)\n pre, # Pre-vaccination titers\n post, # Post-vaccination titers\n # The paired t-test is based on the differences, so it's useful to calculate it\n diff = post - pre # Calculate the change for each subject\n)\n\nhead(dat_paired) # Display first 6 rows\n\n\n# A tibble: 6 × 4\n id pre post diff\n <int> <dbl> <dbl> <dbl>\n1 1 178. 306. 127. \n2 2 245. 389. 143. \n3 3 156. 332. 176. \n4 4 118. 209. 90.9\n5 5 226. 398. 172. \n6 6 151. 285. 133. \n\n\n\n\nFor paired data, we want to see the change within each subject.\n\n\nCode\n# A \"spaghetti plot\" (named for its resemblance to strands of spaghetti) \n# is perfect for paired data. Each line connects measurements from the same subject,\n# letting us see individual responses alongside the overall pattern.\ndat_paired |> # Start with paired data\n pivot_longer(c(pre, post), names_to = \"time\", values_to = \"titer\") |> # Convert to long format for plotting\n mutate(time = factor(time, levels = c(\"pre\", \"post\"))) |> # Ensure proper order: pre first, then post\n ggplot(aes(x = time, y = titer, group = id)) + # Map time to x, titer to y, group by subject ID\n geom_line(alpha = 0.5) + # Draw lines connecting each subject's measurements\n geom_point() + # Add points at each measurement\n labs(title = \"Pre- vs. Post-Vaccination Titers\", x = NULL, y = \"Antibody titer (AU)\") + # Add labels\n theme_minimal(base_size = 14) # Clean theme\n\n\n\n\n\n\n\n\n\nCode\n# Since the paired t-test analyzes the differences, we should inspect their distribution.\n# The differences tell us the \"boost\" each subject got from vaccination.\n# A positive difference means titers increased; negative means decreased.\n\n# Histogram of differences with better binning and reference lines\nggplot(dat_paired, aes(x = diff)) + # Plot the differences\n geom_histogram(bins = 10, fill = \"steelblue\", alpha = 0.7, color = \"black\") + # More bins for better shape\n geom_vline(xintercept = 0, color = \"red\", linetype = \"dashed\", size = 1) + # Reference line at \"no change\"\n geom_vline(xintercept = mean(dat_paired$diff), color = \"darkgreen\", \n linetype = \"solid\", size = 1) + # Line at mean difference\n labs(title = \"Distribution of Titer Differences (Post - Pre)\", \n x = \"Change in Titer (AU)\", y = \"Count\",\n subtitle = \"Red dashed line = no change, Green line = mean change\") + # Informative labels\n theme_minimal(base_size = 14) # Clean theme\n\n\n\n\n\n\n\n\n\nCode\n# Alternative: Density plot with rug plot to show individual values\nggplot(dat_paired, aes(x = diff)) + # Plot the differences\n geom_density(fill = \"steelblue\", alpha = 0.5, color = \"darkblue\", size = 1) + # Smooth density curve\n geom_rug(alpha = 0.6, color = \"darkblue\") + # Show individual data points as tick marks\n geom_vline(xintercept = 0, color = \"red\", linetype = \"dashed\", size = 1) + # Reference line at \"no change\"\n geom_vline(xintercept = mean(dat_paired$diff), color = \"darkgreen\", \n linetype = \"solid\", size = 1) + # Line at mean difference\n labs(title = \"Smooth Distribution of Titer Changes\", \n x = \"Change in Titer (AU)\", y = \"Density\",\n subtitle = \"Tick marks show individual subject changes\") + # Informative labels\n theme_minimal(base_size = 14) # Clean theme\n\n\n\n\n\n\n\n\n\nCode\n# A Q-Q plot helps us check if the differences are normally distributed.\nggplot(dat_paired, aes(sample = diff)) + # Use differences for normality check\n stat_qq() + # Add quantile-quantile points\n stat_qq_line() + # Add reference line for normal distribution\n labs(title = \"Q-Q Plot of Titer Differences\", y = \"Sample Quantiles (diff)\") + # Add labels\n theme_minimal(base_size = 14) # Clean theme\n\n\n\n\n\n\n\n\n\nFrom these visualizations, we can see that:\n\nSpaghetti plot: Shows an increase for nearly every subject, with clear individual trajectories\nHistogram with reference lines: The differences are centered around ~120 AU (green line), well above zero (red dashed line)\nDensity plot with rug: Provides a smooth view of the distribution shape, with tick marks showing individual changes\nQ-Q plot: Confirms the differences are roughly normally distributed (appropriate for paired t-test)\n\nThe multiple visualization approaches give us confidence that vaccination consistently increases antibody titers across subjects.\n\n\n\nThe key question in a paired analysis is about the mean difference. Is it significantly different from zero?\n\n\nCode\nsummary_paired <- dat_paired |> # Start with paired data\n summarise( # Calculate summary statistics for differences\n n = n(), # Number of subject pairs\n mean_diff = mean(diff), # Average change (boost) across all subjects\n median_diff = median(diff), # Middle value of changes\n sd_diff = sd(diff), # Standard deviation of changes\n se_diff = sd_diff/sqrt(n), # Standard error of the mean difference\n ci_low = mean_diff - 1.96*se_diff, # Lower bound of 95% CI for mean difference\n ci_high = mean_diff + 1.96*se_diff # Upper bound of 95% CI for mean difference\n )\nsummary_paired # Display the results\n\n\n# A tibble: 1 × 7\n n mean_diff median_diff sd_diff se_diff ci_low ci_high\n <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>\n1 20 130. 141. 34.3 7.66 115. 145.\n\n\nThe summary shows an average increase (mean_diff) of about 121.5 AU, and the 95% CI for this mean difference does not include zero, giving us a strong indication that the increase is statistically significant.",
"crumbs": [
"In-Class Materials",
"Session 4",
"Session 4: Simulating Immunology Data"
]
},
{
"objectID": "session-materials/session4/A-simple-model.html",
"href": "session-materials/session4/A-simple-model.html",
"title": "A simple model",
"section": "",
"text": "OTE: Following chapter is taken from “R for Data Science” book (Version 1) which is licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0. Please refer to this book or its updated second version from here: https://r4ds.hadley.nz/",
"crumbs": [
"In-Class Materials",
"Session 4",
"A simple model"
]
},
{
"objectID": "session-materials/session4/A-simple-model.html#a-simple-model",
"href": "session-materials/session4/A-simple-model.html#a-simple-model",
"title": "A simple model",
"section": "A simple model",
"text": "A simple model\nLets take a look at the simulated dataset sim1, included with the modelr package. It contains two continuous variables, x and y. Let’s plot them to see how they’re related:\n\nlibrary(ggplot2)\n\nlibrary(modelr)\n\nlibrary(tidyverse)\n\n── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──\n✔ dplyr 1.1.4 ✔ readr 2.1.5\n✔ forcats 1.0.0 ✔ stringr 1.5.1\n✔ lubridate 1.9.4 ✔ tibble 3.2.1\n✔ purrr 1.0.4 ✔ tidyr 1.3.1\n── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──\n✖ dplyr::filter() masks stats::filter()\n✖ dplyr::lag() masks stats::lag()\nℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors\n\nggplot(sim1, aes(x, y)) + \n geom_point()\n\n\n\n\n\n\n\n\nYou can see a strong pattern in the data. Let’s use a model to capture that pattern and make it explicit. It’s our job to supply the basic form of the model. In this case, the relationship looks linear, i.e. y = a_0 + a_1 * x. Let’s start by getting a feel for what models from that family look like by randomly generating a few and overlaying them on the data. For this simple case, we can use geom_abline() which takes a slope and intercept as parameters. Later on we’ll learn more general techniques that work with any model.\n\nmodels <- tibble(\n a1 = runif(250, -20, 40),\n a2 = runif(250, -5, 5)\n)\n\nggplot(sim1, aes(x, y)) + \n geom_abline(aes(intercept = a1, slope = a2), data = models, alpha = 1/4) +\n geom_point() \n\n\n\n\n\n\n\n\nThere are 250 models on this plot, but a lot are really bad! We need to find the good models by making precise our intuition that a good model is “close” to the data. We need a way to quantify the distance between the data and a model. Then we can fit the model by finding the value of a_0 and a_1 that generate the model with the smallest distance from this data.\nOne easy place to start is to find the vertical distance between each point and the model, as in the following diagram. (Note that I’ve shifted the x values slightly so you can see the individual distances.)\n\nThis distance is just the difference between the y value given by the model (the prediction), and the actual y value in the data (the response).\nTo compute this distance, we first turn our model family into an R function. This takes the model parameters and the data as inputs, and gives values predicted by the model as output:\n\nmodel1 <- function(a, data) {\n a[1] + data$x * a[2]\n}\nmodel1(c(7, 1.5), sim1)\n\n [1] 8.5 8.5 8.5 10.0 10.0 10.0 11.5 11.5 11.5 13.0 13.0 13.0 14.5 14.5 14.5\n[16] 16.0 16.0 16.0 17.5 17.5 17.5 19.0 19.0 19.0 20.5 20.5 20.5 22.0 22.0 22.0\n\n\nNext, we need some way to compute an overall distance between the predicted and actual values. In other words, the plot above shows 30 distances: how do we collapse that into a single number?\nOne common way to do this in statistics to use the “root-mean-squared deviation”. We compute the difference between actual and predicted, square them, average them, and the take the square root. This distance has lots of appealing mathematical properties, which we’re not going to talk about here. You’ll just have to take my word for it!\n\nmeasure_distance <- function(mod, data) {\n diff <- data$y - model1(mod, data)\n sqrt(mean(diff ^ 2))\n}\nmeasure_distance(c(7, 1.5), sim1)\n\n[1] 2.665212\n\n\nNow we can use purrr to compute the distance for all the models defined above. We need a helper function because our distance function expects the model as a numeric vector of length 2.\n\nsim1_dist <- function(a1, a2) {\n measure_distance(c(a1, a2), sim1)\n}\n\nmodels <- models %>% \n mutate(dist = purrr::map2_dbl(a1, a2, sim1_dist))\n\nmodels\n\n# A tibble: 250 × 3\n a1 a2 dist\n <dbl> <dbl> <dbl>\n 1 14.2 3.55 18.8 \n 2 3.87 2.24 2.29\n 3 31.5 -0.206 16.4 \n 4 6.09 4.76 18.6 \n 5 12.9 4.24 21.8 \n 6 -13.7 -1.66 39.9 \n 7 17.9 -4.49 29.2 \n 8 39.7 0.660 28.2 \n 9 33.2 -4.75 21.4 \n10 -9.77 -1.37 34.3 \n# ℹ 240 more rows\n\n\nNext, let’s overlay the 10 best models on to the data. I’ve coloured the models by -dist: this is an easy way to make sure that the best models (i.e. the ones with the smallest distance) get the brighest colours.\nggplot(sim1, aes(x, y)) + \n geom_point(size = 2, colour = \"grey30\") + \n geom_abline(\n aes(intercept = a1, slope = a2, colour = -dist), \n data = filter(models, rank(dist) <= 10)\n )\nWe can also think about these models as observations, and visualising with a scatterplot of a1 vs a2, again coloured by -dist. We can no longer directly see how the model compares to the data, but we can see many models at once. Again, I’ve highlighted the 10 best models, this time by drawing red circles underneath them.\n\nggplot(models, aes(a1, a2)) +\n geom_point(data = filter(models, rank(dist) <= 10), size = 4, colour = \"red\") +\n geom_point(aes(colour = -dist))\n\n\n\n\n\n\n\n\nInstead of trying lots of random models, we could be more systematic and generate an evenly spaced grid of points (this is called a grid search). I picked the parameters of the grid roughly by looking at where the best models were in the plot above.\n\ngrid <- expand.grid(\n a1 = seq(-5, 20, length = 25),\n a2 = seq(1, 3, length = 25)\n ) %>% \n mutate(dist = purrr::map2_dbl(a1, a2, sim1_dist))\n\ngrid %>% \n ggplot(aes(a1, a2)) +\n geom_point(data = filter(grid, rank(dist) <= 10), size = 4, colour = \"red\") +\n geom_point(aes(colour = -dist)) \n\n\n\n\n\n\n\n\nWhen you overlay the best 10 models back on the original data, they all look pretty good:\n\nggplot(sim1, aes(x, y)) + \n geom_point(size = 2, colour = \"grey30\") + \n geom_abline(\n aes(intercept = a1, slope = a2, colour = -dist), \n data = filter(grid, rank(dist) <= 10)\n )\n\n\n\n\n\n\n\n\nYou could imagine iteratively making the grid finer and finer until you narrowed in on the best model. But there’s a better way to tackle that problem: a numerical minimisation tool called Newton-Raphson search. The intuition of Newton-Raphson is pretty simple: you pick a starting point and look around for the steepest slope. You then ski down that slope a little way, and then repeat again and again, until you can’t go any lower. In R, we can do that with optim():\n\nbest <- optim(c(0, 0), measure_distance, data = sim1)\nbest$par\n\n[1] 4.222248 2.051204\n\nggplot(sim1, aes(x, y)) + \n geom_point(size = 2, colour = \"grey30\") + \n geom_abline(intercept = best$par[1], slope = best$par[2])\n\n\n\n\n\n\n\n\nDon’t worry too much about the details of how optim() works. It’s the intuition that’s important here. If you have a function that defines the distance between a model and a dataset, an algorithm that can minimise that distance by modifying the parameters of the model, you can find the best model. The neat thing about this approach is that it will work for any family of models that you can write an equation for.\nThere’s one more approach that we can use for this model, because it’s a special case of a broader family: linear models. A linear model has the general form y = a_1 + a_2 * x_1 + a_3 * x_2 + ... + a_n * x_(n - 1). So this simple model is equivalent to a general linear model where n is 2 and x_1 is x. R has a tool specifically designed for fitting linear models called lm(). lm() has a special way to specify the model family: formulas. Formulas look like y ~ x, which lm() will translate to a function like y = a_1 + a_2 * x. We can fit the model and look at the output:\n\nsim1_mod <- lm(y ~ x, data = sim1)\ncoef(sim1_mod)\n\n(Intercept) x \n 4.220822 2.051533 \n\n\nThese are exactly the same values we got with optim()! Behind the scenes lm() doesn’t use optim() but instead takes advantage of the mathematical structure of linear models. Using some connections between geometry, calculus, and linear algebra, lm() actually finds the closest model in a single step, using a sophisticated algorithm. This approach is both faster, and guarantees that there is a global minimum.",
"crumbs": [
"In-Class Materials",
"Session 4",
"A simple model"
]
},
{
"objectID": "session-materials/session3/Exercise-2.html",
"href": "session-materials/session3/Exercise-2.html",
"title": "Exercise-2: Boxplot",
"section": "",
"text": "Exercise 2: Boxplots\nA boxplot provides a graphical view of the distribution of data based on a five number summary:\n\nThe top and bottom of the box represent the (1) first and (2) third quartiles (25th and 75th percentiles, respectively).\n\n\n\nThe line inside the box represents the (3) median (50th percentile).\nThe whiskers extending above and below the box represent the (4) maximum, and (5) minimum of a data set. The whiskers of the plot reach the minimum and maximum values that are not outliers.\n\n\nIn this case, outliers are determined using the interquartile range (IQR), which is defined as: Q3 - Q1. Any values that exceeds 1.5 x IQR below Q1 or above Q3 are considered outliers and are represented as points above or below the whiskers.\n\n\nBoxplot\n\nGenerate a boxplot using the data in the new_metadata dataframe. Create a ggplot2 code chunk with the following instructions:\n1. Use the geom_boxplot() layer to plot the differences in sample means between the Wt and KO genotypes.\n2. Use the fill aesthetic to look at differences in sample means between the celltypes within each genotype.\n3. Add a title to your plot.\n4. Add labels, ‘Genotype’ for the x-axis and ‘Mean expression’ for the y-axis.\n5. Make the following theme() changes:\n\nUse the theme_bw() function to make the background white.\nChange the size of your axes labels to 1.25x larger than the default.\nChange the size of your plot title to 1.5x larger than default.\nCenter the plot title.\n\n\n\n\n\n\n\nSolution\n\n\n\n\nlibrary(ggplot2)\nload(\"data/new_metadata.RData\")\n\nggplot(new_metadata) +\n geom_boxplot(aes(x = genotype, y = samplemeans, fill = celltype)) +\n ggtitle(\"Genotype differences in average gene expression\") +\n xlab(\"Genotype\") +\n ylab(\"Mean expression\") +\n theme_bw() +\n theme(axis.title = element_text(size = rel(1.25))) +\n theme(plot.title=element_text(hjust = 0.5, size = rel(1.5)))\n\n\n\n\n\n\n\n\n\n\n2. Changing the order of genotype on the Boxplot\nLet’s say you wanted to have the “Wt” boxplots displayed first on the left side, and “KO” on the right. How might you go about doing this?\nTo do this, your first question should be - How does ggplot2 determine what to place where on the X-axis? The order of the genotype on the X axis is in alphabetical order. To change it, you need to make sure that the genotype column is a factor. And, the factor levels for that column are in the order you want on the X-axis.\n1. Factor the new_metadata$genotype column without creating any extra variables/objects and change the levels to c(\"Wt\", \"KO\")\n2. Re-run the boxplot code chunk you created for the “Boxplot” exercise above.\n3. Changing default colors\n\n\n\n\n\n\n\nnew_metadata$genotype <- factor(new_metadata$genotype, levels=c(\"Wt\",\"KO\"))\nggplot(new_metadata) +\n geom_boxplot(aes(x = genotype, y = samplemeans, fill = celltype)) +\n ggtitle(\"Genotype differences in average gene expression\") +\n xlab(\"Genotype\") +\n ylab(\"Mean expression\") +\n theme_bw() +\n theme(axis.title = element_text(size = rel(1.25))) +\n theme(plot.title=element_text(hjust = 0.5, size = rel(1.5)))\n\n\n\n\n\n\n\n\n\n\n\nYou can color the boxplot differently by using some specific layers:\n1. Add a new layer scale_color_manual(values=c(\"purple\",\"orange\")).\n\nDo you observe a change?\n\n2. Replace scale_color_manual(values=c(\"purple\",\"orange\")) with scale_fill_manual(values=c(\"purple\",\"orange\")).\n\nDo you observe a change?\nIn the scatterplot we drew in class, add a new layer scale_color_manual(values=c(\"purple\",\"orange\")), do you observe a difference?\nWhat do you think is the difference between scale_color_manual() and scale_fill_manual()?\n\n3. Back in your boxplot code, change the colors in the scale_fill_manual() layer to be your 2 favorite colors.\n\nAre there any colors that you tried that did not work?\n\n\n\n\n\n\n\nSolution\n\n\n\n\nggplot(new_metadata) +\n geom_boxplot(aes(x = genotype, y = samplemeans, fill = celltype)) +\n ggtitle(\"Genotype differences in average gene expression\") +\n xlab(\"Genotype\") +\n ylab(\"Mean expression\") +\n theme_bw() +\n theme(axis.title = element_text(size = rel(1.25))) +\n theme(plot.title=element_text(hjust = 0.5, size = rel(1.5))) +\n #We can also use hex color values to choose colors\n scale_fill_manual(values=c(\"#F67280\", \"#355C7D\"))\n\n\n\n\n\n\n\n\n\n\nThe materials in this lesson have been adapted from work created by the (HBC)\\](http://bioinformatics.sph.harvard.edu/) and Data Carpentry (http://datacarpentry.org/). These are open access materials distributed under the terms of the [Creative Commons Attribution license](https://creativecommons.org/licenses/by/4.0/) (CC BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.",
"crumbs": [
"In-Class Materials",
"Session 3",
"Exercise-2: Boxplot"
]
},
{
"objectID": "session-materials/session2/tidyverse-basics.html",
"href": "session-materials/session2/tidyverse-basics.html",
"title": "Intro to Tidyverse",
"section": "",
"text": "Bracket subsetting is handy, but it can be cumbersome and difficult to read, especially for complicated operations.\nSome packages can greatly facilitate our task when we manipulate data. Packages in R are basically sets of additional functions that let you do more stuff. The functions we’ve been using so far, like str() or data.frame(), come built into R; Loading packages can give you access to other specific functions. Before you use a package for the first time you need to install it on your machine, and then you should import it in every subsequent R session when you need it.\n\nThe package dplyr provides powerful tools for data manipulation tasks. It is built to work directly with data frames, with many manipulation tasks optimised.\nAs we will see latter on, sometimes we want a data frame to be reshaped to be able to do some specific analyses or for visualisation. The package tidyr addresses this common problem of reshaping data and provides tools for manipulating data in a tidy way.\n\nTo learn more about dplyr and tidyr after the workshop, you may want to check out this handy data transformation with dplyr cheatsheet and this one about tidyr.\n\nThe tidyverse package is an “umbrella-package” that installs several useful packages for data analysis which work well together, such as tidyr, dplyr, ggplot2, tibble, etc. These packages help us to work and interact with the data. They allow us to do many things with your data, such as subsetting, transforming, visualising, etc.\n\nIf you did the set up, you should have already installed the tidyverse package. Check to see if you have it by trying to load in from the library:\n\n## load the tidyverse packages, incl. dplyr\nlibrary(\"tidyverse\")\n\nIf you got an error message there is no package called ‘tidyverse’ then you have not installed the package yet for this version of R. To install the tidyverse package type:\n\nBiocManager::install(\"tidyverse\")\n\nIf you had to install the tidyverse package, do not forget to load it in this R session by using the library() command above!",
"crumbs": [
"In-Class Materials",
"Session 2",
"Intro to Tidyverse"
]
},
{
"objectID": "session-materials/session2/tidyverse-basics.html#data-manipulation-using-dplyr-and-tidyr",
"href": "session-materials/session2/tidyverse-basics.html#data-manipulation-using-dplyr-and-tidyr",
"title": "Intro to Tidyverse",
"section": "",
"text": "Bracket subsetting is handy, but it can be cumbersome and difficult to read, especially for complicated operations.\nSome packages can greatly facilitate our task when we manipulate data. Packages in R are basically sets of additional functions that let you do more stuff. The functions we’ve been using so far, like str() or data.frame(), come built into R; Loading packages can give you access to other specific functions. Before you use a package for the first time you need to install it on your machine, and then you should import it in every subsequent R session when you need it.\n\nThe package dplyr provides powerful tools for data manipulation tasks. It is built to work directly with data frames, with many manipulation tasks optimised.\nAs we will see latter on, sometimes we want a data frame to be reshaped to be able to do some specific analyses or for visualisation. The package tidyr addresses this common problem of reshaping data and provides tools for manipulating data in a tidy way.\n\nTo learn more about dplyr and tidyr after the workshop, you may want to check out this handy data transformation with dplyr cheatsheet and this one about tidyr.\n\nThe tidyverse package is an “umbrella-package” that installs several useful packages for data analysis which work well together, such as tidyr, dplyr, ggplot2, tibble, etc. These packages help us to work and interact with the data. They allow us to do many things with your data, such as subsetting, transforming, visualising, etc.\n\nIf you did the set up, you should have already installed the tidyverse package. Check to see if you have it by trying to load in from the library:\n\n## load the tidyverse packages, incl. dplyr\nlibrary(\"tidyverse\")\n\nIf you got an error message there is no package called ‘tidyverse’ then you have not installed the package yet for this version of R. To install the tidyverse package type:\n\nBiocManager::install(\"tidyverse\")\n\nIf you had to install the tidyverse package, do not forget to load it in this R session by using the library() command above!",
"crumbs": [
"In-Class Materials",
"Session 2",
"Intro to Tidyverse"
]
},
{
"objectID": "session-materials/session2/tidyverse-basics.html#loading-data-with-tidyverse",
"href": "session-materials/session2/tidyverse-basics.html#loading-data-with-tidyverse",
"title": "Intro to Tidyverse",
"section": "Loading data with tidyverse",
"text": "Loading data with tidyverse\nInstead of read.csv(), we will read in our data using the read_csv() function (notice the _ instead of the .), from the tidyverse package readr.\n\nrna <- read_csv(\"data/rnaseq.csv\")\n\n## view the data\nrna\n\n# A tibble: 32,428 × 19\n gene sample expression organism age sex infection strain time tissue\n <chr> <chr> <dbl> <chr> <dbl> <chr> <chr> <chr> <dbl> <chr> \n 1 Asl GSM254… 1170 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 2 Apod GSM254… 36194 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 3 Cyp2d22 GSM254… 4060 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 4 Klk6 GSM254… 287 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 5 Fcrls GSM254… 85 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 6 Slc2a4 GSM254… 782 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 7 Exd2 GSM254… 1619 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 8 Gjc2 GSM254… 288 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 9 Plp1 GSM254… 43217 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n10 Gnb4 GSM254… 1071 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n# ℹ 32,418 more rows\n# ℹ 9 more variables: mouse <dbl>, ENTREZID <dbl>, product <chr>,\n# ensembl_gene_id <chr>, external_synonym <chr>, chromosome_name <chr>,\n# gene_biotype <chr>, phenotype_description <chr>,\n# hsapiens_homolog_associated_gene_name <chr>\n\n\nNotice that the class of the data is now referred to as a “tibble”.\nTibbles tweak some of the behaviors of the data frame objects we introduced in the previously. The data structure is very similar to a data frame. For our purposes the only differences are that:\n\nIt displays the data type of each column under its name. Note that <dbl> is a data type defined to hold numeric values with decimal points.\nIt only prints the first few rows of data and only as many columns as fit on one screen.\n\nWe are now going to learn some of the most common dplyr functions:\n\nselect(): subset columns\nfilter(): subset rows on conditions\nmutate(): create new columns by using information from other columns\ngroup_by() and summarise(): create summary statistics on grouped data\narrange(): sort results\ncount(): count discrete values",
"crumbs": [
"In-Class Materials",
"Session 2",
"Intro to Tidyverse"
]
},
{
"objectID": "session-materials/session2/tidyverse-basics.html#selecting-columns-and-filtering-rows",
"href": "session-materials/session2/tidyverse-basics.html#selecting-columns-and-filtering-rows",
"title": "Intro to Tidyverse",
"section": "Selecting columns and filtering rows",
"text": "Selecting columns and filtering rows\nTo select columns of a data frame, use select(). The first argument to this function is the data frame (rna), and the subsequent arguments are the columns to keep.\n\nselect(rna, gene, sample, tissue, expression)\n\n# A tibble: 32,428 × 4\n gene sample tissue expression\n <chr> <chr> <chr> <dbl>\n 1 Asl GSM2545336 Cerebellum 1170\n 2 Apod GSM2545336 Cerebellum 36194\n 3 Cyp2d22 GSM2545336 Cerebellum 4060\n 4 Klk6 GSM2545336 Cerebellum 287\n 5 Fcrls GSM2545336 Cerebellum 85\n 6 Slc2a4 GSM2545336 Cerebellum 782\n 7 Exd2 GSM2545336 Cerebellum 1619\n 8 Gjc2 GSM2545336 Cerebellum 288\n 9 Plp1 GSM2545336 Cerebellum 43217\n10 Gnb4 GSM2545336 Cerebellum 1071\n# ℹ 32,418 more rows\n\n\nTo select all columns except certain ones, put a “-” in front of the variable to exclude it.\n\nselect(rna, -tissue, -organism)\n\n# A tibble: 32,428 × 17\n gene sample expression age sex infection strain time mouse ENTREZID\n <chr> <chr> <dbl> <dbl> <chr> <chr> <chr> <dbl> <dbl> <dbl>\n 1 Asl GSM2545… 1170 8 Fema… Influenz… C57BL… 8 14 109900\n 2 Apod GSM2545… 36194 8 Fema… Influenz… C57BL… 8 14 11815\n 3 Cyp2d22 GSM2545… 4060 8 Fema… Influenz… C57BL… 8 14 56448\n 4 Klk6 GSM2545… 287 8 Fema… Influenz… C57BL… 8 14 19144\n 5 Fcrls GSM2545… 85 8 Fema… Influenz… C57BL… 8 14 80891\n 6 Slc2a4 GSM2545… 782 8 Fema… Influenz… C57BL… 8 14 20528\n 7 Exd2 GSM2545… 1619 8 Fema… Influenz… C57BL… 8 14 97827\n 8 Gjc2 GSM2545… 288 8 Fema… Influenz… C57BL… 8 14 118454\n 9 Plp1 GSM2545… 43217 8 Fema… Influenz… C57BL… 8 14 18823\n10 Gnb4 GSM2545… 1071 8 Fema… Influenz… C57BL… 8 14 14696\n# ℹ 32,418 more rows\n# ℹ 7 more variables: product <chr>, ensembl_gene_id <chr>,\n# external_synonym <chr>, chromosome_name <chr>, gene_biotype <chr>,\n# phenotype_description <chr>, hsapiens_homolog_associated_gene_name <chr>\n\n\nThis will select all the variables in rna except tissue and organism.\nTo choose rows based on a specific criteria, use filter():\n\nfilter(rna, sex == \"Male\")\n\n# A tibble: 14,740 × 19\n gene sample expression organism age sex infection strain time tissue\n <chr> <chr> <dbl> <chr> <dbl> <chr> <chr> <chr> <dbl> <chr> \n 1 Asl GSM254… 626 Mus mus… 8 Male Influenz… C57BL… 4 Cereb…\n 2 Apod GSM254… 13021 Mus mus… 8 Male Influenz… C57BL… 4 Cereb…\n 3 Cyp2d22 GSM254… 2171 Mus mus… 8 Male Influenz… C57BL… 4 Cereb…\n 4 Klk6 GSM254… 448 Mus mus… 8 Male Influenz… C57BL… 4 Cereb…\n 5 Fcrls GSM254… 180 Mus mus… 8 Male Influenz… C57BL… 4 Cereb…\n 6 Slc2a4 GSM254… 313 Mus mus… 8 Male Influenz… C57BL… 4 Cereb…\n 7 Exd2 GSM254… 2366 Mus mus… 8 Male Influenz… C57BL… 4 Cereb…\n 8 Gjc2 GSM254… 310 Mus mus… 8 Male Influenz… C57BL… 4 Cereb…\n 9 Plp1 GSM254… 53126 Mus mus… 8 Male Influenz… C57BL… 4 Cereb…\n10 Gnb4 GSM254… 1355 Mus mus… 8 Male Influenz… C57BL… 4 Cereb…\n# ℹ 14,730 more rows\n# ℹ 9 more variables: mouse <dbl>, ENTREZID <dbl>, product <chr>,\n# ensembl_gene_id <chr>, external_synonym <chr>, chromosome_name <chr>,\n# gene_biotype <chr>, phenotype_description <chr>,\n# hsapiens_homolog_associated_gene_name <chr>\n\nfilter(rna, sex == \"Male\" & infection == \"NonInfected\")\n\n# A tibble: 4,422 × 19\n gene sample expression organism age sex infection strain time tissue\n <chr> <chr> <dbl> <chr> <dbl> <chr> <chr> <chr> <dbl> <chr> \n 1 Asl GSM254… 535 Mus mus… 8 Male NonInfec… C57BL… 0 Cereb…\n 2 Apod GSM254… 13668 Mus mus… 8 Male NonInfec… C57BL… 0 Cereb…\n 3 Cyp2d22 GSM254… 2008 Mus mus… 8 Male NonInfec… C57BL… 0 Cereb…\n 4 Klk6 GSM254… 1101 Mus mus… 8 Male NonInfec… C57BL… 0 Cereb…\n 5 Fcrls GSM254… 375 Mus mus… 8 Male NonInfec… C57BL… 0 Cereb…\n 6 Slc2a4 GSM254… 249 Mus mus… 8 Male NonInfec… C57BL… 0 Cereb…\n 7 Exd2 GSM254… 3126 Mus mus… 8 Male NonInfec… C57BL… 0 Cereb…\n 8 Gjc2 GSM254… 791 Mus mus… 8 Male NonInfec… C57BL… 0 Cereb…\n 9 Plp1 GSM254… 98658 Mus mus… 8 Male NonInfec… C57BL… 0 Cereb…\n10 Gnb4 GSM254… 2437 Mus mus… 8 Male NonInfec… C57BL… 0 Cereb…\n# ℹ 4,412 more rows\n# ℹ 9 more variables: mouse <dbl>, ENTREZID <dbl>, product <chr>,\n# ensembl_gene_id <chr>, external_synonym <chr>, chromosome_name <chr>,\n# gene_biotype <chr>, phenotype_description <chr>,\n# hsapiens_homolog_associated_gene_name <chr>\n\n\nNow let’s imagine we are interested in the human homologs of the mouse genes analysed in this dataset. This information can be found in the last column of the rna tibble, named hsapiens_homolog_associated_gene_name. To visualise it easily, we will create a new table containing just the 2 columns gene and hsapiens_homolog_associated_gene_name.\n\ngenes <- select(rna, gene, hsapiens_homolog_associated_gene_name)\ngenes\n\n# A tibble: 32,428 × 2\n gene hsapiens_homolog_associated_gene_name\n <chr> <chr> \n 1 Asl ASL \n 2 Apod APOD \n 3 Cyp2d22 CYP2D6 \n 4 Klk6 KLK6 \n 5 Fcrls FCRL2 \n 6 Slc2a4 SLC2A4 \n 7 Exd2 EXD2 \n 8 Gjc2 GJC2 \n 9 Plp1 PLP1 \n10 Gnb4 GNB4 \n# ℹ 32,418 more rows\n\n\nSome mouse genes have no human homologs. These can be retrieved using filter() and the is.na() function, that determines whether something is an NA.\n\nfilter(genes, is.na(hsapiens_homolog_associated_gene_name))\n\n# A tibble: 4,290 × 2\n gene hsapiens_homolog_associated_gene_name\n <chr> <chr> \n 1 Prodh <NA> \n 2 Tssk5 <NA> \n 3 Vmn2r1 <NA> \n 4 Gm10654 <NA> \n 5 Hexa <NA> \n 6 Sult1a1 <NA> \n 7 Gm6277 <NA> \n 8 Tmem198b <NA> \n 9 Adam1a <NA> \n10 Ebp <NA> \n# ℹ 4,280 more rows\n\n\nIf we want to keep only mouse genes that have a human homolog, we can insert a “!” symbol that negates the result, so we’re asking for every row where hsapiens_homolog_associated_gene_name is not an NA.\n\nfilter(genes, !is.na(hsapiens_homolog_associated_gene_name))\n\n# A tibble: 28,138 × 2\n gene hsapiens_homolog_associated_gene_name\n <chr> <chr> \n 1 Asl ASL \n 2 Apod APOD \n 3 Cyp2d22 CYP2D6 \n 4 Klk6 KLK6 \n 5 Fcrls FCRL2 \n 6 Slc2a4 SLC2A4 \n 7 Exd2 EXD2 \n 8 Gjc2 GJC2 \n 9 Plp1 PLP1 \n10 Gnb4 GNB4 \n# ℹ 28,128 more rows",
"crumbs": [
"In-Class Materials",
"Session 2",
"Intro to Tidyverse"
]
},
{
"objectID": "session-materials/session2/tidyverse-basics.html#pipes",
"href": "session-materials/session2/tidyverse-basics.html#pipes",
"title": "Intro to Tidyverse",
"section": "Pipes",
"text": "Pipes\nWhat if you want to select and filter at the same time? There are three ways to do this: use intermediate steps, nested functions, or pipes.\nWith intermediate steps, you create a temporary data frame and use that as input to the next function, like this:\n\nrna2 <- filter(rna, sex == \"Male\")\nrna3 <- select(rna2, gene, sample, tissue, expression)\nrna3\n\n# A tibble: 14,740 × 4\n gene sample tissue expression\n <chr> <chr> <chr> <dbl>\n 1 Asl GSM2545340 Cerebellum 626\n 2 Apod GSM2545340 Cerebellum 13021\n 3 Cyp2d22 GSM2545340 Cerebellum 2171\n 4 Klk6 GSM2545340 Cerebellum 448\n 5 Fcrls GSM2545340 Cerebellum 180\n 6 Slc2a4 GSM2545340 Cerebellum 313\n 7 Exd2 GSM2545340 Cerebellum 2366\n 8 Gjc2 GSM2545340 Cerebellum 310\n 9 Plp1 GSM2545340 Cerebellum 53126\n10 Gnb4 GSM2545340 Cerebellum 1355\n# ℹ 14,730 more rows\n\n\nThis is readable, but can clutter up your workspace with lots of intermediate objects that you have to name individually. With multiple steps, that can be hard to keep track of.\nYou can also nest functions (i.e. one function inside of another), like this:\n\nrna3 <- select(filter(rna, sex == \"Male\"), gene, sample, tissue, expression)\nrna3\n\n# A tibble: 14,740 × 4\n gene sample tissue expression\n <chr> <chr> <chr> <dbl>\n 1 Asl GSM2545340 Cerebellum 626\n 2 Apod GSM2545340 Cerebellum 13021\n 3 Cyp2d22 GSM2545340 Cerebellum 2171\n 4 Klk6 GSM2545340 Cerebellum 448\n 5 Fcrls GSM2545340 Cerebellum 180\n 6 Slc2a4 GSM2545340 Cerebellum 313\n 7 Exd2 GSM2545340 Cerebellum 2366\n 8 Gjc2 GSM2545340 Cerebellum 310\n 9 Plp1 GSM2545340 Cerebellum 53126\n10 Gnb4 GSM2545340 Cerebellum 1355\n# ℹ 14,730 more rows\n\n\nThis is handy, but can be difficult to read if too many functions are nested, as R evaluates the expression from the inside out (in this case, filtering, then selecting).\nThe last option, pipes, are a recent addition to R. Pipes let you take the output of one function and send it directly to the next, which is useful when you need to do many things to the same dataset.\nPipes in R look like %>% (made available via the magrittr package) or |> (through base R). If you use RStudio, you can type the pipe with Ctrl + Shift + M if you have a PC or Cmd + Shift + M if you have a Mac.\nIn the above code, we use the pipe to send the rna dataset first through filter() to keep rows where sex is Male, then through select() to keep only the gene, sample, tissue, and expressioncolumns.\nThe pipe %>% takes the object on its left and passes it directly as the first argument to the function on its right, we don’t need to explicitly include the data frame as an argument to the filter() and select() functions any more.\n\nrna %>%\n filter(sex == \"Male\") %>%\n select(gene, sample, tissue, expression)\n\n# A tibble: 14,740 × 4\n gene sample tissue expression\n <chr> <chr> <chr> <dbl>\n 1 Asl GSM2545340 Cerebellum 626\n 2 Apod GSM2545340 Cerebellum 13021\n 3 Cyp2d22 GSM2545340 Cerebellum 2171\n 4 Klk6 GSM2545340 Cerebellum 448\n 5 Fcrls GSM2545340 Cerebellum 180\n 6 Slc2a4 GSM2545340 Cerebellum 313\n 7 Exd2 GSM2545340 Cerebellum 2366\n 8 Gjc2 GSM2545340 Cerebellum 310\n 9 Plp1 GSM2545340 Cerebellum 53126\n10 Gnb4 GSM2545340 Cerebellum 1355\n# ℹ 14,730 more rows\n\n\nSome may find it helpful to read the pipe like the word “then”. For instance, in the above example, we took the data frame rna, then we filtered for rows with sex == \"Male\", then we selected columns gene, sample, tissue, and expression.\nThe dplyr functions by themselves are somewhat simple, but by combining them into linear workflows with the pipe, we can accomplish more complex manipulations of data frames.\nIf we want to create a new object with this smaller version of the data, we can assign it a new name:\n\nrna3 <- rna %>%\n filter(sex == \"Male\") %>%\n select(gene, sample, tissue, expression)\n\nrna3\n\n# A tibble: 14,740 × 4\n gene sample tissue expression\n <chr> <chr> <chr> <dbl>\n 1 Asl GSM2545340 Cerebellum 626\n 2 Apod GSM2545340 Cerebellum 13021\n 3 Cyp2d22 GSM2545340 Cerebellum 2171\n 4 Klk6 GSM2545340 Cerebellum 448\n 5 Fcrls GSM2545340 Cerebellum 180\n 6 Slc2a4 GSM2545340 Cerebellum 313\n 7 Exd2 GSM2545340 Cerebellum 2366\n 8 Gjc2 GSM2545340 Cerebellum 310\n 9 Plp1 GSM2545340 Cerebellum 53126\n10 Gnb4 GSM2545340 Cerebellum 1355\n# ℹ 14,730 more rows\n\n\n\n\n\n\n\n\nChallenge:\n\n\n\nUsing pipes, subset the rna data to keep observations in female mice at time 0, where the gene has an expression higher than 50000, and retain only the columns gene, sample, time, expression and age.\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\nrna %>%\n filter(expression > 50000,\n sex == \"Female\",\n time == 0 ) %>%\n select(gene, sample, time, expression, age)\n\n# A tibble: 9 × 5\n gene sample time expression age\n <chr> <chr> <dbl> <dbl> <dbl>\n1 Plp1 GSM2545337 0 101241 8\n2 Atp1b1 GSM2545337 0 53260 8\n3 Plp1 GSM2545338 0 96534 8\n4 Atp1b1 GSM2545338 0 50614 8\n5 Plp1 GSM2545348 0 102790 8\n6 Atp1b1 GSM2545348 0 59544 8\n7 Plp1 GSM2545353 0 71237 8\n8 Glul GSM2545353 0 52451 8\n9 Atp1b1 GSM2545353 0 61451 8",
"crumbs": [
"In-Class Materials",
"Session 2",
"Intro to Tidyverse"
]
},
{
"objectID": "session-materials/session2/tidyverse-basics.html#mutate",
"href": "session-materials/session2/tidyverse-basics.html#mutate",
"title": "Intro to Tidyverse",
"section": "Mutate",
"text": "Mutate\nFrequently you’ll want to create new columns based on the values of existing columns, for example to do unit conversions, or to find the ratio of values in two columns. For this we’ll use mutate().\nTo create a new column of time in hours:\n\nrna %>%\n mutate(time_hours = time * 24) %>%\n select(time, time_hours)\n\n# A tibble: 32,428 × 2\n time time_hours\n <dbl> <dbl>\n 1 8 192\n 2 8 192\n 3 8 192\n 4 8 192\n 5 8 192\n 6 8 192\n 7 8 192\n 8 8 192\n 9 8 192\n10 8 192\n# ℹ 32,418 more rows\n\n\nYou can also create a second new column based on the first new column within the same call of mutate():\n\nrna %>%\n mutate(time_hours = time * 24,\n time_mn = time_hours * 60) %>%\n select(time, time_hours, time_mn)\n\n# A tibble: 32,428 × 3\n time time_hours time_mn\n <dbl> <dbl> <dbl>\n 1 8 192 11520\n 2 8 192 11520\n 3 8 192 11520\n 4 8 192 11520\n 5 8 192 11520\n 6 8 192 11520\n 7 8 192 11520\n 8 8 192 11520\n 9 8 192 11520\n10 8 192 11520\n# ℹ 32,418 more rows\n\n\n\n\n\n\n\n\nChallenge\n\n\n\nCreate a new data frame from the rna data that meets the following criteria: contains only the gene, chromosome_name, phenotype_description, sample, and expression columns. The expression values should be log-transformed. This data frame must only contain genes located on sex chromosomes, associated with a phenotype_description, and with a log expression higher than 5.\nHint: think about how the commands should be ordered to produce this data frame!\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\nrna %>%\n mutate(expression = log(expression)) %>%\n select(gene, chromosome_name, phenotype_description, sample, expression) %>%\n filter(chromosome_name == \"X\" | chromosome_name == \"Y\") %>%\n filter(!is.na(phenotype_description)) %>%\n filter(expression > 5)\n\n# A tibble: 649 × 5\n gene chromosome_name phenotype_description sample expression\n <chr> <chr> <chr> <chr> <dbl>\n 1 Plp1 X abnormal CNS glial cell morphology GSM25… 10.7 \n 2 Slc7a3 X decreased body length GSM25… 5.46\n 3 Plxnb3 X abnormal coat appearance GSM25… 6.58\n 4 Rbm3 X abnormal liver morphology GSM25… 9.32\n 5 Cfp X abnormal cardiovascular system phys… GSM25… 6.18\n 6 Ebp X abnormal embryonic erythrocyte morp… GSM25… 6.68\n 7 Cd99l2 X abnormal cellular extravasation GSM25… 8.04\n 8 Piga X abnormal brain development GSM25… 6.06\n 9 Pim2 X decreased T cell proliferation GSM25… 7.11\n10 Itm2a X no abnormal phenotype detected GSM25… 7.48\n# ℹ 639 more rows\n\n\n\n\n\n\nThe materials in this lesson have been adapted from work created by the HBC and Data Carpentry, as well as materials created by Laurent Gatto, Charlotte Soneson, Jenny Drnevich, Robert Castelo, and Kevin Rue-Albert. These are open access materials distributed under the terms of the Creative Commons Attribution license (CC BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.",
"crumbs": [
"In-Class Materials",
"Session 2",
"Intro to Tidyverse"
]
},
{
"objectID": "session-materials/session2/making-functions.html",
"href": "session-materials/session2/making-functions.html",
"title": "Creating Functions",
"section": "",
"text": "One of the great strengths of R is the user’s ability to add functions. Sometimes there is a small task (or series of tasks) you need done and you find yourself having to repeat it multiple times. In these types of situations, it can be helpful to create your own custom function. The structure of a function is given below:\nname_of_function <- function(argument1, argument2) {\n statements or code that does something\n return(something)\n}\n\nFirst you give your function a name.\nThen you assign value to it, where the value is the function.\n\nWhen defining the function you will want to provide the list of arguments required (inputs and/or options to modify behaviour of the function), and wrapped between curly brackets place the tasks that are being executed on/using those arguments. The argument(s) can be any type of object (like a scalar, a matrix, a dataframe, a vector, a logical, etc), and it’s not necessary to define what it is in any way.\nFinally, you can “return” the value of the object from the function, meaning pass the value of it into the global environment. The important idea behind functions is that objects that are created within the function are local to the environment of the function – they don’t exist outside of the function.\nLet’s try creating a simple example function. This function will take in a numeric value as input, and return the squared value.\n\nsquare_it <- function(x) {\n square <- x * x\n return(square)\n}\n\nOnce you run the code, you should see a function named square_it in the Environment panel (located at the top right of Rstudio interface). Now, we can use this function as any other base R functions. We type out the name of the function, and inside the parentheses we provide a numeric value x:\n\nsquare_it(5)\n\n[1] 25\n\n\nPretty simple, right? In this case, we only had one line of code that was run, but in theory you could have many lines of code to get obtain the final results that you want to “return” to the user.\n\n\nIn the example above, we created a new variable called square inside the function, and then return the value of square. If you don’t use return(), by default R will return the value of the last line of code inside that function. That is to say, the following function will also work.\n\nsquare_it <- function(x) {\n x * x\n}\n\nHowever, we recommend always using return at the end of a function as the best practice.\n\nWe have only scratched the surface here when it comes to creating functions! We will revisit this in later lessons, but if interested you can also find more detailed information on this R-bloggers site, which is where we adapted this example from.\nExercise\n\n\n\n\n\n\nBasic\n\n\n\n\nLet’s create a function temp_conv(), which converts the temperature in Fahrenheit (input) to the temperature in Kelvin (output).\n\nWe could perform a two-step calculation: first convert from Fahrenheit to Celsius, and then convert from Celsius to Kelvin.\nThe formula for these two calculations are as follows: temp_c = (temp_f - 32) * 5 / 9; temp_k = temp_c + 273.15. To test your function,\nif your input is 70, the result of temp_conv(70) should be 294.2611.\n\nNow we want to round the temperature in Kelvin (output of temp_conv()) to a single decimal place. Use the round() function with the newly-created temp_conv() function to achieve this in one line of code. If your input is 70, the output should now be 294.3.\n\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\n# Basic\n\n# 1\ntemp_conv <- function(temp_f) {\n temp_c = (temp_f - 32) * 5 / 9\n temp_k = temp_c + 273.15\n return (temp_k)\n}\n\n# 2\nround(temp_conv(70), digits = 1)\n\n[1] 294.3\n\n\n\n\n\n\n\n\n\n\n\nAdvanced\n\n\n\nThe Fibonacci sequence is \\(0, 1, 1, 2, 3, 5, 8, ...\\) where the first two terms are 0 and 1, and for all other terms \\(n^{th}\\) term is the sum of the \\((n-1)^{th}\\) and \\((n-2)^{th}\\) terms. Note that for n=0 you should return 0 and for n=1 you should return 1 as the first 2 terms.\n\nWrite a function fibonacci which takes in a single integer argument n and returns the \\(n^{th}\\) term in the Fibonacci sequence.\nHave your function stop with an appropriate message if the argument n is not an integer. Stop allows you to create your own errors in R. This StackOverflow thread contains useful information on how to tell if something is or is not an integer in R.\n\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\n# Advanced\nfibonacci <- function(n){\n \n # These next 3 lines are part 2\n if((n %% 1)!=0){\n stop(\"Must provide an integer to fibonacci\")\n }\n fibs <- c(0,1)\n for (i in 2:n){\n fibs <- c(fibs, fibs[i-1]+fibs[i])\n }\n return(fibs[n+1])\n}\n\n\n\n\n\n\n\n\n\n\nChallenge\n\n\n\nRe-write your fibonacci function so that it calculates the Fibonacci sequence recursively, meaning that it calls itself. Your function should contain no loops or iterative code.\nYou will need to define two base cases, where the function does not call itself.\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\n#Challenge\nfibonacci2 <- function(n){\n if((n %% 1)!=0){\n stop(\"Must provide an integer to fibonacci\")\n }\n # We call these two if statement the 'base cases' of the recursion\n if (n==0){\n return(0)\n }\n if (n==1){\n return(1)\n }\n # And this is the recursive case, where the function calls itself\n return(fibonacci2(n-1)+fibonacci2(n-2))\n}\n\nRecursion isn’t relevant to most data analysis, as it is often significantly slower than a non-recursive solution in most programming languages.\nHowever, setting up a solution as recursive sometimes allows us to perform an algorithmic strategy called dynamic programming and is fundamental to most sequence alignment algorithms.\n\n\n\n\nThe materials in this lesson have been adapted from work created by the HBC and Data Carpentry, as well as materials created by Laurent Gatto, Charlotte Soneson, Jenny Drnevich, Robert Castelo, and Kevin Rue-Albert. These are open access materials distributed under the terms of the Creative Commons Attribution license (CC BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.",
"crumbs": [
"In-Class Materials",
"Session 2",
"Creating Functions"
]
},
{
"objectID": "session-materials/session2/making-functions.html#user-defined-functions",
"href": "session-materials/session2/making-functions.html#user-defined-functions",
"title": "Creating Functions",
"section": "",
"text": "One of the great strengths of R is the user’s ability to add functions. Sometimes there is a small task (or series of tasks) you need done and you find yourself having to repeat it multiple times. In these types of situations, it can be helpful to create your own custom function. The structure of a function is given below:\nname_of_function <- function(argument1, argument2) {\n statements or code that does something\n return(something)\n}\n\nFirst you give your function a name.\nThen you assign value to it, where the value is the function.\n\nWhen defining the function you will want to provide the list of arguments required (inputs and/or options to modify behaviour of the function), and wrapped between curly brackets place the tasks that are being executed on/using those arguments. The argument(s) can be any type of object (like a scalar, a matrix, a dataframe, a vector, a logical, etc), and it’s not necessary to define what it is in any way.\nFinally, you can “return” the value of the object from the function, meaning pass the value of it into the global environment. The important idea behind functions is that objects that are created within the function are local to the environment of the function – they don’t exist outside of the function.\nLet’s try creating a simple example function. This function will take in a numeric value as input, and return the squared value.\n\nsquare_it <- function(x) {\n square <- x * x\n return(square)\n}\n\nOnce you run the code, you should see a function named square_it in the Environment panel (located at the top right of Rstudio interface). Now, we can use this function as any other base R functions. We type out the name of the function, and inside the parentheses we provide a numeric value x:\n\nsquare_it(5)\n\n[1] 25\n\n\nPretty simple, right? In this case, we only had one line of code that was run, but in theory you could have many lines of code to get obtain the final results that you want to “return” to the user.\n\n\nIn the example above, we created a new variable called square inside the function, and then return the value of square. If you don’t use return(), by default R will return the value of the last line of code inside that function. That is to say, the following function will also work.\n\nsquare_it <- function(x) {\n x * x\n}\n\nHowever, we recommend always using return at the end of a function as the best practice.\n\nWe have only scratched the surface here when it comes to creating functions! We will revisit this in later lessons, but if interested you can also find more detailed information on this R-bloggers site, which is where we adapted this example from.\nExercise\n\n\n\n\n\n\nBasic\n\n\n\n\nLet’s create a function temp_conv(), which converts the temperature in Fahrenheit (input) to the temperature in Kelvin (output).\n\nWe could perform a two-step calculation: first convert from Fahrenheit to Celsius, and then convert from Celsius to Kelvin.\nThe formula for these two calculations are as follows: temp_c = (temp_f - 32) * 5 / 9; temp_k = temp_c + 273.15. To test your function,\nif your input is 70, the result of temp_conv(70) should be 294.2611.\n\nNow we want to round the temperature in Kelvin (output of temp_conv()) to a single decimal place. Use the round() function with the newly-created temp_conv() function to achieve this in one line of code. If your input is 70, the output should now be 294.3.\n\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\n# Basic\n\n# 1\ntemp_conv <- function(temp_f) {\n temp_c = (temp_f - 32) * 5 / 9\n temp_k = temp_c + 273.15\n return (temp_k)\n}\n\n# 2\nround(temp_conv(70), digits = 1)\n\n[1] 294.3\n\n\n\n\n\n\n\n\n\n\n\nAdvanced\n\n\n\nThe Fibonacci sequence is \\(0, 1, 1, 2, 3, 5, 8, ...\\) where the first two terms are 0 and 1, and for all other terms \\(n^{th}\\) term is the sum of the \\((n-1)^{th}\\) and \\((n-2)^{th}\\) terms. Note that for n=0 you should return 0 and for n=1 you should return 1 as the first 2 terms.\n\nWrite a function fibonacci which takes in a single integer argument n and returns the \\(n^{th}\\) term in the Fibonacci sequence.\nHave your function stop with an appropriate message if the argument n is not an integer. Stop allows you to create your own errors in R. This StackOverflow thread contains useful information on how to tell if something is or is not an integer in R.\n\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\n# Advanced\nfibonacci <- function(n){\n \n # These next 3 lines are part 2\n if((n %% 1)!=0){\n stop(\"Must provide an integer to fibonacci\")\n }\n fibs <- c(0,1)\n for (i in 2:n){\n fibs <- c(fibs, fibs[i-1]+fibs[i])\n }\n return(fibs[n+1])\n}\n\n\n\n\n\n\n\n\n\n\nChallenge\n\n\n\nRe-write your fibonacci function so that it calculates the Fibonacci sequence recursively, meaning that it calls itself. Your function should contain no loops or iterative code.\nYou will need to define two base cases, where the function does not call itself.\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\n#Challenge\nfibonacci2 <- function(n){\n if((n %% 1)!=0){\n stop(\"Must provide an integer to fibonacci\")\n }\n # We call these two if statement the 'base cases' of the recursion\n if (n==0){\n return(0)\n }\n if (n==1){\n return(1)\n }\n # And this is the recursive case, where the function calls itself\n return(fibonacci2(n-1)+fibonacci2(n-2))\n}\n\nRecursion isn’t relevant to most data analysis, as it is often significantly slower than a non-recursive solution in most programming languages.\nHowever, setting up a solution as recursive sometimes allows us to perform an algorithmic strategy called dynamic programming and is fundamental to most sequence alignment algorithms.\n\n\n\n\nThe materials in this lesson have been adapted from work created by the HBC and Data Carpentry, as well as materials created by Laurent Gatto, Charlotte Soneson, Jenny Drnevich, Robert Castelo, and Kevin Rue-Albert. These are open access materials distributed under the terms of the Creative Commons Attribution license (CC BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.",
"crumbs": [
"In-Class Materials",
"Session 2",
"Creating Functions"
]
},
{
"objectID": "session-materials/session2/split-apply-combine.html",
"href": "session-materials/session2/split-apply-combine.html",
"title": "Split-Apply-Combine",
"section": "",
"text": "Many data analysis tasks can be approached using the split-apply-combine paradigm: split the data into groups, apply some analysis to each group, and then combine the results. dplyr makes this very easy through the use of the group_by() function.\n\nrna %>%\n group_by(gene)\n\n# A tibble: 32,428 × 19\n# Groups: gene [1,474]\n gene sample expression organism age sex infection strain time tissue\n <chr> <chr> <dbl> <chr> <dbl> <chr> <chr> <chr> <dbl> <chr> \n 1 Asl GSM254… 1170 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 2 Apod GSM254… 36194 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 3 Cyp2d22 GSM254… 4060 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 4 Klk6 GSM254… 287 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 5 Fcrls GSM254… 85 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 6 Slc2a4 GSM254… 782 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 7 Exd2 GSM254… 1619 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 8 Gjc2 GSM254… 288 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 9 Plp1 GSM254… 43217 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n10 Gnb4 GSM254… 1071 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n# ℹ 32,418 more rows\n# ℹ 9 more variables: mouse <dbl>, ENTREZID <dbl>, product <chr>,\n# ensembl_gene_id <chr>, external_synonym <chr>, chromosome_name <chr>,\n# gene_biotype <chr>, phenotype_description <chr>,\n# hsapiens_homolog_associated_gene_name <chr>\n\n\nThe group_by() function doesn’t perform any data processing, it groups the data into subsets: in the example above, our initial tibble of 32428 observations is split into 1474 groups based on the gene variable.\nWe could similarly decide to group the tibble by the samples:\n\nrna %>%\n group_by(sample)\n\n# A tibble: 32,428 × 19\n# Groups: sample [22]\n gene sample expression organism age sex infection strain time tissue\n <chr> <chr> <dbl> <chr> <dbl> <chr> <chr> <chr> <dbl> <chr> \n 1 Asl GSM254… 1170 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 2 Apod GSM254… 36194 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 3 Cyp2d22 GSM254… 4060 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 4 Klk6 GSM254… 287 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 5 Fcrls GSM254… 85 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 6 Slc2a4 GSM254… 782 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 7 Exd2 GSM254… 1619 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 8 Gjc2 GSM254… 288 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 9 Plp1 GSM254… 43217 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n10 Gnb4 GSM254… 1071 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n# ℹ 32,418 more rows\n# ℹ 9 more variables: mouse <dbl>, ENTREZID <dbl>, product <chr>,\n# ensembl_gene_id <chr>, external_synonym <chr>, chromosome_name <chr>,\n# gene_biotype <chr>, phenotype_description <chr>,\n# hsapiens_homolog_associated_gene_name <chr>\n\n\nHere our initial tibble of 32428 observations is split into 22 groups based on the sample variable.\nOnce the data has been grouped, subsequent operations will be applied on each group independently.\n\n\ngroup_by() is often used together with summarise(), which collapses each group into a single-row summary of that group.\ngroup_by() takes as arguments the column names that contain the categorical variables for which you want to calculate the summary statistics. So to compute the mean expression by gene:\n\nrna %>%\n group_by(gene) %>%\n summarise(mean_expression = mean(expression))\n\n# A tibble: 1,474 × 2\n gene mean_expression\n <chr> <dbl>\n 1 AI504432 1053. \n 2 AW046200 131. \n 3 AW551984 295. \n 4 Aamp 4751. \n 5 Abca12 4.55\n 6 Abcc8 2498. \n 7 Abhd14a 525. \n 8 Abi2 4909. \n 9 Abi3bp 1002. \n10 Abl2 2124. \n# ℹ 1,464 more rows\n\n\nWe could also want to calculate the mean expression levels of all genes in each sample:\n\nrna %>%\n group_by(sample) %>%\n summarise(mean_expression = mean(expression))\n\n# A tibble: 22 × 2\n sample mean_expression\n <chr> <dbl>\n 1 GSM2545336 2062.\n 2 GSM2545337 1766.\n 3 GSM2545338 1668.\n 4 GSM2545339 1696.\n 5 GSM2545340 1682.\n 6 GSM2545341 1638.\n 7 GSM2545342 1594.\n 8 GSM2545343 2107.\n 9 GSM2545344 1712.\n10 GSM2545345 1700.\n# ℹ 12 more rows\n\n\nBut we can can also group by multiple columns:\n\nrna %>%\n group_by(gene, infection, time) %>%\n summarise(mean_expression = mean(expression))\n\n`summarise()` has grouped output by 'gene', 'infection'. You can override using\nthe `.groups` argument.\n\n\n# A tibble: 4,422 × 4\n# Groups: gene, infection [2,948]\n gene infection time mean_expression\n <chr> <chr> <dbl> <dbl>\n 1 AI504432 InfluenzaA 4 1104.\n 2 AI504432 InfluenzaA 8 1014 \n 3 AI504432 NonInfected 0 1034.\n 4 AW046200 InfluenzaA 4 152.\n 5 AW046200 InfluenzaA 8 81 \n 6 AW046200 NonInfected 0 155.\n 7 AW551984 InfluenzaA 4 302.\n 8 AW551984 InfluenzaA 8 342.\n 9 AW551984 NonInfected 0 238 \n10 Aamp InfluenzaA 4 4870 \n# ℹ 4,412 more rows\n\n\nOnce the data is grouped, you can also summarise multiple variables at the same time (and not necessarily on the same variable). For instance, we could add a column indicating the median expression by gene and by condition:\n\nrna %>%\n group_by(gene, infection, time) %>%\n summarise(mean_expression = mean(expression),\n median_expression = median(expression))\n\n`summarise()` has grouped output by 'gene', 'infection'. You can override using\nthe `.groups` argument.\n\n\n# A tibble: 4,422 × 5\n# Groups: gene, infection [2,948]\n gene infection time mean_expression median_expression\n <chr> <chr> <dbl> <dbl> <dbl>\n 1 AI504432 InfluenzaA 4 1104. 1094.\n 2 AI504432 InfluenzaA 8 1014 985 \n 3 AI504432 NonInfected 0 1034. 1016 \n 4 AW046200 InfluenzaA 4 152. 144.\n 5 AW046200 InfluenzaA 8 81 82 \n 6 AW046200 NonInfected 0 155. 163 \n 7 AW551984 InfluenzaA 4 302. 245 \n 8 AW551984 InfluenzaA 8 342. 287 \n 9 AW551984 NonInfected 0 238 265 \n10 Aamp InfluenzaA 4 4870 4708 \n# ℹ 4,412 more rows\n\n\n\n\n\n\n\n\nChallenge\n\n\n\nCalculate the mean expression level of gene “Dok3” by timepoints.\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\nrna %>%\n filter(gene == \"Dok3\") %>%\n group_by(time) %>%\n summarise(mean = mean(expression))\n\n# A tibble: 3 × 2\n time mean\n <dbl> <dbl>\n1 0 169 \n2 4 156.\n3 8 61 \n\n\n\n\n\n\n\n\nWhen working with data, we often want to know the number of observations found for each factor or combination of factors. For this task, dplyr provides count(). For example, if we wanted to count the number of rows of data for each infected and non-infected samples, we would do:\n\nrna %>%\n count(infection)\n\n# A tibble: 2 × 2\n infection n\n <chr> <int>\n1 InfluenzaA 22110\n2 NonInfected 10318\n\n\nThe count() function is shorthand for something we’ve already seen: grouping by a variable, and summarising it by counting the number of observations in that group. In other words, rna %>% count(infection) is equivalent to:\n\nrna %>%\n group_by(infection) %>%\n summarise(n = n())\n\n# A tibble: 2 × 2\n infection n\n <chr> <int>\n1 InfluenzaA 22110\n2 NonInfected 10318\n\n\nThe previous example shows the use of count() to count the number of rows/observations for one factor (i.e., infection). If we wanted to count a combination of factors, such as infection and time, we would specify the first and the second factor as the arguments of count():\n\nrna %>%\n count(infection, time)\n\n# A tibble: 3 × 3\n infection time n\n <chr> <dbl> <int>\n1 InfluenzaA 4 11792\n2 InfluenzaA 8 10318\n3 NonInfected 0 10318\n\n\nwhich is equivalent to this:\n\nrna %>%\n group_by(infection, time) %>%\n summarise(n = n())\n\n`summarise()` has grouped output by 'infection'. You can override using the\n`.groups` argument.\n\n\n# A tibble: 3 × 3\n# Groups: infection [2]\n infection time n\n <chr> <dbl> <int>\n1 InfluenzaA 4 11792\n2 InfluenzaA 8 10318\n3 NonInfected 0 10318\n\n\nIt is sometimes useful to sort the result to facilitate the comparisons. We can use arrange() to sort the table. For instance, we might want to arrange the table above by time:\n\nrna %>%\n count(infection, time) %>%\n arrange(time)\n\n# A tibble: 3 × 3\n infection time n\n <chr> <dbl> <int>\n1 NonInfected 0 10318\n2 InfluenzaA 4 11792\n3 InfluenzaA 8 10318\n\n\nor by counts:\n\nrna %>%\n count(infection, time) %>%\n arrange(n)\n\n# A tibble: 3 × 3\n infection time n\n <chr> <dbl> <int>\n1 InfluenzaA 8 10318\n2 NonInfected 0 10318\n3 InfluenzaA 4 11792\n\n\nTo sort in descending order, we need to add the desc() function:\n\nrna %>%\n count(infection, time) %>%\n arrange(desc(n))\n\n# A tibble: 3 × 3\n infection time n\n <chr> <dbl> <int>\n1 InfluenzaA 4 11792\n2 InfluenzaA 8 10318\n3 NonInfected 0 10318\n\n\n\n\n\n\n\n\nChallenge\n\n\n\n\nHow many genes were analysed in each sample?\nUse group_by() and summarise() to evaluate the sequencing depth (the sum of all counts) in each sample. Which sample has the highest sequencing depth?\nPick one sample and evaluate the number of genes by biotype.\nIdentify genes associated with the “abnormal DNA methylation” phenotype description, and calculate their mean expression (in log) at time 0, time 4 and time 8.\n\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\n## 1.\nrna %>%\n count(sample)\n\n# A tibble: 22 × 2\n sample n\n <chr> <int>\n 1 GSM2545336 1474\n 2 GSM2545337 1474\n 3 GSM2545338 1474\n 4 GSM2545339 1474\n 5 GSM2545340 1474\n 6 GSM2545341 1474\n 7 GSM2545342 1474\n 8 GSM2545343 1474\n 9 GSM2545344 1474\n10 GSM2545345 1474\n# ℹ 12 more rows\n\n## 2.\nrna %>%\n group_by(sample) %>%\n summarise(seq_depth = sum(expression)) %>%\n arrange(desc(seq_depth))\n\n# A tibble: 22 × 2\n sample seq_depth\n <chr> <dbl>\n 1 GSM2545350 3255566\n 2 GSM2545352 3216163\n 3 GSM2545343 3105652\n 4 GSM2545336 3039671\n 5 GSM2545380 3036098\n 6 GSM2545353 2953249\n 7 GSM2545348 2913678\n 8 GSM2545362 2913517\n 9 GSM2545351 2782464\n10 GSM2545349 2758006\n# ℹ 12 more rows\n\n## 3.\nrna %>%\n filter(sample == \"GSM2545336\") %>%\n group_by(gene_biotype) %>%\n count(gene_biotype) %>%\n arrange(desc(n))\n\n# A tibble: 13 × 2\n# Groups: gene_biotype [13]\n gene_biotype n\n <chr> <int>\n 1 protein_coding 1321\n 2 lncRNA 69\n 3 processed_pseudogene 59\n 4 miRNA 7\n 5 snoRNA 5\n 6 TEC 4\n 7 polymorphic_pseudogene 2\n 8 unprocessed_pseudogene 2\n 9 IG_C_gene 1\n10 scaRNA 1\n11 transcribed_processed_pseudogene 1\n12 transcribed_unitary_pseudogene 1\n13 transcribed_unprocessed_pseudogene 1\n\n## 4.\nrna %>%\n filter(phenotype_description == \"abnormal DNA methylation\") %>%\n group_by(gene, time) %>%\n summarise(mean_expression = mean(log(expression))) %>%\n arrange()\n\n`summarise()` has grouped output by 'gene'. You can override using the\n`.groups` argument.\n\n\n# A tibble: 6 × 3\n# Groups: gene [2]\n gene time mean_expression\n <chr> <dbl> <dbl>\n1 Xist 0 6.95\n2 Xist 4 6.34\n3 Xist 8 7.13\n4 Zdbf2 0 6.27\n5 Zdbf2 4 6.27\n6 Zdbf2 8 6.19\n\n\n\n\n\n\nThe materials in this lesson have been adapted from work created by the HBC and Data Carpentry, as well as materials created by Laurent Gatto, Charlotte Soneson, Jenny Drnevich, Robert Castelo, and Kevin Rue-Albert. These are open access materials distributed under the terms of the Creative Commons Attribution license (CC BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.",
"crumbs": [
"In-Class Materials",
"Session 2",
"Split-Apply-Combine"
]
},
{
"objectID": "session-materials/session2/split-apply-combine.html#split-apply-combine-data-analysis",
"href": "session-materials/session2/split-apply-combine.html#split-apply-combine-data-analysis",
"title": "Split-Apply-Combine",
"section": "",
"text": "Many data analysis tasks can be approached using the split-apply-combine paradigm: split the data into groups, apply some analysis to each group, and then combine the results. dplyr makes this very easy through the use of the group_by() function.\n\nrna %>%\n group_by(gene)\n\n# A tibble: 32,428 × 19\n# Groups: gene [1,474]\n gene sample expression organism age sex infection strain time tissue\n <chr> <chr> <dbl> <chr> <dbl> <chr> <chr> <chr> <dbl> <chr> \n 1 Asl GSM254… 1170 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 2 Apod GSM254… 36194 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 3 Cyp2d22 GSM254… 4060 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 4 Klk6 GSM254… 287 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 5 Fcrls GSM254… 85 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 6 Slc2a4 GSM254… 782 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 7 Exd2 GSM254… 1619 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 8 Gjc2 GSM254… 288 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 9 Plp1 GSM254… 43217 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n10 Gnb4 GSM254… 1071 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n# ℹ 32,418 more rows\n# ℹ 9 more variables: mouse <dbl>, ENTREZID <dbl>, product <chr>,\n# ensembl_gene_id <chr>, external_synonym <chr>, chromosome_name <chr>,\n# gene_biotype <chr>, phenotype_description <chr>,\n# hsapiens_homolog_associated_gene_name <chr>\n\n\nThe group_by() function doesn’t perform any data processing, it groups the data into subsets: in the example above, our initial tibble of 32428 observations is split into 1474 groups based on the gene variable.\nWe could similarly decide to group the tibble by the samples:\n\nrna %>%\n group_by(sample)\n\n# A tibble: 32,428 × 19\n# Groups: sample [22]\n gene sample expression organism age sex infection strain time tissue\n <chr> <chr> <dbl> <chr> <dbl> <chr> <chr> <chr> <dbl> <chr> \n 1 Asl GSM254… 1170 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 2 Apod GSM254… 36194 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 3 Cyp2d22 GSM254… 4060 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 4 Klk6 GSM254… 287 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 5 Fcrls GSM254… 85 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 6 Slc2a4 GSM254… 782 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 7 Exd2 GSM254… 1619 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 8 Gjc2 GSM254… 288 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n 9 Plp1 GSM254… 43217 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n10 Gnb4 GSM254… 1071 Mus mus… 8 Fema… Influenz… C57BL… 8 Cereb…\n# ℹ 32,418 more rows\n# ℹ 9 more variables: mouse <dbl>, ENTREZID <dbl>, product <chr>,\n# ensembl_gene_id <chr>, external_synonym <chr>, chromosome_name <chr>,\n# gene_biotype <chr>, phenotype_description <chr>,\n# hsapiens_homolog_associated_gene_name <chr>\n\n\nHere our initial tibble of 32428 observations is split into 22 groups based on the sample variable.\nOnce the data has been grouped, subsequent operations will be applied on each group independently.\n\n\ngroup_by() is often used together with summarise(), which collapses each group into a single-row summary of that group.\ngroup_by() takes as arguments the column names that contain the categorical variables for which you want to calculate the summary statistics. So to compute the mean expression by gene:\n\nrna %>%\n group_by(gene) %>%\n summarise(mean_expression = mean(expression))\n\n# A tibble: 1,474 × 2\n gene mean_expression\n <chr> <dbl>\n 1 AI504432 1053. \n 2 AW046200 131. \n 3 AW551984 295. \n 4 Aamp 4751. \n 5 Abca12 4.55\n 6 Abcc8 2498. \n 7 Abhd14a 525. \n 8 Abi2 4909. \n 9 Abi3bp 1002. \n10 Abl2 2124. \n# ℹ 1,464 more rows\n\n\nWe could also want to calculate the mean expression levels of all genes in each sample:\n\nrna %>%\n group_by(sample) %>%\n summarise(mean_expression = mean(expression))\n\n# A tibble: 22 × 2\n sample mean_expression\n <chr> <dbl>\n 1 GSM2545336 2062.\n 2 GSM2545337 1766.\n 3 GSM2545338 1668.\n 4 GSM2545339 1696.\n 5 GSM2545340 1682.\n 6 GSM2545341 1638.\n 7 GSM2545342 1594.\n 8 GSM2545343 2107.\n 9 GSM2545344 1712.\n10 GSM2545345 1700.\n# ℹ 12 more rows\n\n\nBut we can can also group by multiple columns:\n\nrna %>%\n group_by(gene, infection, time) %>%\n summarise(mean_expression = mean(expression))\n\n`summarise()` has grouped output by 'gene', 'infection'. You can override using\nthe `.groups` argument.\n\n\n# A tibble: 4,422 × 4\n# Groups: gene, infection [2,948]\n gene infection time mean_expression\n <chr> <chr> <dbl> <dbl>\n 1 AI504432 InfluenzaA 4 1104.\n 2 AI504432 InfluenzaA 8 1014 \n 3 AI504432 NonInfected 0 1034.\n 4 AW046200 InfluenzaA 4 152.\n 5 AW046200 InfluenzaA 8 81 \n 6 AW046200 NonInfected 0 155.\n 7 AW551984 InfluenzaA 4 302.\n 8 AW551984 InfluenzaA 8 342.\n 9 AW551984 NonInfected 0 238 \n10 Aamp InfluenzaA 4 4870 \n# ℹ 4,412 more rows\n\n\nOnce the data is grouped, you can also summarise multiple variables at the same time (and not necessarily on the same variable). For instance, we could add a column indicating the median expression by gene and by condition:\n\nrna %>%\n group_by(gene, infection, time) %>%\n summarise(mean_expression = mean(expression),\n median_expression = median(expression))\n\n`summarise()` has grouped output by 'gene', 'infection'. You can override using\nthe `.groups` argument.\n\n\n# A tibble: 4,422 × 5\n# Groups: gene, infection [2,948]\n gene infection time mean_expression median_expression\n <chr> <chr> <dbl> <dbl> <dbl>\n 1 AI504432 InfluenzaA 4 1104. 1094.\n 2 AI504432 InfluenzaA 8 1014 985 \n 3 AI504432 NonInfected 0 1034. 1016 \n 4 AW046200 InfluenzaA 4 152. 144.\n 5 AW046200 InfluenzaA 8 81 82 \n 6 AW046200 NonInfected 0 155. 163 \n 7 AW551984 InfluenzaA 4 302. 245 \n 8 AW551984 InfluenzaA 8 342. 287 \n 9 AW551984 NonInfected 0 238 265 \n10 Aamp InfluenzaA 4 4870 4708 \n# ℹ 4,412 more rows\n\n\n\n\n\n\n\n\nChallenge\n\n\n\nCalculate the mean expression level of gene “Dok3” by timepoints.\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\nrna %>%\n filter(gene == \"Dok3\") %>%\n group_by(time) %>%\n summarise(mean = mean(expression))\n\n# A tibble: 3 × 2\n time mean\n <dbl> <dbl>\n1 0 169 \n2 4 156.\n3 8 61 \n\n\n\n\n\n\n\n\nWhen working with data, we often want to know the number of observations found for each factor or combination of factors. For this task, dplyr provides count(). For example, if we wanted to count the number of rows of data for each infected and non-infected samples, we would do:\n\nrna %>%\n count(infection)\n\n# A tibble: 2 × 2\n infection n\n <chr> <int>\n1 InfluenzaA 22110\n2 NonInfected 10318\n\n\nThe count() function is shorthand for something we’ve already seen: grouping by a variable, and summarising it by counting the number of observations in that group. In other words, rna %>% count(infection) is equivalent to:\n\nrna %>%\n group_by(infection) %>%\n summarise(n = n())\n\n# A tibble: 2 × 2\n infection n\n <chr> <int>\n1 InfluenzaA 22110\n2 NonInfected 10318\n\n\nThe previous example shows the use of count() to count the number of rows/observations for one factor (i.e., infection). If we wanted to count a combination of factors, such as infection and time, we would specify the first and the second factor as the arguments of count():\n\nrna %>%\n count(infection, time)\n\n# A tibble: 3 × 3\n infection time n\n <chr> <dbl> <int>\n1 InfluenzaA 4 11792\n2 InfluenzaA 8 10318\n3 NonInfected 0 10318\n\n\nwhich is equivalent to this:\n\nrna %>%\n group_by(infection, time) %>%\n summarise(n = n())\n\n`summarise()` has grouped output by 'infection'. You can override using the\n`.groups` argument.\n\n\n# A tibble: 3 × 3\n# Groups: infection [2]\n infection time n\n <chr> <dbl> <int>\n1 InfluenzaA 4 11792\n2 InfluenzaA 8 10318\n3 NonInfected 0 10318\n\n\nIt is sometimes useful to sort the result to facilitate the comparisons. We can use arrange() to sort the table. For instance, we might want to arrange the table above by time:\n\nrna %>%\n count(infection, time) %>%\n arrange(time)\n\n# A tibble: 3 × 3\n infection time n\n <chr> <dbl> <int>\n1 NonInfected 0 10318\n2 InfluenzaA 4 11792\n3 InfluenzaA 8 10318\n\n\nor by counts:\n\nrna %>%\n count(infection, time) %>%\n arrange(n)\n\n# A tibble: 3 × 3\n infection time n\n <chr> <dbl> <int>\n1 InfluenzaA 8 10318\n2 NonInfected 0 10318\n3 InfluenzaA 4 11792\n\n\nTo sort in descending order, we need to add the desc() function:\n\nrna %>%\n count(infection, time) %>%\n arrange(desc(n))\n\n# A tibble: 3 × 3\n infection time n\n <chr> <dbl> <int>\n1 InfluenzaA 4 11792\n2 InfluenzaA 8 10318\n3 NonInfected 0 10318\n\n\n\n\n\n\n\n\nChallenge\n\n\n\n\nHow many genes were analysed in each sample?\nUse group_by() and summarise() to evaluate the sequencing depth (the sum of all counts) in each sample. Which sample has the highest sequencing depth?\nPick one sample and evaluate the number of genes by biotype.\nIdentify genes associated with the “abnormal DNA methylation” phenotype description, and calculate their mean expression (in log) at time 0, time 4 and time 8.\n\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\n## 1.\nrna %>%\n count(sample)\n\n# A tibble: 22 × 2\n sample n\n <chr> <int>\n 1 GSM2545336 1474\n 2 GSM2545337 1474\n 3 GSM2545338 1474\n 4 GSM2545339 1474\n 5 GSM2545340 1474\n 6 GSM2545341 1474\n 7 GSM2545342 1474\n 8 GSM2545343 1474\n 9 GSM2545344 1474\n10 GSM2545345 1474\n# ℹ 12 more rows\n\n## 2.\nrna %>%\n group_by(sample) %>%\n summarise(seq_depth = sum(expression)) %>%\n arrange(desc(seq_depth))\n\n# A tibble: 22 × 2\n sample seq_depth\n <chr> <dbl>\n 1 GSM2545350 3255566\n 2 GSM2545352 3216163\n 3 GSM2545343 3105652\n 4 GSM2545336 3039671\n 5 GSM2545380 3036098\n 6 GSM2545353 2953249\n 7 GSM2545348 2913678\n 8 GSM2545362 2913517\n 9 GSM2545351 2782464\n10 GSM2545349 2758006\n# ℹ 12 more rows\n\n## 3.\nrna %>%\n filter(sample == \"GSM2545336\") %>%\n group_by(gene_biotype) %>%\n count(gene_biotype) %>%\n arrange(desc(n))\n\n# A tibble: 13 × 2\n# Groups: gene_biotype [13]\n gene_biotype n\n <chr> <int>\n 1 protein_coding 1321\n 2 lncRNA 69\n 3 processed_pseudogene 59\n 4 miRNA 7\n 5 snoRNA 5\n 6 TEC 4\n 7 polymorphic_pseudogene 2\n 8 unprocessed_pseudogene 2\n 9 IG_C_gene 1\n10 scaRNA 1\n11 transcribed_processed_pseudogene 1\n12 transcribed_unitary_pseudogene 1\n13 transcribed_unprocessed_pseudogene 1\n\n## 4.\nrna %>%\n filter(phenotype_description == \"abnormal DNA methylation\") %>%\n group_by(gene, time) %>%\n summarise(mean_expression = mean(log(expression))) %>%\n arrange()\n\n`summarise()` has grouped output by 'gene'. You can override using the\n`.groups` argument.\n\n\n# A tibble: 6 × 3\n# Groups: gene [2]\n gene time mean_expression\n <chr> <dbl> <dbl>\n1 Xist 0 6.95\n2 Xist 4 6.34\n3 Xist 8 7.13\n4 Zdbf2 0 6.27\n5 Zdbf2 4 6.27\n6 Zdbf2 8 6.19\n\n\n\n\n\n\nThe materials in this lesson have been adapted from work created by the HBC and Data Carpentry, as well as materials created by Laurent Gatto, Charlotte Soneson, Jenny Drnevich, Robert Castelo, and Kevin Rue-Albert. These are open access materials distributed under the terms of the Creative Commons Attribution license (CC BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.",
"crumbs": [
"In-Class Materials",
"Session 2",
"Split-Apply-Combine"
]
},
{
"objectID": "session-materials/session5/summarized_experiment.html",
"href": "session-materials/session5/summarized_experiment.html",
"title": "Working with summarized experimental data",
"section": "",
"text": "Working with summarized experimental data\nThis section introduces another broadly useful package and data structure, the SummarizedExperiment package and SummarizedExperiment object.\n\nThe SummarizedExperiment object has matrix-like properties – it has two dimensions and can be subset by ‘rows’ and ‘columns’. The assay() data of a SummarizedExperiment experiment contains one or more matrix-like objects where rows represent features of interest (e.g., genes), columns represent samples, and elements of the matrix represent results of a genomic assay (e.g., counts of reads overlaps genes in each sample of an bulk RNA-seq differential expression assay.\nObject construction\nThe SummarizedExperiment coordinates assays with (optional) descriptions of rows and columns. We start by reading in a simple data.frame describing 8 samples from an RNASeq experiment looking at dexamethasone treatment across 4 human smooth muscle cell lines; use browseVignettes(\"airway\") for a more complete description of the experiment and data processing. Read the column data in using file.choose() and read.csv().\n\n#fname <- file.choose() # airway_colData.csv\n#fname\n\nWe want the first column the the data to be treated as row names (sample identifiers) in the data.frame, so read.csv() has an extra argument to indicate this.\n\ncolData <- read.csv(fname, row.names = 1)\nhead(colData)\n\n SampleName cell dex albut Run avgLength Experiment\nSRR1039508 GSM1275862 N61311 untrt untrt SRR1039508 126 SRX384345\nSRR1039509 GSM1275863 N61311 trt untrt SRR1039509 126 SRX384346\nSRR1039512 GSM1275866 N052611 untrt untrt SRR1039512 126 SRX384349\nSRR1039513 GSM1275867 N052611 trt untrt SRR1039513 87 SRX384350\nSRR1039516 GSM1275870 N080611 untrt untrt SRR1039516 120 SRX384353\nSRR1039517 GSM1275871 N080611 trt untrt SRR1039517 126 SRX384354\n Sample BioSample\nSRR1039508 SRS508568 SAMN02422669\nSRR1039509 SRS508567 SAMN02422675\nSRR1039512 SRS508571 SAMN02422678\nSRR1039513 SRS508572 SAMN02422670\nSRR1039516 SRS508575 SAMN02422682\nSRR1039517 SRS508576 SAMN02422673\n\n\nThe data are from the Short Read Archive, and the row names, SampleName, Run, Experiment, Sampel, and BioSample columns are classifications from the archive. Additional columns include:\n\ncell: the cell line used. There are four cell lines.\ndex: whether the sample was untreated, or treated with dexamethasone.\nalbut: a second treatment, which we ignore\navgLength: the sample-specific average length of the RNAseq reads estimated in the experiment.\n\nAssay data\nNow import the assay data from the file “airway_counts.csv”\n\nfname <- file.choose() # airway_counts.csv\nfname\n\n\ncounts <- read.csv(fname, row.names=1)\n\nAlthough the data are read as a data.frame, all columns are of the same type (integer-valued) and represent the same attribute; the data is really a matrix rather than data.frame, so we coerce to matrix using as.matrix().\n\ncounts <- as.matrix(counts)\n\nWe see the dimensions and first few rows of the counts matrix\n\ndim(counts)\n\n[1] 33469 8\n\nhead(counts)\n\n SRR1039508 SRR1039509 SRR1039512 SRR1039513 SRR1039516\nENSG00000000003 679 448 873 408 1138\nENSG00000000419 467 515 621 365 587\nENSG00000000457 260 211 263 164 245\nENSG00000000460 60 55 40 35 78\nENSG00000000938 0 0 2 0 1\nENSG00000000971 3251 3679 6177 4252 6721\n SRR1039517 SRR1039520 SRR1039521\nENSG00000000003 1047 770 572\nENSG00000000419 799 417 508\nENSG00000000457 331 233 229\nENSG00000000460 63 76 60\nENSG00000000938 0 0 0\nENSG00000000971 11027 5176 7995\n\n\nIt’s interesting to think about what the counts mean – for ENSG00000000003, sample SRR1039508 had 679 reads that overlapped this gene, sample SRR1039509 had 448 reads, etc. Notice that for this gene there seems to be a consistent pattern – within a cell line, the read counts in the untreated group are always larger than the read counts for the treated group. This and other basic observations from ‘looking at’ the data motivate many steps in a rigorous RNASeq differential expression analysis.\nCreating a SummarizedExperiment object\nThere is considerable value in tightly coupling of the column data with the assay data, as it reduces the chances of bookkeeping errors as we work with our data.\nAttach the SummarizedExperiment library to our R session.\n\nlibrary(\"SummarizedExperiment\")\n\nUse the SummarizedExperiment() function to coordinate the assay and column data; this function uses row and column names to make sure the correct assay columns are described by the correct column data rows.\n\nse <- SummarizedExperiment(assay = list(count=counts), colData = colData)\nse\n\nclass: SummarizedExperiment \ndim: 33469 8 \nmetadata(0):\nassays(1): count\nrownames(33469): ENSG00000000003 ENSG00000000419 ... ENSG00000273492\n ENSG00000273493\nrowData names(0):\ncolnames(8): SRR1039508 SRR1039509 ... SRR1039520 SRR1039521\ncolData names(9): SampleName cell ... Sample BioSample\n\n\nIt is straight-forward to use subset() on SummarizedExperiment to create subsets of the data in a coordinated way. Remember that a SummarizedExperiment is conceptually two-dimensional (matrix-like), and in the example below we are subsetting on the second dimension.\n\nsubset(se, , dex == \"trt\")\n\nclass: SummarizedExperiment \ndim: 33469 4 \nmetadata(0):\nassays(1): count\nrownames(33469): ENSG00000000003 ENSG00000000419 ... ENSG00000273492\n ENSG00000273493\nrowData names(0):\ncolnames(4): SRR1039509 SRR1039513 SRR1039517 SRR1039521\ncolData names(9): SampleName cell ... Sample BioSample\n\n\nThere are also accessors that extract data from the SummarizedExperiment. For instance, we can use assay() to extract the count matrix, and colSums() to calculate the library size (total number of reads overlapping genes in each sample).\n\ncolSums(assay(se))\n\nSRR1039508 SRR1039509 SRR1039512 SRR1039513 SRR1039516 SRR1039517 SRR1039520 \n 20637971 18809481 25348649 15163415 24448408 30818215 19126151 \nSRR1039521 \n 21164133 \n\n\nNote that library sizes differ by a factor of 2 from largest to smallest; how would this influence the interpretation of counts in individual cells of the assay data?\nIt might be useful to remember important computations in a way that is robust, e.g.,\n\nse$lib.size <- colSums(assay(se))\ncolData(se)\n\nDataFrame with 8 rows and 10 columns\n SampleName cell dex albut Run\n <character> <character> <character> <character> <character>\nSRR1039508 GSM1275862 N61311 untrt untrt SRR1039508\nSRR1039509 GSM1275863 N61311 trt untrt SRR1039509\nSRR1039512 GSM1275866 N052611 untrt untrt SRR1039512\nSRR1039513 GSM1275867 N052611 trt untrt SRR1039513\nSRR1039516 GSM1275870 N080611 untrt untrt SRR1039516\nSRR1039517 GSM1275871 N080611 trt untrt SRR1039517\nSRR1039520 GSM1275874 N061011 untrt untrt SRR1039520\nSRR1039521 GSM1275875 N061011 trt untrt SRR1039521\n avgLength Experiment Sample BioSample lib.size\n <integer> <character> <character> <character> <numeric>\nSRR1039508 126 SRX384345 SRS508568 SAMN02422669 20637971\nSRR1039509 126 SRX384346 SRS508567 SAMN02422675 18809481\nSRR1039512 126 SRX384349 SRS508571 SAMN02422678 25348649\nSRR1039513 87 SRX384350 SRS508572 SAMN02422670 15163415\nSRR1039516 120 SRX384353 SRS508575 SAMN02422682 24448408\nSRR1039517 126 SRX384354 SRS508576 SAMN02422673 30818215\nSRR1039520 101 SRX384357 SRS508579 SAMN02422683 19126151\nSRR1039521 98 SRX384358 SRS508580 SAMN02422677 21164133\n\n\nExercises\n\n\n\n\n\n\nBasic\n\n\n\n\nSubset the SummarizedExperiment object se created above to genes (rows) with at least 8 reads mapped across all samples.\n\nHint: use rowSums.\n\nScale the read counts by library size, i.e. divide each column of assay(se) by the corresponding lib.size of each sample (column). Multiply the resulting scaled counts by 10^6 to obtain counts per million reads mapped.\n\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\n#1\n#Method 1\nrs <- rowSums(assay(se))\nse.sub <- subset(se, rs>7,)\n#Method 2\nind <- rowSums(assay(se)) >= 8\nse.sub <- se[ind,]\n#2\n# scale by library size\n# option 1: loop \nfor(i in 1:8)\n assay(se.sub)[,i] <- assay(se.sub)[,i] / se$lib.size[i]\nassay(se.sub)[1:5,1:5]\n\n SRR1039508 SRR1039509 SRR1039512 SRR1039513\nENSG00000000003 3.290052e-05 2.381778e-05 3.443971e-05 2.690687e-05\nENSG00000000419 2.262819e-05 2.737981e-05 2.449835e-05 2.407109e-05\nENSG00000000457 1.259814e-05 1.121775e-05 1.037531e-05 1.081551e-05\nENSG00000000460 2.907263e-06 2.924057e-06 1.577993e-06 2.308187e-06\nENSG00000000971 1.575252e-04 1.955929e-04 2.436816e-04 2.804118e-04\n SRR1039516\nENSG00000000003 4.654700e-05\nENSG00000000419 2.400974e-05\nENSG00000000457 1.002110e-05\nENSG00000000460 3.190392e-06\nENSG00000000971 2.749054e-04\n\n# option 2: vectorized\nse.sub <- se[ind,]\ntassay <- t(assay(se.sub)) / se$lib.size\nassay(se.sub) <- t(tassay)\nassay(se.sub)[1:5,1:5]\n\n SRR1039508 SRR1039509 SRR1039512 SRR1039513\nENSG00000000003 3.290052e-05 2.381778e-05 3.443971e-05 2.690687e-05\nENSG00000000419 2.262819e-05 2.737981e-05 2.449835e-05 2.407109e-05\nENSG00000000457 1.259814e-05 1.121775e-05 1.037531e-05 1.081551e-05\nENSG00000000460 2.907263e-06 2.924057e-06 1.577993e-06 2.308187e-06\nENSG00000000971 1.575252e-04 1.955929e-04 2.436816e-04 2.804118e-04\n SRR1039516\nENSG00000000003 4.654700e-05\nENSG00000000419 2.400974e-05\nENSG00000000457 1.002110e-05\nENSG00000000460 3.190392e-06\nENSG00000000971 2.749054e-04\n\n# counts per million reads mapped\nassay(se.sub) <- assay(se.sub) * 10^6\nassay(se.sub)[1:5,1:5]\n\n SRR1039508 SRR1039509 SRR1039512 SRR1039513 SRR1039516\nENSG00000000003 32.900521 23.817776 34.439705 26.906868 46.546998\nENSG00000000419 22.628193 27.379809 24.498347 24.071095 24.009743\nENSG00000000457 12.598138 11.217747 10.375306 10.815506 10.021102\nENSG00000000460 2.907263 2.924057 1.577993 2.308187 3.190392\nENSG00000000971 157.525175 195.592850 243.681626 280.411767 274.905425\n\nind <- rowSums(assay(se)) >= 8\nse.sub <- se[ind,]\nnrow(se.sub)\n\n[1] 23171\n\n\n\n\n\n\n\n\n\n\n\nAdvanced\n\n\n\nCarry out a t-test for each of the first 100 genes. Test for differences in mean read count per million reads mapped between the dexamethasone treated and untreated sample group. Annotate the resulting p-values as a new column in the rowData slot of your SummarizedExperiment.\nHint: use apply and t.test.\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\nse.sub <- se[1:100,]\n\n# logical index of treatment\ntrt <- se.sub$dex == \"trt\"\n\n# computing the p-value for a single gene (row)\ngetPValue <- function(row)\n{ \n tt <- t.test(row[trt], row[!trt]) \n p <- tt$p.value\n return(p)\n}\n\n# calculate t-test p-values for all genes\nps <- apply(assay(se.sub), 1, getPValue)\n\n# annotate to SE\nrowData(se.sub)$pvalue <- ps\nrowData(se.sub)\n\nDataFrame with 100 rows and 1 column\n pvalue\n <numeric>\nENSG00000000003 0.220827\nENSG00000000419 0.827554\nENSG00000000457 0.674747\nENSG00000000460 0.384125\nENSG00000000938 0.215170\n... ...\nENSG00000005486 0.588909\nENSG00000005513 0.144668\nENSG00000005700 0.559485\nENSG00000005801 0.181433\nENSG00000005810 0.656287\n\n\n\n\n\n\nThis lesson was adapted from materials created by Ludwig Geistlinger",
"crumbs": [
"In-Class Materials",
"Session 5",
"Working with summarized experimental data"
]
},
{
"objectID": "session-materials/session1/objects-and-functions.html",
"href": "session-materials/session1/objects-and-functions.html",
"title": "Variables and Functions",
"section": "",
"text": "You can get output from R simply by typing math in the console:\n\n3 + 5\n\n[1] 8\n\n12 / 7\n\n[1] 1.714286\n\n\nHowever, to do useful and interesting things, we need to assign values to objects. To create an object, we need to give it a name followed by the assignment operator <-, and the value we want to give it:\n\nweight_kg <- 55\n\n<- is the assignment operator. It assigns values on the right to objects on the left. So, after executing x <- 3, the value of x is 3. The arrow can be read as 3 goes into x. For historical reasons, you can also use = for assignments, but not in every context. Because of the slight differences in syntax, it is good practice to always use <- for assignments.\nIn RStudio, typing Alt + - (push Alt at the same time as the - key) will write <- in a single keystroke in a PC, while typing Option + - (push Option at the same time as the - key) does the same in a Mac.\n\n\nObjects can be given any name such as x, current_temperature, or subject_id. You want your object names to be explicit and not too long. They cannot start with a number (2x is not valid, but x2 is). R is case sensitive (e.g., weight_kg is different from Weight_kg). There are some names that cannot be used because they are the names of fundamental functions in R (e.g., if, else, for, see here for a complete list). In general, even if it’s allowed, it’s best to not use other function names (e.g., c, T, mean, data, df, weights). If in doubt, check the help to see if the name is already in use. It’s also best to avoid dots (.) within an object name as in my.dataset. There are many functions in R with dots in their names for historical reasons, but because dots have a special meaning in R (for methods) and other programming languages, it’s best to avoid them. It is also recommended to use nouns for object names, and verbs for function names. It’s important to be consistent in the styling of your code (where you put spaces, how you name objects, etc.). Using a consistent coding style makes your code clearer to read for your future self and your collaborators. In R, some popular style guides are Google’s, the tidyverse’s style and the Bioconductor style guide. The tidyverse’s is very comprehensive and may seem overwhelming at first. You can install the lintr package to automatically check for issues in the styling of your code.\n\nObjects vs. variables: What are known as objects in R are known as variables in many other programming languages. Depending on the context, object and variable can have drastically different meanings. However, in this lesson, the two words are used synonymously. For more information see here.\n\nWhen assigning a value to an object, R does not print anything. You can force R to print the value by using parentheses or by typing the object name:\n\nweight_kg <- 55 # doesn't print anything\n(weight_kg <- 55) # but putting parenthesis around the call prints the value of `weight_kg`\n\n[1] 55\n\nweight_kg # and so does typing the name of the object\n\n[1] 55\n\n\nNow that R has weight_kg in memory, we can do arithmetic with it. For instance, we may want to convert this weight into pounds (weight in pounds is 2.2 times the weight in kg):\n\n2.2 * weight_kg\n\n[1] 121\n\n\nWe can also change an object’s value by assigning it a new one:\n\nweight_kg <- 57.5\n2.2 * weight_kg\n\n[1] 126.5\n\n\nThis means that assigning a value to one object does not change the values of other objects For example, let’s store the animal’s weight in pounds in a new object, weight_lb:\n\nweight_lb <- 2.2 * weight_kg\n\nand then change weight_kg to 100.\n\nweight_kg <- 100\n\n\n\n\n\n\n\nChallenge:\n\n\n\nWhat do you think is the current content of the object weight_lb? 126.5 or 220?",
"crumbs": [
"In-Class Materials",
"Session 1",
"Variables and Functions"
]
},
{
"objectID": "session-materials/session1/objects-and-functions.html#creating-objects-in-r",
"href": "session-materials/session1/objects-and-functions.html#creating-objects-in-r",
"title": "Variables and Functions",
"section": "",
"text": "You can get output from R simply by typing math in the console:\n\n3 + 5\n\n[1] 8\n\n12 / 7\n\n[1] 1.714286\n\n\nHowever, to do useful and interesting things, we need to assign values to objects. To create an object, we need to give it a name followed by the assignment operator <-, and the value we want to give it:\n\nweight_kg <- 55\n\n<- is the assignment operator. It assigns values on the right to objects on the left. So, after executing x <- 3, the value of x is 3. The arrow can be read as 3 goes into x. For historical reasons, you can also use = for assignments, but not in every context. Because of the slight differences in syntax, it is good practice to always use <- for assignments.\nIn RStudio, typing Alt + - (push Alt at the same time as the - key) will write <- in a single keystroke in a PC, while typing Option + - (push Option at the same time as the - key) does the same in a Mac.\n\n\nObjects can be given any name such as x, current_temperature, or subject_id. You want your object names to be explicit and not too long. They cannot start with a number (2x is not valid, but x2 is). R is case sensitive (e.g., weight_kg is different from Weight_kg). There are some names that cannot be used because they are the names of fundamental functions in R (e.g., if, else, for, see here for a complete list). In general, even if it’s allowed, it’s best to not use other function names (e.g., c, T, mean, data, df, weights). If in doubt, check the help to see if the name is already in use. It’s also best to avoid dots (.) within an object name as in my.dataset. There are many functions in R with dots in their names for historical reasons, but because dots have a special meaning in R (for methods) and other programming languages, it’s best to avoid them. It is also recommended to use nouns for object names, and verbs for function names. It’s important to be consistent in the styling of your code (where you put spaces, how you name objects, etc.). Using a consistent coding style makes your code clearer to read for your future self and your collaborators. In R, some popular style guides are Google’s, the tidyverse’s style and the Bioconductor style guide. The tidyverse’s is very comprehensive and may seem overwhelming at first. You can install the lintr package to automatically check for issues in the styling of your code.\n\nObjects vs. variables: What are known as objects in R are known as variables in many other programming languages. Depending on the context, object and variable can have drastically different meanings. However, in this lesson, the two words are used synonymously. For more information see here.\n\nWhen assigning a value to an object, R does not print anything. You can force R to print the value by using parentheses or by typing the object name:\n\nweight_kg <- 55 # doesn't print anything\n(weight_kg <- 55) # but putting parenthesis around the call prints the value of `weight_kg`\n\n[1] 55\n\nweight_kg # and so does typing the name of the object\n\n[1] 55\n\n\nNow that R has weight_kg in memory, we can do arithmetic with it. For instance, we may want to convert this weight into pounds (weight in pounds is 2.2 times the weight in kg):\n\n2.2 * weight_kg\n\n[1] 121\n\n\nWe can also change an object’s value by assigning it a new one:\n\nweight_kg <- 57.5\n2.2 * weight_kg\n\n[1] 126.5\n\n\nThis means that assigning a value to one object does not change the values of other objects For example, let’s store the animal’s weight in pounds in a new object, weight_lb:\n\nweight_lb <- 2.2 * weight_kg\n\nand then change weight_kg to 100.\n\nweight_kg <- 100\n\n\n\n\n\n\n\nChallenge:\n\n\n\nWhat do you think is the current content of the object weight_lb? 126.5 or 220?",
"crumbs": [
"In-Class Materials",
"Session 1",
"Variables and Functions"
]
},
{
"objectID": "session-materials/session1/objects-and-functions.html#comments",
"href": "session-materials/session1/objects-and-functions.html#comments",
"title": "Variables and Functions",
"section": "Comments",
"text": "Comments\nThe comment character in R is #, anything to the right of a # in a script will be ignored by R. It is useful to leave notes, and explanations in your scripts.\nRStudio makes it easy to comment or uncomment a paragraph: after selecting the lines you want to comment, press at the same time on your keyboard Ctrl + Shift + C. If you only want to comment out one line, you can put the cursor at any location of that line (i.e. no need to select the whole line), then press Ctrl + Shift + C.\n\n\n\n\n\n\nChallenge\n\n\n\nWhat are the values after each statement in the following?\n\nmass <- 47.5 # mass?\nage <- 122 # age?\nmass <- mass * 2.0 # mass?\nage <- age - 20 # age?\nmass_index <- mass/age # mass_index?",
"crumbs": [
"In-Class Materials",
"Session 1",
"Variables and Functions"
]
},
{
"objectID": "session-materials/session1/objects-and-functions.html#functions-and-their-arguments",
"href": "session-materials/session1/objects-and-functions.html#functions-and-their-arguments",
"title": "Variables and Functions",
"section": "Functions and their arguments",
"text": "Functions and their arguments\nFunctions are “canned scripts” that automate more complicated sets of commands including operations assignments, etc. Many functions are predefined, or can be made available by importing R packages (more on that later). A function usually gets one or more inputs called arguments. Functions often (but not always) return a value. A typical example would be the function sqrt(). The input (the argument) must be a number, and the return value (in fact, the output) is the square root of that number. Executing a function (‘running it’) is called calling the function. An example of a function call is:\n\nb <- sqrt(a)\n\nHere, the value of a is given to the sqrt() function, the sqrt() function calculates the square root, and returns the value which is then assigned to the object b. This function is very simple, because it takes just one argument.\nThe return ‘value’ of a function need not be numerical (like that of sqrt()), and it also does not need to be a single item: it can be a set of things, or even a dataset. We’ll see that when we read data files into R.\nArguments can be anything, not only numbers or filenames, but also other objects. Exactly what each argument means differs per function, and must be looked up in the documentation (see below). Some functions take arguments which may either be specified by the user, or, if left out, take on a default value: these are called options. Options are typically used to alter the way the function operates, such as whether it ignores ‘bad values’, or what symbol to use in a plot. However, if you want something specific, you can specify a value of your choice which will be used instead of the default.\nLet’s try a function that can take multiple arguments: round().\n\nround(3.14159)\n\n[1] 3\n\n\nHere, we’ve called round() with just one argument, 3.14159, and it has returned the value 3. That’s because the default is to round to the nearest whole number. If we want more digits we can see how to do that by getting information about the round function. We can use args(round) or look at the help for this function using ?round.\n\nargs(round)\n\nfunction (x, digits = 0, ...) \nNULL\n\n\n\n?round\n\nWe see that if we want a different number of digits, we can type digits=2 or however many we want.\n\nround(3.14159, digits = 2)\n\n[1] 3.14\n\n\nIf you provide the arguments in the exact same order as they are defined you don’t have to name them:\n\nround(3.14159, 2)\n\n[1] 3.14\n\n\nAnd if you do name the arguments, you can switch their order:\n\nround(digits = 2, x = 3.14159)\n\n[1] 3.14\n\n\nIt’s good practice to put the non-optional arguments (like the number you’re rounding) first in your function call, and to specify the names of all optional arguments. If you don’t, someone reading your code might have to look up the definition of a function with unfamiliar arguments to understand what you’re doing. By specifying the name of the arguments you are also safeguarding against possible future changes in the function interface, which may potentially add new arguments in between the existing ones.\n\nThe materials in this lesson have been adapted from work created by the HBC and Data Carpentry, as well as materials created by Laurent Gatto, Charlotte Soneson, Jenny Drnevich, Robert Castelo, and Kevin Rue-Albert. These are open access materials distributed under the terms of the Creative Commons Attribution license (CC BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.",
"crumbs": [
"In-Class Materials",
"Session 1",
"Variables and Functions"
]
},
{
"objectID": "session-materials/session1/vectors-and-conditionals.html",
"href": "session-materials/session1/vectors-and-conditionals.html",
"title": "Vectors, Types, and Conditional Statements",
"section": "",
"text": "A vector is the most common and basic data type in R, and is pretty much the workhorse of R. A vector is composed by a series of values, such as numbers or characters. We can assign a series of values to a vector using the c() function. For example we can create a vector of animal weights and assign it to a new object weight_g:\n\nweight_g <- c(50, 60, 65, 82)\nweight_g\n\n[1] 50 60 65 82\n\n\nA vector can also contain characters:\n\nmolecules <- c(\"dna\", \"rna\", \"protein\")\nmolecules\n\n[1] \"dna\" \"rna\" \"protein\"\n\n\nThe quotes around “dna”, “rna”, etc. are essential here. Without the quotes R will assume there are objects called dna, rna and protein. As these objects don’t exist in R’s memory, there will be an error message.\nThere are many functions that allow you to inspect the content of a vector. length() tells you how many elements are in a particular vector:\n\nlength(weight_g)\n\n[1] 4\n\nlength(molecules)\n\n[1] 3\n\n\nAn important feature of a vector, is that all of the elements are the same type of data. The function class() indicates the class (the type of element) of an object:\n\nclass(weight_g)\n\n[1] \"numeric\"\n\nclass(molecules)\n\n[1] \"character\"\n\n\nThe function str() provides an overview of the structure of an object and its elements. It is a useful function when working with large and complex objects:\n\nstr(weight_g)\n\n num [1:4] 50 60 65 82\n\nstr(molecules)\n\n chr [1:3] \"dna\" \"rna\" \"protein\"\n\n\nYou can use the c() function to add other elements to your vector:\n\nweight_g <- c(weight_g, 90) # add to the end of the vector\nweight_g <- c(30, weight_g) # add to the beginning of the vector\nweight_g\n\n[1] 30 50 60 65 82 90\n\n\nIn the first line, we take the original vector weight_g, add the value 90 to the end of it, and save the result back into weight_g. Then we add the value 30 to the beginning, again saving the result back into weight_g.\nWe can do this over and over again to grow a vector, or assemble a dataset. As we program, this may be useful to add results that we are collecting or calculating.\nAn atomic vector is the simplest R data type and is a linear vector of a single type. Above, we saw 2 of the 6 main atomic vector types that R uses: \"character\" and \"numeric\" (or \"double\"). These are the basic building blocks that all R objects are built from. The other 4 atomic vector types are:\n\n\"logical\" for TRUE and FALSE (the boolean data type)\n\"integer\" for integer numbers (e.g., 2L, the L indicates to R that it’s an integer)\n\"complex\" to represent complex numbers with real and imaginary parts (e.g., 1 + 4i) and that’s all we’re going to say about them\n\"raw\" for bitstreams that we won’t discuss further\n\nYou can check the type of your vector using the typeof() function and inputting your vector as the argument.\nVectors are one of the many data structures that R uses. Other important ones are lists (list), matrices (matrix), data frames (data.frame), factors (factor) and arrays (array).\n\n\n\n\n\n\nChallenge:\n\n\n\nWe’ve seen that atomic vectors can be of type character, numeric (or double), integer, and logical. But what happens if we try to mix these types in a single vector?\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\nR implicitly converts them to all be the same type\n\n\n\n\n\n\n\n\n\nChallenge:\n\n\n\nWhat will happen in each of these examples? (hint: use class() to check the data type of your objects and type in their names to see what happens):\n\nnum_char <- c(1, 2, 3, \"a\")\nnum_logical <- c(1, 2, 3, TRUE, FALSE)\nchar_logical <- c(\"a\", \"b\", \"c\", TRUE)\ntricky <- c(1, 2, 3, \"4\")\n\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\nclass(num_char)\n\n[1] \"character\"\n\nnum_char\n\n[1] \"1\" \"2\" \"3\" \"a\"\n\nclass(num_logical)\n\n[1] \"numeric\"\n\nnum_logical\n\n[1] 1 2 3 1 0\n\nclass(char_logical)\n\n[1] \"character\"\n\nchar_logical\n\n[1] \"a\" \"b\" \"c\" \"TRUE\"\n\nclass(tricky)\n\n[1] \"character\"\n\ntricky\n\n[1] \"1\" \"2\" \"3\" \"4\"\n\n\n\n\n\n\n\n\n\n\n\nChallenge:\n\n\n\nWhy do you think it happens?\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\nVectors can be of only one data type. R tries to convert (coerce) the content of this vector to find a common denominator that doesn’t lose any information.\n\n\n\n\n\n\n\n\n\nChallenge:\n\n\n\nHow many values in combined_logical are \"TRUE\" (as a character) in the following example:\n\nnum_logical <- c(1, 2, 3, TRUE)\nchar_logical <- c(\"a\", \"b\", \"c\", TRUE)\ncombined_logical <- c(num_logical, char_logical)\n\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\nOnly one. There is no memory of past data types, and the coercion happens the first time the vector is evaluated. Therefore, the TRUE in num_logical gets converted into a 1 before it gets converted into \"1\" in combined_logical.\n\ncombined_logical\n\n[1] \"1\" \"2\" \"3\" \"1\" \"a\" \"b\" \"c\" \"TRUE\"\n\n\n\n\n\n\n\n\n\n\n\nChallenge:\n\n\n\nIn R, we call converting objects from one class into another class coercion. These conversions happen according to a hierarchy, whereby some types get preferentially coerced into other types. Can you draw a diagram that represents the hierarchy of how these data types are coerced?\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\nlogical → numeric → character ← logical",
"crumbs": [
"In-Class Materials",
"Session 1",
"Vectors, Types, and Conditional Statements"
]
},
{
"objectID": "session-materials/session1/vectors-and-conditionals.html#vectors-and-data-types",
"href": "session-materials/session1/vectors-and-conditionals.html#vectors-and-data-types",
"title": "Vectors, Types, and Conditional Statements",
"section": "",
"text": "A vector is the most common and basic data type in R, and is pretty much the workhorse of R. A vector is composed by a series of values, such as numbers or characters. We can assign a series of values to a vector using the c() function. For example we can create a vector of animal weights and assign it to a new object weight_g:\n\nweight_g <- c(50, 60, 65, 82)\nweight_g\n\n[1] 50 60 65 82\n\n\nA vector can also contain characters:\n\nmolecules <- c(\"dna\", \"rna\", \"protein\")\nmolecules\n\n[1] \"dna\" \"rna\" \"protein\"\n\n\nThe quotes around “dna”, “rna”, etc. are essential here. Without the quotes R will assume there are objects called dna, rna and protein. As these objects don’t exist in R’s memory, there will be an error message.\nThere are many functions that allow you to inspect the content of a vector. length() tells you how many elements are in a particular vector:\n\nlength(weight_g)\n\n[1] 4\n\nlength(molecules)\n\n[1] 3\n\n\nAn important feature of a vector, is that all of the elements are the same type of data. The function class() indicates the class (the type of element) of an object:\n\nclass(weight_g)\n\n[1] \"numeric\"\n\nclass(molecules)\n\n[1] \"character\"\n\n\nThe function str() provides an overview of the structure of an object and its elements. It is a useful function when working with large and complex objects:\n\nstr(weight_g)\n\n num [1:4] 50 60 65 82\n\nstr(molecules)\n\n chr [1:3] \"dna\" \"rna\" \"protein\"\n\n\nYou can use the c() function to add other elements to your vector:\n\nweight_g <- c(weight_g, 90) # add to the end of the vector\nweight_g <- c(30, weight_g) # add to the beginning of the vector\nweight_g\n\n[1] 30 50 60 65 82 90\n\n\nIn the first line, we take the original vector weight_g, add the value 90 to the end of it, and save the result back into weight_g. Then we add the value 30 to the beginning, again saving the result back into weight_g.\nWe can do this over and over again to grow a vector, or assemble a dataset. As we program, this may be useful to add results that we are collecting or calculating.\nAn atomic vector is the simplest R data type and is a linear vector of a single type. Above, we saw 2 of the 6 main atomic vector types that R uses: \"character\" and \"numeric\" (or \"double\"). These are the basic building blocks that all R objects are built from. The other 4 atomic vector types are:\n\n\"logical\" for TRUE and FALSE (the boolean data type)\n\"integer\" for integer numbers (e.g., 2L, the L indicates to R that it’s an integer)\n\"complex\" to represent complex numbers with real and imaginary parts (e.g., 1 + 4i) and that’s all we’re going to say about them\n\"raw\" for bitstreams that we won’t discuss further\n\nYou can check the type of your vector using the typeof() function and inputting your vector as the argument.\nVectors are one of the many data structures that R uses. Other important ones are lists (list), matrices (matrix), data frames (data.frame), factors (factor) and arrays (array).\n\n\n\n\n\n\nChallenge:\n\n\n\nWe’ve seen that atomic vectors can be of type character, numeric (or double), integer, and logical. But what happens if we try to mix these types in a single vector?\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\nR implicitly converts them to all be the same type\n\n\n\n\n\n\n\n\n\nChallenge:\n\n\n\nWhat will happen in each of these examples? (hint: use class() to check the data type of your objects and type in their names to see what happens):\n\nnum_char <- c(1, 2, 3, \"a\")\nnum_logical <- c(1, 2, 3, TRUE, FALSE)\nchar_logical <- c(\"a\", \"b\", \"c\", TRUE)\ntricky <- c(1, 2, 3, \"4\")\n\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\nclass(num_char)\n\n[1] \"character\"\n\nnum_char\n\n[1] \"1\" \"2\" \"3\" \"a\"\n\nclass(num_logical)\n\n[1] \"numeric\"\n\nnum_logical\n\n[1] 1 2 3 1 0\n\nclass(char_logical)\n\n[1] \"character\"\n\nchar_logical\n\n[1] \"a\" \"b\" \"c\" \"TRUE\"\n\nclass(tricky)\n\n[1] \"character\"\n\ntricky\n\n[1] \"1\" \"2\" \"3\" \"4\"\n\n\n\n\n\n\n\n\n\n\n\nChallenge:\n\n\n\nWhy do you think it happens?\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\nVectors can be of only one data type. R tries to convert (coerce) the content of this vector to find a common denominator that doesn’t lose any information.\n\n\n\n\n\n\n\n\n\nChallenge:\n\n\n\nHow many values in combined_logical are \"TRUE\" (as a character) in the following example:\n\nnum_logical <- c(1, 2, 3, TRUE)\nchar_logical <- c(\"a\", \"b\", \"c\", TRUE)\ncombined_logical <- c(num_logical, char_logical)\n\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\nOnly one. There is no memory of past data types, and the coercion happens the first time the vector is evaluated. Therefore, the TRUE in num_logical gets converted into a 1 before it gets converted into \"1\" in combined_logical.\n\ncombined_logical\n\n[1] \"1\" \"2\" \"3\" \"1\" \"a\" \"b\" \"c\" \"TRUE\"\n\n\n\n\n\n\n\n\n\n\n\nChallenge:\n\n\n\nIn R, we call converting objects from one class into another class coercion. These conversions happen according to a hierarchy, whereby some types get preferentially coerced into other types. Can you draw a diagram that represents the hierarchy of how these data types are coerced?\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\nlogical → numeric → character ← logical",
"crumbs": [
"In-Class Materials",
"Session 1",
"Vectors, Types, and Conditional Statements"
]
},
{
"objectID": "session-materials/session1/vectors-and-conditionals.html#subsetting-vectors",
"href": "session-materials/session1/vectors-and-conditionals.html#subsetting-vectors",
"title": "Vectors, Types, and Conditional Statements",
"section": "Subsetting vectors",
"text": "Subsetting vectors\nIf we want to extract one or several values from a vector, we must provide one or several indices in square brackets. For instance:\n\nmolecules <- c(\"dna\", \"rna\", \"peptide\", \"protein\")\nmolecules[2]\n\n[1] \"rna\"\n\nmolecules[c(3, 2)]\n\n[1] \"peptide\" \"rna\" \n\n\nWe can also repeat the indices to create an object with more elements than the original one:\n\nmore_molecules <- molecules[c(1, 2, 3, 2, 1, 4)]\nmore_molecules\n\n[1] \"dna\" \"rna\" \"peptide\" \"rna\" \"dna\" \"protein\"\n\n\nR indices start at 1. Programming languages like Fortran, MATLAB, Julia, and R start counting at 1, because that’s what human beings typically do. Languages in the C family (including C++, Java, Perl, and Python) count from 0 because that’s simpler for computers to do.\nFinally, it is also possible to get all the elements of a vector except some specified elements using negative indices:\n\nmolecules ## all molecules\n\n[1] \"dna\" \"rna\" \"peptide\" \"protein\"\n\nmolecules[-1] ## all but the first one\n\n[1] \"rna\" \"peptide\" \"protein\"\n\nmolecules[-c(1, 3)] ## all but 1st/3rd ones\n\n[1] \"rna\" \"protein\"\n\nmolecules[c(-1, -3)] ## all but 1st/3rd ones\n\n[1] \"rna\" \"protein\"",
"crumbs": [
"In-Class Materials",
"Session 1",
"Vectors, Types, and Conditional Statements"
]
},
{
"objectID": "session-materials/session1/vectors-and-conditionals.html#conditional-subsetting",
"href": "session-materials/session1/vectors-and-conditionals.html#conditional-subsetting",
"title": "Vectors, Types, and Conditional Statements",
"section": "Conditional subsetting",
"text": "Conditional subsetting\nAnother common way of subsetting is by using a logical vector. TRUE will select the element with the same index, while FALSE will not:\n\nweight_g <- c(21, 34, 39, 54, 55)\nweight_g[c(TRUE, FALSE, TRUE, TRUE, FALSE)]\n\n[1] 21 39 54\n\n\nTypically, these logical vectors are not typed by hand, but are the output of other functions or logical tests. For instance, if you wanted to select only the values above 50:\n\n## will return logicals with TRUE for the indices that meet\n## the condition\nweight_g > 50\n\n[1] FALSE FALSE FALSE TRUE TRUE\n\n## so we can use this to select only the values above 50\nweight_g[weight_g > 50]\n\n[1] 54 55\n\n\nYou can combine multiple tests using & (both conditions are true, AND) or | (at least one of the conditions is true, OR):\n\nweight_g[weight_g < 30 | weight_g > 50]\n\n[1] 21 54 55\n\nweight_g[weight_g >= 30 & weight_g == 21]\n\nnumeric(0)\n\n\nHere, < stands for “less than”, > for “greater than”, >= for “greater than or equal to”, and == for “equal to”. The double equal sign == is a test for numerical equality between the left and right hand sides, and should not be confused with the single = sign, which performs variable assignment (similar to <-).\nA common task is to search for certain strings in a vector. One could use the “or” operator | to test for equality to multiple values, but this can quickly become tedious. The function %in% allows you to test if any of the elements of a search vector are found:\n\nmolecules <- c(\"dna\", \"rna\", \"protein\", \"peptide\")\nmolecules[molecules == \"rna\" | molecules == \"dna\"] # returns both rna and dna\n\n[1] \"dna\" \"rna\"\n\nmolecules %in% c(\"rna\", \"dna\", \"metabolite\", \"peptide\", \"glycerol\")\n\n[1] TRUE TRUE FALSE TRUE\n\nmolecules[molecules %in% c(\"rna\", \"dna\", \"metabolite\", \"peptide\", \"glycerol\")]\n\n[1] \"dna\" \"rna\" \"peptide\"\n\n\n\n\n\n\n\n\nChallenge:\n\n\n\nCan you figure out why \"four\" > \"five\" returns TRUE?\n\n\n\n\n\n\n\n\nSolution\n\n\n\n\n\n\n\"four\" > \"five\"\n\n[1] TRUE\n\n\nWhen using > or < on strings, R compares their alphabetical order. Here \"four\" comes after \"five\", and therefore is greater than it.",
"crumbs": [
"In-Class Materials",
"Session 1",
"Vectors, Types, and Conditional Statements"
]
},
{
"objectID": "session-materials/session1/vectors-and-conditionals.html#names",
"href": "session-materials/session1/vectors-and-conditionals.html#names",
"title": "Vectors, Types, and Conditional Statements",
"section": "Names",
"text": "Names\nIt is possible to name each element of a vector. The code chunk below shows an initial vector without any names, how names are set, and retrieved.\n\nx <- c(1, 5, 3, 5, 10)\nnames(x) ## no names\n\nNULL\n\nnames(x) <- c(\"A\", \"B\", \"C\", \"D\", \"E\")\nnames(x) ## now we have names\n\n[1] \"A\" \"B\" \"C\" \"D\" \"E\"\n\n\nWhen a vector has names, it is possible to access elements by their name, in addition to their index.\n\nx[c(1, 3)]\n\nA C \n1 3 \n\nx[c(\"A\", \"C\")]\n\nA C \n1 3 \n\n\n\nThe materials in this lesson have been adapted from work created by the HBC and Data Carpentry, as well as materials created by Laurent Gatto, Charlotte Soneson, Jenny Drnevich, Robert Castelo, and Kevin Rue-Albert. These are open access materials distributed under the terms of the Creative Commons Attribution license (CC BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.",
"crumbs": [
"In-Class Materials",
"Session 1",
"Vectors, Types, and Conditional Statements"
]
},
{
"objectID": "LICENSE.html",
"href": "LICENSE.html",
"title": "Licenses",
"section": "",
"text": "All Software Carpentry, Data Carpentry, and Library Carpentry instructional material is made available under the Creative Commons Attribution license. The following is a human-readable summary of (and not a substitute for) the full legal text of the CC BY 4.0 license.\nYou are free:\n\nto Share—copy and redistribute the material in any medium or format\nto Adapt—remix, transform, and build upon the material\n\nfor any purpose, even commercially.\nThe licensor cannot revoke these freedoms as long as you follow the license terms.\nUnder the following terms:\n\nAttribution—You must give appropriate credit (mentioning that your work is derived from work that is Copyright © Software Carpentry and, where practical, linking to http://software-carpentry.org/), provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.\n\nNo additional restrictions—You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. With the understanding that:\nNotices:\n\nYou do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation.\nNo warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material."
},
{
"objectID": "LICENSE.html#instructional-material",
"href": "LICENSE.html#instructional-material",
"title": "Licenses",
"section": "",
"text": "All Software Carpentry, Data Carpentry, and Library Carpentry instructional material is made available under the Creative Commons Attribution license. The following is a human-readable summary of (and not a substitute for) the full legal text of the CC BY 4.0 license.\nYou are free:\n\nto Share—copy and redistribute the material in any medium or format\nto Adapt—remix, transform, and build upon the material\n\nfor any purpose, even commercially.\nThe licensor cannot revoke these freedoms as long as you follow the license terms.\nUnder the following terms:\n\nAttribution—You must give appropriate credit (mentioning that your work is derived from work that is Copyright © Software Carpentry and, where practical, linking to http://software-carpentry.org/), provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.\n\nNo additional restrictions—You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. With the understanding that:\nNotices:\n\nYou do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation.\nNo warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material."
},
{
"objectID": "LICENSE.html#software",
"href": "LICENSE.html#software",
"title": "Licenses",
"section": "Software",
"text": "Software\nExcept where otherwise noted, the example programs and other software provided by Software Carpentry and Data Carpentry are made available under the OSI-approved MIT license.\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
},
{
"objectID": "LICENSE.html#trademark",
"href": "LICENSE.html#trademark",
"title": "Licenses",
"section": "Trademark",
"text": "Trademark\n“Software Carpentry” and “Data Carpentry” and their respective logos are registered trademarks of Community Initiatives."
},
{
"objectID": "resources/install.html",
"href": "resources/install.html",
"title": "Installing R and RStudio",
"section": "",
"text": "Open an internet browser and go to www.r-project.org.\nClick the “download R” link in the middle of the page under “Getting Started.”\nSelect a CRAN location (a mirror site) and click the corresponding link.\nClick on the “Download R for (Mac) OS X” link at the top of the page.\nClick on the file containing the latest version of R under “Files.”\nSave the .pkg file, double-click it to open, and follow the installation instructions.\nNow that R is installed, you need to download and install RStudio.\n\n\n\n\n\nGo to www.rstudio.com and click on the “Download RStudio” button.\nClick on “DOWNLOAD” in the upper right corner.\nDownload the Free version of RStudio Desktop.\nSave the .dmg file on your computer, double-click it to open, and then drag and drop it to your applications folder.\n\n\n\n\n\n\n\n\nOpen an internet browser and go to www.r-project.org.\nClick the “download R” link in the middle of the page under “Getting Started.”\nSelect a CRAN location (a mirror site) and click the corresponding link.\nClick on the “Download R for Windows” link at the top of the page.\nClick on the “install R for the first time” link at the top of the page.\nClick “Download R for Windows” and save the executable file somewhere on your computer. Run the .exe file and follow the installation instructions.\nNow that R is installed, you need to download and install RStudio.\n\n\n\n\n\nGo to www.rstudio.com and click on the “Download RStudio” button.\nClick on “DOWNLOAD” in the upper right corner.\nDownload the Free version of RStudio Desktop.\nSave the executable file. Run the .exe file and follow the installation instructions.\n\nPermissions\nNote that you should install R and RStudio to a drive where you have read and write permissions. Otherwise, your ability to install R packages (a frequent occurrence) will be impacted. If you encounter problems, try opening RStudio by right-clicking the icon and selecting “Run as administrator”. Other tips can be found in the page [R on network drives].\nHow to update R and RStudio\nYour version of R is printed to the R Console at start-up. You can also run sessionInfo().\nTo update R, go to the website mentioned above and re-install R. Be aware that the old R version will still exist in your computer. You can temporarily run an older version (older “installation”) of R by clicking “Tools” -> “Global Options” in RStudio and choosing an R version. This can be useful if you want to use a package that has not been updated to work on the newest version of R.\nTo update RStudio, you can go to the website above and re-download RStudio. Another option is to click “Help” -> “Check for Updates” within RStudio, but this may not show the very latest updates.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nInstructions adapted from guide developed by HMS Research computing and Chapter 3 of the The Epidemiologist R Handbook.",
"crumbs": [
"Resources",
"Installing R and RStudio"
]
},
{
"objectID": "resources/install.html#mac-users",
"href": "resources/install.html#mac-users",
"title": "Installing R and RStudio",
"section": "",
"text": "Open an internet browser and go to www.r-project.org.\nClick the “download R” link in the middle of the page under “Getting Started.”\nSelect a CRAN location (a mirror site) and click the corresponding link.\nClick on the “Download R for (Mac) OS X” link at the top of the page.\nClick on the file containing the latest version of R under “Files.”\nSave the .pkg file, double-click it to open, and follow the installation instructions.\nNow that R is installed, you need to download and install RStudio.\n\n\n\n\n\nGo to www.rstudio.com and click on the “Download RStudio” button.\nClick on “DOWNLOAD” in the upper right corner.\nDownload the Free version of RStudio Desktop.\nSave the .dmg file on your computer, double-click it to open, and then drag and drop it to your applications folder.",
"crumbs": [
"Resources",
"Installing R and RStudio"
]
},
{
"objectID": "resources/install.html#windows-users",
"href": "resources/install.html#windows-users",
"title": "Installing R and RStudio",
"section": "",
"text": "Open an internet browser and go to www.r-project.org.\nClick the “download R” link in the middle of the page under “Getting Started.”\nSelect a CRAN location (a mirror site) and click the corresponding link.\nClick on the “Download R for Windows” link at the top of the page.\nClick on the “install R for the first time” link at the top of the page.\nClick “Download R for Windows” and save the executable file somewhere on your computer. Run the .exe file and follow the installation instructions.\nNow that R is installed, you need to download and install RStudio.\n\n\n\n\n\nGo to www.rstudio.com and click on the “Download RStudio” button.\nClick on “DOWNLOAD” in the upper right corner.\nDownload the Free version of RStudio Desktop.\nSave the executable file. Run the .exe file and follow the installation instructions.\n\nPermissions\nNote that you should install R and RStudio to a drive where you have read and write permissions. Otherwise, your ability to install R packages (a frequent occurrence) will be impacted. If you encounter problems, try opening RStudio by right-clicking the icon and selecting “Run as administrator”. Other tips can be found in the page [R on network drives].\nHow to update R and RStudio\nYour version of R is printed to the R Console at start-up. You can also run sessionInfo().\nTo update R, go to the website mentioned above and re-install R. Be aware that the old R version will still exist in your computer. You can temporarily run an older version (older “installation”) of R by clicking “Tools” -> “Global Options” in RStudio and choosing an R version. This can be useful if you want to use a package that has not been updated to work on the newest version of R.\nTo update RStudio, you can go to the website above and re-download RStudio. Another option is to click “Help” -> “Check for Updates” within RStudio, but this may not show the very latest updates.",
"crumbs": [
"Resources",
"Installing R and RStudio"
]
},
{
"objectID": "resources/install.html#reference",
"href": "resources/install.html#reference",
"title": "Installing R and RStudio",
"section": "",
"text": "Instructions adapted from guide developed by HMS Research computing and Chapter 3 of the The Epidemiologist R Handbook.",
"crumbs": [
"Resources",
"Installing R and RStudio"
]
},
{
"objectID": "resources/resources.html",
"href": "resources/resources.html",
"title": "Additional Resources",
"section": "",
"text": "Help documentation\nSearch the RStudio “Help” tab for documentation on R packages and specific functions. This is within the pane that also contains Files, Plots, and Packages (typically in the lower-right pane). As a shortcut, you can also type the name of a package or function into the R console after a question-mark to open the relevant Help page. Do not include parentheses.\nFor example: ?filter or ?diagrammeR.\nInteractive tutorials\nThere are several ways to learn R interactively within RStudio.\nRStudio itself offers a Tutorial pane that is powered by the learnr R package. Simply install this package and open a tutorial via the new “Tutorial” tab in the upper-right RStudio pane (which also contains Environment and History tabs).\nThe R package swirl offers interactive courses in the R Console. Install and load this package, then run the command swirl() (empty parentheses) in the R console. You will see prompts appear in the Console. Respond by typing in the Console. It will guide you through a course of your choice.\n\n\n\nThere are many PDF “cheatsheets” available on the RStudio website, for example:\n\nFactors with forcats package\n\nDates and times with lubridate package\n\nStrings with stringr package\n\niterative opertaions with purrr package\n\nData import\n\nData transformation cheatsheet with dplyr package\n\nR Markdown (to create documents like PDF, Word, Powerpoint…)\n\nShiny (to build interactive web apps)\n\nData visualization with ggplot2 package\n\nCartography (GIS)\n\nleaflet package (interactive maps)\n\nPython with R (reticulate package)\n\nThis is an online R resource specifically for Excel users\n\n\n\n\n\n\nSingle cell course materials\nOffical Seurat tutorials such as Using sctransform in Seurat and Cell-Cycle Scoring and Regression\nExtensive list of packages for single-cell analysis\n\n\n\n\n\nThe definitive R Markdown guide\n\n\n\n\n\nSome great lecture slides on data visualization\nanother plotting cheat sheet\ntidyverse ggplot basics page\n\nplotting continuous variables\n\nR for Data Science pages on data visualization\ngraphics for communicaton\n\n\n\n\n\n\nThe full flextable book is here: https://ardata-fr.github.io/flextable-book/\nThe Github site is here\n\nA manual of all the flextable functions can be found here\nA gallery of beautiful example flextable tables with code can be accessed here\n\n\n\n\nThe Data Carpentry page on dplyr\n\nThe tidyverse reference pages on group_by() and grouping\n\nThis page on Data manipulation\n\nSummarize with conditions in dplyr\n\nA helpful tutorial on pivoting tutorial\nThe tidyverse page on joins\n\nThe R for Data Science page on relational data\n\n\n\n\n\nLecture slides on surveys\nUCLA stats page\n\nArticle by Pew research for a general audeince on why we use survey weights\n\n\n\n\n\n\n\nR has a vibrant twitter community where you can learn tips, shortcuts, and news - follow these accounts:\n\nEpiHandbook (makers of the textbook these materials are based on) @epiRhandbook\n\nR Function A Day @rfuntionaday is an incredible resource\n\nR for Data Science @rstats4ds\n\nRStudio @RStudio\n\nRStudio Tips @rstudiotips\n\nR-Bloggers @Rbloggers\n\nR-ladies @RLadiesGlobal\n\nHadley Wickham @hadleywickham\n\nAlso:\n#epitwitter and #rstats\n\n\n\nA definitive text is the R for Data Science book by Garrett Grolemund and Hadley Wickham\nThe R4Epis project website aims to “develop standardised data cleaning, analysis and reporting tools to cover common types of outbreaks and population-based surveys that would be conducted in an MSF emergency response setting.” You can find R basics training materials, templates for RMarkdown reports on outbreaks and surveys, and tutorials to help you set them up.\n\n\n\n\nMateriales de RStudio en Español\nIntroduction à R et au tidyverse (Francais)",
"crumbs": [
"Resources",
"Additional Resources"
]
},
{
"objectID": "resources/resources.html#other-r-resources",
"href": "resources/resources.html#other-r-resources",
"title": "Additional Resources",
"section": "",
"text": "Help documentation\nSearch the RStudio “Help” tab for documentation on R packages and specific functions. This is within the pane that also contains Files, Plots, and Packages (typically in the lower-right pane). As a shortcut, you can also type the name of a package or function into the R console after a question-mark to open the relevant Help page. Do not include parentheses.\nFor example: ?filter or ?diagrammeR.\nInteractive tutorials\nThere are several ways to learn R interactively within RStudio.\nRStudio itself offers a Tutorial pane that is powered by the learnr R package. Simply install this package and open a tutorial via the new “Tutorial” tab in the upper-right RStudio pane (which also contains Environment and History tabs).\nThe R package swirl offers interactive courses in the R Console. Install and load this package, then run the command swirl() (empty parentheses) in the R console. You will see prompts appear in the Console. Respond by typing in the Console. It will guide you through a course of your choice.\n\n\n\nThere are many PDF “cheatsheets” available on the RStudio website, for example:\n\nFactors with forcats package\n\nDates and times with lubridate package\n\nStrings with stringr package\n\niterative opertaions with purrr package\n\nData import\n\nData transformation cheatsheet with dplyr package\n\nR Markdown (to create documents like PDF, Word, Powerpoint…)\n\nShiny (to build interactive web apps)\n\nData visualization with ggplot2 package\n\nCartography (GIS)\n\nleaflet package (interactive maps)\n\nPython with R (reticulate package)\n\nThis is an online R resource specifically for Excel users\n\n\n\n\n\n\nSingle cell course materials\nOffical Seurat tutorials such as Using sctransform in Seurat and Cell-Cycle Scoring and Regression\nExtensive list of packages for single-cell analysis\n\n\n\n\n\nThe definitive R Markdown guide\n\n\n\n\n\nSome great lecture slides on data visualization\nanother plotting cheat sheet\ntidyverse ggplot basics page\n\nplotting continuous variables\n\nR for Data Science pages on data visualization\ngraphics for communicaton\n\n\n\n\n\n\nThe full flextable book is here: https://ardata-fr.github.io/flextable-book/\nThe Github site is here\n\nA manual of all the flextable functions can be found here\nA gallery of beautiful example flextable tables with code can be accessed here\n\n\n\n\nThe Data Carpentry page on dplyr\n\nThe tidyverse reference pages on group_by() and grouping\n\nThis page on Data manipulation\n\nSummarize with conditions in dplyr\n\nA helpful tutorial on pivoting tutorial\nThe tidyverse page on joins\n\nThe R for Data Science page on relational data\n\n\n\n\n\nLecture slides on surveys\nUCLA stats page\n\nArticle by Pew research for a general audeince on why we use survey weights\n\n\n\n\n\n\n\nR has a vibrant twitter community where you can learn tips, shortcuts, and news - follow these accounts:\n\nEpiHandbook (makers of the textbook these materials are based on) @epiRhandbook\n\nR Function A Day @rfuntionaday is an incredible resource\n\nR for Data Science @rstats4ds\n\nRStudio @RStudio\n\nRStudio Tips @rstudiotips\n\nR-Bloggers @Rbloggers\n\nR-ladies @RLadiesGlobal\n\nHadley Wickham @hadleywickham\n\nAlso:\n#epitwitter and #rstats\n\n\n\nA definitive text is the R for Data Science book by Garrett Grolemund and Hadley Wickham\nThe R4Epis project website aims to “develop standardised data cleaning, analysis and reporting tools to cover common types of outbreaks and population-based surveys that would be conducted in an MSF emergency response setting.” You can find R basics training materials, templates for RMarkdown reports on outbreaks and surveys, and tutorials to help you set them up.\n\n\n\n\nMateriales de RStudio en Español\nIntroduction à R et au tidyverse (Francais)",
"crumbs": [
"Resources",
"Additional Resources"
]
},
{
"objectID": "about.html",
"href": "about.html",
"title": "About",
"section": "",
"text": "About this site\n\n1 + 1\n\n[1] 2"
}
]