R Basics Continued - Factors and Data Frames - Intro To R and RStudio For Genomics
R Basics Continued - Factors and Data Frames - Intro To R and RStudio For Genomics
TTeeaacchhiinngg:: 60 min
EExxeerrcciisseess:: 30 min
QQuueessttiioonnss
How do I get started with tabular data (e.g. spreadsheets) in R?
What are some best practices for reading data into R?
How do I save tabular data generated in R?
O
Obbjjeeccttiivveess
Explain the basic principle of tidy datasets
Be able to load a tabular dataset using base R functions
Be able to determine the structure of a data frame including its dimensions and the
datatypes of variables
Be able to subset/retrieve values from a data frame
Understand how R may coerce data into different modes
Be able to change the mode of an object
Understand that R uses factors to store and manipulate categorical data
Be able to manipulate a factor, including subsetting and reordering
Be able to apply an arithmetic function to a data frame
Be able to coerce the class of an object (including variables in a data frame)
Be able to import data from Excel
Be able to save a data frame as a delimited file
11)) K
Keeeepp rraaw
w ddaattaa sseeppaarraattee ffrroom
m aannaallyyzzeedd ddaattaa
This is principle number one because if you can’t tell which files are the original raw data, you risk
making some serious mistakes (e.g. drawing conclusion from data which have been manipulated in
some unknown way).
22)) K
Keeeepp sspprreeaaddsshheeeett ddaattaa TTiiddyy
The simplest principle of TTiiddyy ddaattaa is that we have one row in our spreadsheet for each
observation or sample, and one column for every variable that we measure or report on. As simple
as this sounds, it’s very easily violated. Most data scientists agree that significant amounts of their
time is spent tidying data for analysis. Read more about data organization in our lesson
(https://datacarpentry.org/organization-genomics/) and in this paper (https://www.jstatsoft.org
/article/view/v059i10).
Finally, while you don’t need to be paranoid about data, you should have a plan for how you will
prepare it for analysis. TThhiiss aa ffooccuuss ooff tthhiiss lleessssoonn.. You probably already have a lot of intuition,
expectations, assumptions about your data - the range of values you expect, how many values
should have been recorded, etc. Of course, as the data get larger our human ability to keep track
will start to fail (and yes, it can fail for small data sets too). R will help you to examine your data so
that you can have greater confidence in your analysis, and its reproducibility.
When you work with data in R, you are not changing the original file you loaded that data from.
This is different than (for example) working with a spreadsheet program where changing the
value of the cell leaves you one “save”-click away from overwriting the original file. You have to
purposely use a writing function (e.g. write.csv() ) to save data loaded into R. In that case, be
sure to save the manipulated data into a new file. More on this later in the lesson.
Hint: Entering ‘?’ before the function name and then running that line will bring up the help
documentation. Also, when reading this particular help be careful to pay attention to the
‘read.csv’ expression under the ‘Usage’ heading. Other answers will be in the ‘Arguments’
heading.
B) What argument would you have to change to read a file that was delimited by semicolons (;)
rather than commas?
C) What argument would you have to change to read file in which numbers used commas for
decimal separation (i.e. 1,00)?
D) What argument would you have to change to read in only the first 10,000 rows of a very large
file?
Solution
/home/dcuser/.solutions/R_data/ . Call this data variants . The first argument to pass to our
read.csv() function is the file path for our data. The file path must be in quotes and now is a
good time to remember to use tab autocompletion. IIff yyoouu uussee ttaabb aauuttooccoom
mpplleettiioonn yyoouu aavvooiidd
ttyyppooss aanndd eerrrroorrss iinn fi
fillee ppaatthhss.. Use it!
R
R
One of the first things you should notice is that in the Environment window, you have the
variants object, listed as 801 obs. (observations/rows) of 29 variables (columns). Double-clicking
on the name of the object will open a view of the data in a new tab.
R
R
summary(variants)
O
Ouuttppuutt
Our data frame had 29 variables, so we get 29 fields that summarize the data. The QUAL , IMF , and
VDB variables (and several others) are numerical data and so you get summary statistics on the
min and max values for these columns, as well as mean, median, and interquartile ranges. Many of
the other variables (e.g. sample_id ) are treated as categorical data (which have special treatment
in R - more on this in a bit). The most frequent 6 different categories and the number of times they
appear (e.g. the sample_id called ‘SRR2584863’ appeared 25 times) are displayed. There was only
one value for CHROM , “CP000819.1” which appeared in all 801 observations.
Before we operate on the data, we also need to know a little more about the data frame structure
to do that we use the str() function:
R
R
str(variants)
O
Ouuttppuutt
the object type data.frame is displayed in the first row along with its dimensions, in this case
801 observations (rows) and 29 variables (columns)
Each variable (column) has a name (e.g. sample_id ). This is followed by the object mode (e.g.
factor, int, num, etc.). Notice that before each variable name there is a $ - this will be
important later.
Introducing Factors
Factors are the final major data structure we will introduce in our R genomics lessons. Factors can
be thought of as vectors which are specialized for categorical data. Given R’s specialization for
statistics, this make sense since categorial and continuous variables usually have different
treatments. Sometimes you may want to have data treated as a factor, but in other cases, this may
be undesirable.
Since some of the data in our data frame are factors, lets see how factors work. First, we’ll extract
one of the columns of our data frame to a new object, so that we don’t end up modifying the
variants object by mistake.
R
R
Let’s look at the first few items in our factor using head() :
R
R
head(REF)
O
Ouuttppuutt
What we get back are the items in our factor, and also something called “Levels”. LLeevveellss aarree tthhee
ddiiff
ffeerreenntt ccaatteeggoorriieess ccoonnttaaiinneedd iinn aa ffaaccttoorr. By default, R will organize the levels in a factor in
alphabetical order. So the first level in this factor is “A”.
Lets look at the contents of a factor in a slightly different way using str() :
R
R
str(REF)
O
Ouuttppuutt
chr [1:801] "T" "G" "G" "CTTTTTTT" "CCGC" "C" "C" "G" ...
For the sake of efficiency, R stores the content of a factor as a vector of integers, which an integer
is assigned to each of the possible levels. Recall levels are assigned in alphabetical order. In this
case, the first item in our “REF” object is “T”, which happens to be the 49th level of our factor,
ordered alphabeticaly. The next two items are both “G”s, which is the 33rd level of our factor.
R
R
plot(REF)
W
Waarrnniinngg
W
Waarrnniinngg
W
Waarrnniinngg
EErrrroorr
This isn’t a particularly pretty example of a plot. We’ll be learning much more about creating nice,
publication-quality graphics later in this lesson.
The first thing to remember is that a data frame is two-dimensional (rows and columns). Therefore,
to select a specific value we will will once again use [] (bracket) notation, but we will specify more
than one value (except in some cases where we are taking a range).
a. variants[1,1]
b. variants[2,4]
c. variants[801,29]
d. variants[2, ]
e. variants[-1, ]
f. variants[1:4,1]
g. variants[1:10,c("REF","ALT")]
h. variants[,c("sample_id")]
i. head(variants)
j. tail(variants)
k. variants$sample_id
l. variants[variants$REF == "A",]
Solution
The subsetting notation is very similar to what we learned for vectors. The key differences include:
Finally, in all of the subsetting exercises above, we printed values to the screen. You can create a
new data frame object by assigning them to a new object name:
R
R
dim(SRR2584863_variants)
O
Ouuttppuutt
[1] 25 29
R
R
summary(SRR2584863_variants)
O
Ouuttppuutt
While we are going to address coercion in the context of data frames most of these methods
apply to other data structures, such as vectors
Sometimes, it is possible that R will misinterpret the type of data represented in a data frame, or
store that data in a mode which prevents you from operating on the data the way you wish. For
example, a long list of gene names isn’t usually thought of as a categorical variable, the way that
your experimental condition (e.g. control, treatment) might be. More importantly, some R packages
you use to analyze your data may expect characters as input, not factors. At other times (such as
plotting or some statistical analyses) a factor may be more appropriate. Ultimately, you should
know how to change the mode of an object.
First, its very important to recognize that coercion happens in R all the time. This can be a good
thing when R gets it right, or a bad thing when the result is not what you expect. Consider:
R
R
O
Ouuttppuutt
[1] "character"
Although there are several numbers in our vector, they are all in quotes, so we have explicitly told R
to consider them as characters. However, even if we removed the quotes from the numbers, R
would coerce everything into a character:
R
R
O
Ouuttppuutt
[1] "character"
R
R
snp_chromosomes_2[1]
O
Ouuttppuutt
[1] "3"
We can use the as. functions to explicitly coerce values from one form into another. Consider the
following vector of characters, which all happen to be valid numbers:
R
R
O
Ouuttppuutt
[1] "character"
R
R
snp_positions_2[1]
O
Ouuttppuutt
[1] "8762685"
R
R
O
Ouuttppuutt
[1] "double"
R
R
snp_positions_2[1]
O
Ouuttppuutt
[1] 8762685
Sometimes coercion is straight forward, but what would happen if we tried using as.numeric()
on snp_chromosomes_2
R
R
W
Waarrnniinngg
If we check, we will see that an NA value (R’s default value for missing data) has been introduced.
R
R
snp_chromosomes_2
O
Ouuttppuutt
[1] 3 11 NA 6
Trouble can really start when we try to coerce a factor. For example, when we try to coerce the
sample_id column in our data frame into a numeric mode look at the result:
R
R
as.numeric(variants$sample_id)
W
Waarrnniinngg
O
Ouuttppuutt
[1] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[26] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[51] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[76] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[101] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[126] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[151] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[176] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[201] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[226] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[251] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[276] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[301] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[326] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[351] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[376] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[401] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[426] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[451] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[476] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[501] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[526] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[551] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[576] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[601] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[626] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[651] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[676] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[701] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[726] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[751] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[776] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[801] NA
Strangely, it works! Almost. Instead of giving an error message, R returns numeric values, which in
this case are the integers assigned to the levels in this factor. This kind of behavior can lead to hard-
to-find bugs, for example when we do have numbers in a factor, and we get numbers from a
coercion. If we don’t look carefully, we may not notice a problem.
If you need to coerce an entire column you can overwrite it using an expression like this one:
R
R
O
Ouuttppuutt
[1] "character"
StringsAsFactors = FALSE
Lets summarize this section on coercion with a few take home messages.
When you explicitly coerce one data type into another (this is known as eexxpplliicciitt ccooeerrcciioonn), be
careful to check the result. Ideally, you should try to see if its possible to avoid steps in your
analysis that force you to coerce.
R will sometimes coerce without you asking for it. This is called (appropriately) iim mpplliicciitt
ccooeerrcciioonn. For example when we tried to create a vector with multiple data types, R chose one
Regarding the first bullet point, one way to avoid needless coercion when importing a data frame
using any one of the read.table() functions such as read.csv() is to set the argument
StringsAsFactors to FALSE. By default, this argument is TRUE. Setting it to FALSE will treat any
non-numeric column to a character type. read.csv() documentation, you will also see you can
explicitly type your columns using the colClasses argument. Other R packages (such as the
Tidyverse “readr”) don’t have this particular conversion issue, but many packages will still try to
guess a data type.
There are lots of arithmetic functions you may want to apply to your data frame, covering those
would be a course in itself (there is some starting material here (https://swcarpentry.github.io/r-
novice-inflammation/15-supp-loops-in-depth/)). Our lessons will cover some additional summary
statistical functions in a subsequent lesson, but overall we will focus on data cleaning and
visualization.
You can use functions like mean() , min() , max() on an individual column. Let’s look at the “DP”
or filtered depth. This value shows the number of filtered reads that support each of the reported
variants.
R
R
max(variants$DP)
O
Ouuttppuutt
[1] 79
R
R
O
Ouuttppuutt
[1] 2 2 2 2 2 2
✏ Exercise
The order() function lists values in increasing order by default. Look at the documentation for
this function and change sorted_by_DP to start with variants with the greatest filtered depth
(“DP”).
Solution
R
R
O
Ouuttppuutt
R
R
The write.csv() function has some additional arguments listed in the help, but at a minimum
you need to tell it what data frame to write to file, and give a path to a file name in quotes (if you
only provide a file name, the file will be written in the current working directory).
One common R package (a set of code with features you can download and add to your R
installation) is the readxl package (https://CRAN.R-project.org/package=readxl) which can open and
import Excel files. Rather than addressing package installation this second (we’ll discuss this soon!),
we can take advantage of RStudio’s import feature which integrates this package. (Note: this
feature is available only in the latest versions of RStudio such as is installed on our cloud instance).
Notice that you have the option to change the data type of each variable by clicking arrow (drop-
down menu) next to each column title. Under IIm mppoorrtt O
Oppttiioonnss you may also rename the data,
choose a different sheet to import, and choose how you will handle headers and skipped rows.
Under CCooddee PPrreevviieew
w you can see the code that will be used to import this file. We could have
written this code and imported the Excel file without the RStudio import function, but now you can
choose your preference.
In this exercise, we will leave the title of the data frame as EEccoollii__m
meettaaddaattaa, and there are no other
options we need to adjust. Click the Import button to import the data.
Finally, let’s check the first few lines of the Ecoli_metadata data frame:
EErrrroorr
R
R
head(Ecoli_metadata)
EErrrroorr
The type of this object is ‘tibble’, a type of data frame we will talk more about in the ‘dplyr’ section.
If you needed a true R data frame you could coerce with as.data.frame() .
U
Ussiinngg tthhee Ecoli_metadata ddaattaa ffrraam
mee ccrreeaatteedd aabboovvee,, aannssw
weerr tthhee ffoolllloow
wiinngg qquueessttiioonnss
B) What are categories are there in the cit column? hint: treat column as factor
D) What is the genome size for the 7th observation in this data set?
G) Create a new column (name genome_size_bp) and set it equal to the genome_size multiplied
by 1,000,000
H) Save the edited Ecoli_metadata data frame as “exercise_solution.csv” in your current working
directory.
Solution
Key Points
It is easy to import data into R from tabular formats including Excel. However, you still need
to check that R has imported and interpreted your data correctly
There are best practices for organizing your data (keeping it tidy) and R is great for this
Base R has many useful functions for manipulating your data, but all of R’s capabilities are
greatly enhanced by software packages developed by the community
(../02- (../04-
r-basics dplyr/index.html
/index.html)
Using The Carpentries theme (https://github.com/carpentries/carpentries-theme/) — Site last built on: 2021-01-13
16:14:44 +0000.