-
Notifications
You must be signed in to change notification settings - Fork 10
/
r-dataframes.Rmd
203 lines (148 loc) · 11.8 KB
/
r-dataframes.Rmd
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
---
title: "Data Frames"
---
```{r init, include=F}
library(knitr)
opts_chunk$set(message=FALSE, warning=FALSE, eval=TRUE, echo=TRUE, cache=TRUE)
options(digits=3)
options(max.print=200)
.ex <- 1 # Track ex numbers w/ hidden var. Increment each ex: `r .ex``r .ex=.ex+1`
```
There are _lots_ of different basic data structures in R. If you take any kind of longer introduction to R you'll probably learn about arrays, lists, matrices, etc. Let's skip straight to the data structure you'll probably use most -- the **data frame**. We use data frames to store heterogeneous tabular data in R: tabular, meaning that individuals or observations are typically represented in rows, while variables or features are represented as columns; heterogeneous, meaning that columns/features/variables can be different classes (on variable, e.g. age, can be numeric, while another, e.g., cause of death, can be text).
**This lesson assumes a [basic familiarity with R](r-basics.html).**
**Recommended reading:** Review the [_Introduction_ (10.1)](http://r4ds.had.co.nz/tibbles.html#introduction-4) and [_Tibbles vs. data.frame_ (10.3)](http://r4ds.had.co.nz/tibbles.html#tibbles-vs.data.frame) sections of the [**_R for Data Science_ book**](http://r4ds.had.co.nz/tibbles.html). We will initially be using the `read_*` functions from the [**readr** package](http://readr.tidyverse.org/). These functions load data into a _tibble_ instead of R's traditional data.frame. Tibbles are data frames, but they tweak some older behaviors to make life a little easier. These sections explain the few key small differences between traditional data.frames and tibbles.
## Our data
<!-- There are some built-in data frames that ship with R that you'll often see people referencing in help forums or other places to demonstrate some functionality. The `mtcars` data frame has data extracted from the 1974 _Motor Trend_ magazine giving statistics about design and performance for 32 different vehicles. You can just type `mtcars` to look at the whole dataset. -->
The data we're going to look at is cleaned up version of a gene expression dataset from [Brauer et al. Coordination of Growth Rate, Cell Cycle, Stress Response, and Metabolic Activity in Yeast (2008) _Mol Biol Cell_ 19:352-367](http://www.ncbi.nlm.nih.gov/pubmed/17959824). This data is from a gene expression microarray, and in this paper the authors are examining the relationship between growth rate and gene expression in yeast cultures limited by one of six different nutrients (glucose, leucine, ammonium, sulfate, phosphate, uracil). If you give yeast a rich media loaded with nutrients except restrict the supply of a _single_ nutrient, you can control the growth rate to any rate you choose. By starving yeast of specific nutrients you can find genes that:
1. **Raise or lower their expression in response to growth rate**. Growth-rate dependent expression patterns can tell us a lot about cell cycle control, and how the cell responds to stress. The authors found that expression of >25% of all yeast genes is linearly correlated with growth rate, independent of the limiting nutrient. They also found that the subset of negatively growth-correlated genes is enriched for peroxisomal functions, and positively correlated genes mainly encode ribosomal functions.
2. **Respond differently when different nutrients are being limited**. If you see particular genes that respond very differently when a nutrient is sharply restricted, these genes might be involved in the transport or metabolism of that specific nutrient.
You can download the cleaned up version of the data at [the link above](data.html). The file is called [**brauer2007_tidy.csv**](data/brauer2007_tidy.csv). Later on we'll actually start with the original raw data (minimally processed) and manipulate it so that we can make it more amenable for analysis.
## Reading in data
### dplyr and readr
There are some built-in functions for reading in data in text files. These functions are _read-dot-something_ -- for example, `read.csv()` reads in comma-delimited text data; `read.delim()` reads in tab-delimited text, etc. We're going to read in data a little bit differently here using the [readr](http://readr.tidyverse.org/) package. When you load the readr package, you'll have access to very similar looking functions, named _read-underscore-something_ -- e.g., `read_csv()`. You have to have the readr package installed to access these functions. Compared to the base functions, they're _much_ faster, they're good at guessing the types of data in the columns, they don't do some of the other silly things that the base functions do. We're going to use another package later on called [dplyr](https://cran.r-project.org/web/packages/dplyr/index.html), and if you have the dplyr package loaded as well, and you read in the data with readr, the data will display nicely.
First let's load those packages.
```{r loadpkgs}
library(readr)
library(dplyr)
```
If you see a warning that looks like this: `Error in library(packageName) : there is no package called 'packageName'`, then you don't have the package installed correctly. See the [setup page](setup.html).
### `read_csv()`
Now, let's actually load the data. You can get help for the import function with `?read_csv`. When we load data we assign it to a variable just like any other, and we can choose a name for that data. Since we're going to be referring to this data a lot, let's give it a short easy name to type. I'm going to call it `ydat`. Once we've loaded it we can type the name of the object itself (`ydat`) to see it printed to the screen.
```{r loaddata}
ydat <- read_csv(file="data/brauer2007_tidy.csv")
ydat
```
Take a look at that output. The nice thing about loading dplyr and reading in data with readr is that data frames are displayed in a much more friendly way. This dataset has nearly 200,000 rows and 7 columns. When you import data this way and try to display the object in the console, instead of trying to display all 200,000 rows, you'll only see about 10 by default. Also, if you have so many columns that the data would wrap off the edge of your screen, those columns will not be displayed, but you'll see at the bottom of the output which, if any, columns were hidden from view. If you want to see the whole dataset, there are two ways to do this. First, you can click on the name of the data.frame in the **Environment** panel in RStudio. Or you could use the `View()` function (_with a capital V_).
```{r view, eval=FALSE}
View(ydat)
```
## Inspecting data.frame objects
### Built-in functions
There are several built-in functions that are useful for working with data frames.
* Content:
* `head()`: shows the first few rows
* `tail()`: shows the last few rows
* Size:
* `dim()`: returns a 2-element vector with the number of rows in the first element, and the number of columns as the second element (the dimensions of the object)
* `nrow()`: returns the number of rows
* `ncol()`: returns the number of columns
* Summary:
* `colnames()` (or just `names()`): returns the column names
* `str()`: structure of the object and information about the class, length and content of each column
* `summary()`: works differently depending on what kind of object you pass to it. Passing a data frame to the `summary()` function prints out useful summary statistics about numeric column (min, max, median, mean, etc.)
```{r tibble_functions}
head(ydat)
tail(ydat)
dim(ydat)
names(ydat)
str(ydat)
summary(ydat)
```
### Other packages
The `glimpse()` function is available once you load the **dplyr** library, and it's like `str()` but its display is a little bit better.
```{r}
glimpse(ydat)
```
The **skimr** package has a nice function, skim, that provides summary statistics the user can skim quickly to understand your data. You can install it with `install.packages("skimr")` if you don't have it already.
```{r}
library(skimr)
skim(ydat)
```
## Accessing variables & subsetting data frames
We can access individual variables within a data frame using the `$` operator, e.g., `mydataframe$specificVariable`. Let's print out all the gene names in the data. Then let's calculate the average expression across all conditions, all genes (using the built-in `mean()` function).
```{r}
# display all gene symbols
ydat$symbol
#mean expression
mean(ydat$expression)
```
Now that's not too interesting. This is the average gene expression across all genes, across all conditions. The data is actually scaled/centered around zero:
```{r histogram_expression_values, echo=F}
library(ggplot2)
ggplot(ydat, aes(expression)) +
geom_histogram(bins=100)+
xlab("Expression") +
ggtitle("Histogram of expression values") +
theme_bw()
```
We might be interested in the average expression of genes with a particular biological function, and how that changes over different growth rates restricted by particular nutrients. This is the kind of thing we're going to do in the next section.
----
**EXERCISE `r .ex``r .ex=.ex+1`**
1. What's the standard deviation expression (hint: get help on the `sd` function with `?sd`).
1. What's the range of rate represented in the data? (hint: `range()`).
----
## BONUS: Preview to advanced manipulation
What if we wanted show the mean expression, standard deviation, and correlation between growth rate and expression, separately for each limiting nutrient, separately for each gene, for all genes involved in the leucine biosynthesis pathway?
```{r, results='hide'}
ydat %>%
filter(bp=="leucine biosynthesis") %>%
group_by(nutrient, symbol) %>%
summarize(mean=mean(expression), sd=sd(expression), r=cor(rate, expression))
```
```{r, echo=FALSE}
ydat %>%
filter(bp=="leucine biosynthesis") %>%
group_by(nutrient, symbol) %>%
summarize(mean=mean(expression), sd=sd(expression), r=cor(rate, expression)) %>%
mutate_each(funs(round(., 2)), mean:r) %>% kable
```
Neat eh? We'll learn how to do that in the advanced manipulation with dplyr lesson.
<!-- Neat eh? Let's learn how to do that in the [advanced manipulation with dplyr lesson](r-dplyr-yeast.html). -->
```{r makedata, include=F, eval=F}
# Here's how I made the data
library(readr)
library(dplyr)
library(tidyr)
library(ggplot2)
theme_set(theme_bw())
# read original data
(ydat <- read_delim("data/brauer2007_orig.txt", delim="\t"))
# change variable WS||WS to ::
(ydat <- ydat %>% mutate(NAME=gsub("\\s*\\|\\|\\s*", "::", NAME, perl=TRUE)))
# separate on :: into gene symbol, go-bp, go-mf, syssymbol, and somenumber
(ydat <- ydat %>% separate(NAME, c("symbol", "bp", "mf", "systematic_symbol", "somenumber"), sep = "::"))
# replace missing with NA
(ydat[ydat==""] <- NA)
# remove things where syssymbol is missing
(ydat <- ydat %>% filter(!is.na(systematic_symbol) & systematic_symbol!=""))
# write out go terms to join to later
ydat %>% select(systematic_symbol, bp, mf) %>% distinct %>% write_csv("data/brauer2007_syssymbol2go.csv")
# unite the symbol column again
(ydat <- ydat %>% unite(NAME, symbol, systematic_symbol, somenumber, sep="::") %>% select(-bp, -mf))
# write out the messy data that you'll use for data cleaning class
ydat %>% write_csv("data/brauer2007_messy.csv")
rm(ydat)
(ydat <- read_csv("data/brauer2007_messy.csv"))
(syssymbol2go <- read_csv("data/brauer2007_syssymbol2go.csv"))
ydat <- ydat %>%
separate(NAME, c("symbol", "systematic_symbol", "somenumber"), sep = "::") %>%
select(-somenumber, -GID, -YORF, -GWEIGHT) %>%
gather(sample, expression, G0.05:U0.3) %>%
separate(sample, c("nutrient", "rate"), sep = 1) %>%
filter(!is.na(expression), systematic_symbol != "") %>%
inner_join(syssymbol2go, by="systematic_symbol") %>%
mutate(nutrient = plyr::mapvalues(nutrient,
from=c("G", "L", "P", "S", "N", "U"),
to=c("Glucose", "Leucine", "Phosphate", "Sulfate", "Ammonium", "Uracil")))
ydat %>% write_csv("data/brauer2007_tidy.csv")
```