This lab is part of a series designed for BIOL 300, based on The Analysis of Biological Data. The rest of the labs can be found here.


Learning outcomes

  • Make data files for R

  • Understand how data frames and tibbles organize information

  • Learn how to increase the power of R with packages


If you have not already done so, download the zip file containing Data, R scripts, and other resources for these labs. Remember to start RStudio from the ABDLabs.Rproj file in that folder to make these exercises work more seamlessly.



Learning the tools


Structure of a good data file

Data files appear in many formats, and different formats are sometimes preferable for different tasks. However, there is one especially useful way to organize data for statistics and R, called “tabular data”.

The tabular format is actually very simple. Each row in the data set represents an individual or observational unit, and each column represents a variable measured on those individuals.

For example, here is a small data set about the tongue and palate lengths of several species of bats. In this data set, each row is one species of bat, and the columns contain the species name, palate length, and tongue length.

species palate_length tongue_length
Lichonycteris obscura 10 36.1
Glossophaga comissarisi 10.7 26.6
Glossophaga soricina 11.4 30.2
Anoura caudifer 11.6 36.7
Hylonycteris underwoodi 13.4 36.7
Anoura geoffroyi 13.8 39.6
Lonchophylla robusta 14.3 42.6
Anoura fistulata 12.4 85.2
Anoura cultrata 14.3 34.3
Leptonycteris curosoae 16 40.2
Choeronycteris mexicana 18 52.1


Recording metadata

Data have no value unless they can be correctly interpreted. It is important to record enough information that each variable in your data set can be well-understood by another reader (including your future self).

Key information includes the meaning of each variable name, including units, along with when, where and who collected the data. The objective here is to provide a short document that another person could read to unambiguously interpret your data file.


Creating a data file

When you have new data that you want to get into the computer in a format that R can read, it is often easiest to do this outside of R. A spreadsheet program like Excel (or a free alternative like Google Sheets) gives a straightforward way to create a comma-separated values file, or .csv, that R can read.

In your spreadsheet program, create a new blank workbook. In the first row of your new spreadsheet, write your variable names, one for each column. (Be sure to give them good names that will work in R. Mainly, don’t have any spaces in a variable name and make sure that it doesn’t start with a number or contain punctuation marks. See Lab 1 for more about naming variables.)

On the rows immediately below that first row, enter the data for each individual, in the correct column. Here’s what the spreadsheet would look like for the bat data after they are entered:


Saving a .csv file

Saving a spreadsheet in a format that R can read is very straightforward. In these labs, we are using .csv files. Once you have made your spreadsheet, choose File → Save as…. This will open a dialog box. First, give the file a name with the extension .csv at the end. We used BatTongues.csv.

Second, choose the folder in which you want to save the file. For any data files we create for BIOL 300, save them in the DataForLabs folder (DataForLabs should be within your ABDLabs folder).

Third, select the correct format for the file: “Comma separated values (.csv)”, which you can find in the dropdown next to Format: in the dialog box. It might look something like this:

In the resulting file, the first line will be a header that lists the names of each column (variable). After that there will be one line for each individual. All the variable names in the first row and the variable values in the later rows will be separated by commas, hence the name of the format.

If you followed what we did for all 3 steps, the relative file path for the bat data would be something like this: ABDLabs/DataForLabs/BatTongues.csv. If you opened the .csv file in a text editor, it would look like this:


species,palate_length,tongue_length
Lichonycteris obscura,10,36.1
Glossophaga comissarisi,10.7,26.6
Glossophaga soricina,11.4,30.2
Anoura caudifer,11.6,36.7
Hylonycteris underwoodi,13.4,36.7
Anoura geoffroyi,13.8,39.6
Lonchophylla robusta,14.3,42.6
Anoura fistulata,12.4,85.2
Anoura cultrata,14.3,34.3
Leptonycteris curosoae,16,40.2
Choeronycteris mexicana,18,52.1


All the information is there, and it is stored in a simple text file that can be read again by Excel or R or many other programs. We strongly recommend against hand-editing a .csv file in a text editor, because it is very easy to mess up the formatting and make the file unreadable by R. If you need to edit a .csv file, we recommend that you open it in Excel or Google Sheets, make your edits, and then save it again as a .csv file.



Installing packages

R has a lot of power in its basic form, but one of the most important parts about R is that it is expandable by the work of other people. These expansions are usually released in “packages”. An R package is a collection of functions that can be used in addition to the built-in R functions once loaded.

Each package needs to be installed on your computer only once, but to be used it has to be loaded into R during each session.

To install a package in RStudio, click on the Packages tab from the sub-window with tables for Files, Plots, Packages, Help, and Viewer. Immediately below that will be a button labeled “Install”—click that and a window will open.

In the second row (labeled “Packages”), type tidyverse (without quotation marks). Make sure the box for “Install dependencies” near the bottom is clicked, and then click the “Install” button at bottom right.

Installing an R package only needs to be done once on a given computer. If installed correctly, the package will permanently available from that point forward (though it will need to be loaded).


Loading a package

Once a package is installed, it needs to be loaded into R during a session if you want to use it. You do this with a function called library().


library()

For this lab, we will use functions from a couple packages within the tidyverse. This might feel initially confusing, but tidyverse itself is a collection of separate packages. Therefore, tidyverse is sometimes referred to as a “metapackage” (don’t worry, that bit of trivia won’t be on an exam).

Before using the functions in these packages, we need to load tidyverse. We do this with the library() function:

library(tidyverse)
## ── Attaching core tidyverse packages ─────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.2.1     ✔ readr     2.2.0
## ✔ forcats   1.0.1     ✔ stringr   1.6.0
## ✔ ggplot2   4.0.3     ✔ tibble    3.3.1
## ✔ lubridate 1.9.5     ✔ tidyr     1.3.2
## ✔ purrr     1.2.2     
## ── Conflicts ───────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors

If the tidyverse was installed on your computer correctly, the computer will show a brief status message like the one above and then will be ready to go. In the first part of the message, you can see the names of each individual R package within tidyverse, such as dplyr, forcats, and so on. The numbers after each package name indicate the version of each package that is on your computer (this can occasionally be important if specific functions change across versions of a package).

Our labs will use functions from several packages within tidyverse, including ggplot2 and dplyr, and the above status message shows you that they have been loaded up as part of doing library(tidyverse).

If the package was not installed correctly (or at all), it will give you an error message in red asking you to install the package.


Setting the working directory

The files on your computers are organized hierarchically into folders, or “directories”. It is convenient in RStudio to tell R which directory to look for files at the beginning of a session, to minimize typing later.

For these labs, the best way to set your working directory is to start R and Rstudio by clicking on the ABDLabs.Rproj file in the ABDLabs directory. This will automatically load the needed packages and set the working directory to this folder.

You can also manually set the working directory from RStudio’s menu via Session → Set Working Directory → Choose Directory…. This will open a dialog box that will let you find and select the directory you want. For BIOL 300 labs, use ABDLabs as your working directory.


Reading a file

In these labs, we have saved the data in a comma-separated variable (CSV) format. All files in this format ought to have .csv as the end of their file name. A CSV file is a plain text file, easily read by a wide variety of programs. Each row in the file (besides the first row) is the data for a given individual, and for each individual each variable is listed in the same order, separated by commas. It’s important to note that you can’t have commas elsewhere in the file; they function as the separators.

The first row of a CSV file should be a “header” row, which gives the names of each variable, again separated by commas.


read.csv()

For examples in this lab, let’s use a data set about the passengers of the RMS Titanic. One of the data sets in the folder of data attached to this lab is called titanic.csv. This is a data set of 1313 passengers from the voyage of this ship, which contains information about some personal info about each passenger as well as whether they survived the accident or not.

To import a CSV file into R, use the read.csv() function as in the following command. (This assumes that you have set the working directory to ABDLabs, as we described above.)

titanicData <- read.csv("DataForLabs/titanic.csv", stringsAsFactors = TRUE)

This looks for the file called titanic.csv in the folder called DataForLabs. Here we have given the name titanicData to the object in R that contains all this passenger data. Of course, if you wanted to load a different data set, you would be better off giving it a more apt name than “titanicData”. The option stringsAsFactors = TRUE asks R to interpret the columns with non-numerical information as “factor” with possibly repeated instances of the same value of a categorical variable.

R has other functions that can read other data formats besides csv files, but the function read.csv() requires that the file be a csv file.

Looking at the Environment tab in RStudio can help confirm that the data were imported. The Environment allows you to see all objects that are in R’s memory and available for you to use.


Finding files in other places

Note that for all labs that you turn in for BIOL 300, we expect you to use ABDLabs.Rproj to start your session, and put any new data into the DataForLabs folder. Do not use either of the examples in this section in your R scripts for BIOL 300.

When you work on a new data set outside of BIOL 300 labs, you will want to store the data somewhere else. To import data into R from another location on your computer, you need to know the full file path for the file. For example, the file path to the titanic file on my Mac is “/Users/vikram/Desktop/ABDLabs/DataForLabs/titanic.csv”. On a Windows computer, it would look something like “C:\Documents\ABDLabs\DataForLabs\titanic.csv”. In either case, this file path describes a series of folders nested inside one another that tells the computer where to look for the file.

By supplying the full address, you are telling read.csv() to look for the file in a specific place, which may be outside your current working directory:

titanicData <-
  read.csv("/Users/vikram/Desktop/titanic.csv",
    stringsAsFactors = TRUE
  )

Another alternative is to have R use your Finder or Windows Explorer to help you select the right file. This can be occasionally useful if you are importing a file for one-time usage and aren’t worried so much about the reproducibility of your code. Do not use this in your R scripts for BIOL 300. Reading a file from any location can be done with file.choose() like this:

titanicData <-
  read.csv(file.choose(),
    stringsAsFactors = TRUE
  )

This will bring up a new window where you navigate to find the file of interest yourself. Note that the specific name of the file (titanic.csv) isn’t specified in the block of code; it’s incumbent on you to select the correct file.

Again note that for all labs that you turn in for BIOL 300, we expect you to use ABDLabs.Rproj to start your session, and put data into the DataForLabs folder. Do not use either of the examples above in your R scripts. All data import should follow something like: object_name <- read.csv("DataForLabs/data.csv", stringsAsFactors = TRUE)

This enforces a standard environment for all submitted work, which makes life easier for you & your TAs.


A first look at data

To see if the data were imported appropriately and/or get a sense of the characteristics of the data, the commands below can be useful.

View()

The View() function is a nice way to see the data in a spreadsheet-like format. To see the data in titanicData with View(), run the following command:

View(titanicData)

Again, note that the titanicData object has 1313 rows and 7 columns. A convention in R is to describe data frames according to the number of rows first and then columns second. For example, titanicData would be a 1313 x 7 object.

The horizontal and vertical scroll bars will allow you to see more of the data compared to what may initially appear in the window.


summary()

This function will list all the variables and some summary statistics for each variable.

summary(titanicData)
##  passenger_class                        name           age         
##  1st:322         Carlsson,MrFransOlof     :   2   Min.   : 0.1667  
##  2nd:280         Connolly,MissKate        :   2   1st Qu.:21.0000  
##  3rd:711         Kelly,MrJames            :   2   Median :30.0000  
##                  Abbing,MrAnthony         :   1   Mean   :31.1942  
##                  Abbott,MasterEugeneJoseph:   1   3rd Qu.:41.0000  
##                  Abbott,MrRossmoreEdward  :   1   Max.   :71.0000  
##                  (Other)                  :1304   NAs    :680      
##         embarked            home_destination     sex      survive  
##             :493                    :558     female:463   no :864  
##  Cherbourg  :202   NewYork,NY       : 65     male  :850   yes:449  
##  Queenstown : 45   London           : 14                           
##  Southampton:573   Montreal,PQ      : 10                           
##                    Cornwall/Akron,OH:  9                           
##                    Paris,France     :  9                           
##                    (Other)          :648


names()

Sometimes, we’ll deal with datasets that are quite “wide” (lots of measured variables). These data may not lend themselves well to being inspected via View() and/or you may just want to see basic information about the names of variables.

names(), if used on a data.frame or tibble, will report the names of each column:

names(titanicData)
## [1] "passenger_class"  "name"             "age"             
## [4] "embarked"         "home_destination" "sex"             
## [7] "survive"


Data frames and tibbles

In R, a data set is often stored as a data.frame or a tibble. For now, you can think of these as very similar: both store data in rows and columns (tabular data).

Each column contains one variable, such as age, sex, or survival. Each row contains one individual, such as one passenger on the Titanic. The values in a row belong together, so the first value in each column describes the same individual, the second value in each column describes the next individual, and so on.

The function read.csv() imports data into R as a data.frame. To work with the data, it will be important to save it as an object in your R Environment. For example, in the previous section we read in a data set and saved it as titanicData. This object contains information about passengers on the Titanic. It has seven variables, so it has seven columns: passenger_class, name, age, embarked, home_destination, sex, and survive.


Columns within data frames

Very importantly, we can grab one column from a data frame or tibble by itself. We write the name of the data set, followed by a $, and then the name of the variable.

For example, to show a list of the age of all the passengers on the Titanic, use

titanicData$age

This will show a vector that has all the values for this variable age, one for each individual in the data set.


Indexing

“Indexing” means selecting specific parts of objects. In Lab 1, we used square brackets [ ] to look at specific elements within a vector.

Data frames and tibbles are often described by how many rows and columns they contain. The convention is to list rows first: for example, titanicData would be a 1313 x 7 object.

We can use this convention as a coordinate system for indexing specific subsets of the data. To do this, we again use square brackets right next to the object name and specify [row(s), column(s)] of interest. Remember that in R, the indexing starts at 1, not 0 (this is different from the way Python works!).

For example, to see the entry for the 6th row and 3rd column of titanicData:

titanicData[6, 3]
## [1] 47

If you do not include a value in either the “row” or “column” spot, R will include all rows or all columns. The comma within the brackets [ , ] will still be necessary for R to interpret what you intend. The following shows all columns for the 6th row:

titanicData[6, ]
##   passenger_class             name age    embarked home_destination  sex
## 6             1st Anderson,MrHarry  47 Southampton       NewYork,NY male
##   survive
## 6     yes

Indexing data can get a bit more complicated. Other operators such as : or functions such as c() can be used to grab specific subsets of data. This won’t be a point of emphasis in BIOL 300; we’re more likely to subset our data based on factors using the filter() function described in a section below.


Adding a new column

Sometimes we would like to add a new column to a data frame. The easiest way to do this is to simply assign a new vector to a new column name, using the $.

For example, to add the natural log of age as a column in the titanicData data frame, we can write

titanicData$log_age <- log(titanicData$age)

You can run the command head(titanicData) to see that log_age is now a column in titanicData.

head(titanicData)
##   passenger_class                                      name     age
## 1             1st                 Allen,MissElisabethWalton 29.0000
## 2             1st                  Allison,MissHelenLoraine  2.0000
## 3             1st           Allison,MrHudsonJoshuaCreighton 30.0000
## 4             1st Allison,MrsHudsonJ.C.(BessieWaldoDaniels) 25.0000
## 5             1st                Allison,MasterHudsonTrevor  0.9167
## 6             1st                          Anderson,MrHarry 47.0000
##      embarked            home_destination    sex survive     log_age
## 1 Southampton                  StLouis,MO female     yes  3.36729583
## 2 Southampton Montreal,PQ/Chesterville,ON female      no  0.69314718
## 3 Southampton Montreal,PQ/Chesterville,ON   male      no  3.40119738
## 4 Southampton Montreal,PQ/Chesterville,ON female      no  3.21887582
## 5 Southampton Montreal,PQ/Chesterville,ON   male     yes -0.08697501
## 6 Southampton                  NewYork,NY   male     yes  3.85014760

The head() function shows the first 6 rows of a data frame or tibble, which is a handy way to check that you understand the structure of the data and/or that you have added the new column correctly.


Subsetting with filter()

Sometimes we want to do an analysis only on some of the data that fit certain criteria. For example, we might want to analyze the data from the Titanic using only the information from females.

The easiest way to do this is to use the filter() function from the package dplyr. dplyr is a package within the tidyverse, so make sure you have loaded tidyverse into R using library(). The line libary(tidyverse) only needs to be run once per session, and then you can use any of the functions in the tidyverse packages, including filter().

In the Titanic data set, there is a variable named sex and an individual is female if that variable has value female. We can create a new data frame that includes only the data from females with the following command:

titanicDataFemalesOnly <- filter(titanicData, sex == "female")
summary(titanicDataFemalesOnly)
##  passenger_class                            name          age         
##  1st:143         Connolly,MissKate            :  2   Min.   : 0.1667  
##  2nd:107         Abbott,MrsStanton(Rosa)      :  1   1st Qu.:19.0000  
##  3rd:213         Abelseth,MissAnnaKaren       :  1   Median :29.0000  
##                  Abelson,MrsSamuel(Anna)      :  1   Mean   :30.5727  
##                  Abraham,MrsJoseph(SophieEasu):  1   3rd Qu.:40.0000  
##                  Ahlin,MrsJohannaPersdotter   :  1   Max.   :69.0000  
##                  (Other)                      :456   NAs    :220      
##         embarked            home_destination     sex      survive  
##             :152                    :171     female:463   no :156  
##  Cherbourg  : 97   NewYork,NY       : 31     male  :  0   yes:307  
##  Queenstown : 21   Cornwall/Akron,OH:  5                           
##  Southampton:193   Paris,France     :  5                           
##                    SwedenWinnipeg,MN:  5                           
##                    London           :  4                           
##                    (Other)          :242                           
##     log_age      
##  Min.   :-1.792  
##  1st Qu.: 2.944  
##  Median : 3.367  
##  Mean   : 3.240  
##  3rd Qu.: 3.689  
##  Max.   : 4.234  
##  NAs    :220

This new data frame will include all the same columns as the original titanicData, but it will only include the 463 rows for which sex was female. Note the differences between summary(titanicDataFemalesOnly) and what we saw when we ran summary(titanicData).

Note that the syntax within filter() requires a double equal sign: ==. In R (and many other computer languages), the double equal sign creates a statement that can be evaluated as TRUE or FALSE, whereas a single equal sign may change the value of the object to the value on the right-hand side of the equal sign. Here we are asking, for each individual, whether sex is female. We not assigning the value female to the variable sex. So we must use a double equal sign ==.


R commands summary



Activities & Questions

Make a script in RStudio that includes all your R code to answer the following questions. Include answers to all questions below using comments.


1. Install the tidyverse package on your system if you haven’t already. After it has installed, use library() to load it into your R environment. A good practice is to write library(tidyverse) as one of the first lines of code in each R script you write for BIOL 300.


2. In Lab 1, Question 1, you and your fellow students may have recorded information about yourselves. You will now use the data from all of these sheets to create a .csv file in Excel or Google Sheets. Include all of seven of the variables for each of the individuals in the data sheets. Be sure to use variable names that are 1) legal in R and 2) intelligible to another person who may encounter the file (remember that snake_case or camelCase are preferred conventions). If any data are missing (e.g., the person chose to not record a value), leave the corresponding cell blank. Save the data as a .csv file in the DataForLabs folder on your computer with the file name Lab01_section_measurements.csv.

  1. Prepare a text file which describes each of the variables in the data file. Remember to list the meaning of each variable name, including units. The objective here is to provide a short document that another person could read to unambiguously interpret your data file.

  2. The line of code below will not work because it contains mistakes. Make corrections to the code to import the csv into R as a data frame named Lab01_section_measurements. Explain each correction you made (there should be at least 2). Why is each one necessary?

    Lab01_section_measurements <- read.csv(DataForLabs/Lab01_section_measurements.csv, strings = "TRUE")

  3. Use the View() function to check that you have read the data correctly.

  4. Use the summary() function to determine the proportion of people in your tutorial section who are right-handed.


3. The data file called countries.csv in the DataForLabs folder contains information about all the countries on Earth. Each row is a country, and each column contains a variable.

  1. Use read.csv() to read the data from this file into a data frame called countries.

  2. Use summary() to get a quick description of this data set. What are the first three variables?

  3. Using the output of summary(), how many countries are from Asia in this data set?

  4. What kinds of variables (i.e., categorical or numerical) are continents, cell_phone_subscriptions_per_100_people_2012, total_population_in_thousands_2015 and fines_for_tobacco_advertising_2014? (Don’t go by their variable names – look at the data in the summary results to decide.)

  5. Add a new column to your countries data frame that has the difference in ecological footprint between 2012 and 2000. What is the mean of this difference? (Note: this variable will have “missing data”, which means that some of the countries do not have data in this file for one or the other of the years of ecological footprint. By default, R doesn’t calculate a mean unless all the data are present. To tell R to ignore the missing data, add an option to the mean() command that says na.rm=TRUE. We’ll learn more about this later.)


4. A researcher wants to examine data from Japan only and writes the line: japan_data <- filter(countries, country = Japan). Would this line work? If not, make corrections to the code. Then determine the value of life_expectancy_at_birth_female for Japan, as found in japan_data.


5. Using the countries data, create a new data frame called AfricaData that only includes data for countries in Africa.

  1. How many countries are included in AfricaData?

  2. What is the sum of the total_population_in_thousands_2015 in AfricaData?


Last substantive update: 2026-08-21