Starting with Data
Overview
Teaching: 30 min
Exercises: 10 minQuestions
What is a data.frame?
How can I read a complete csv file into R?
How can I get basic summary information about my dataset?
Objectives
Describe what a data frame is.
Load external data from a .csv file into a data frame.
Summarize the contents of a data frame.
Subset and extract values from data frames.
What are data frames and tibbles?
Data frames are the de facto data structure for tabular data in R
, and what
we use for data processing, statistics, and plotting.
A data frame is the representation of data in the format of a table where the columns are vectors that all have the same length. Data frames are analogous to the more familiar spreadsheet in programs such as Excel, with one key difference. Because columns are vectors, each column must contain a single type of data (e.g., characters, integers, factors). For example, here is a figure depicting a data frame comprising a numeric, a character, and a logical vector.
Data frames can be created by hand, but most commonly they are generated by the
functions read_csv()
or read_table()
; in other words, when importing
spreadsheets from your hard drive (or the web). We will now demonstrate how to
import tabular data using read_csv()
.
Presentation of the SAFI Data
SAFI (Studying African Farmer-Led Irrigation) is a study looking at farming and irrigation methods in Tanzania and Mozambique. The survey data was collected through interviews conducted between November 2016 and June 2017. For this lesson, we will be using a subset of the available data. For information about the full dataset, see the dataset description.
We will be using a subset of the cleaned version of the dataset that
was produced through cleaning in OpenRefine (data/SAFI_clean.csv
). Each row holds
information for a single interview respondent, and the columns
represent:
column_name | description |
---|---|
key_id | Added to provide a unique Id for each observation. (The InstanceID field does this as well but it is not as convenient to use) |
village | Village name |
interview_date | Date of interview |
no_membrs | How many members in the household? |
respondent_wall_type | What type of walls does their house have (from list) |
no_meals | How many meals do people in your household normally eat in a day? |
Importing data
You are going load the data in R’s memory using the function read_csv()
from the readr
package, which is part of the tidyverse
; learn
more about the tidyverse
collection of packages
here.
readr
gets installed as part as the tidyverse
installation.
When you load the tidyverse
(library(tidyverse)
), the core packages
(the packages used in most data analyses) get loaded, including readr
.
Before we can use the read_csv()
we need to load the tidyverse package.
Also, if you recall, the missing data is encoded as “NULL” in the dataset.
We’ll tell it to the function, so R will automatically convert all the “NULL”
entries in the dataset into NA
.
The specific path we are using here is dependent on the specific setup. If you have followed the recommendations for structuring your project-folder, it should be this command:
library(tidyverse)
interviews <- read_csv("data/SAFI_clean.csv", na = "NULL")
If you were to type in the code above, it is likely that the read.csv()
function would appear in the automatically populated list of functions. This
function is different from the read_csv()
function, as it is included in the
“base” packages that come pre-installed with R. Overall, read.csv()
behaves
similar to read_csv()
, with a few notable differences. First, read.csv()
coerces column names with spaces and/or special characters to different names
(e.g. interview date
becomes interview.date
). Second, read.csv()
stores data as a data.frame
, where read_csv()
stores data as a tibble
.
We prefer tibbles because they have nice printing properties among other
desirable qualities. Read more about tibbles
here.
The second statement in the code above creates a data frame but doesn’t output
any data because, as you might recall, assignments (<-
) don’t display
anything. (Note, however, that read_csv
may show informational
text about the data frame that is created.) If we want to check that our data
has been loaded, we can see the contents of the data frame by typing its name:
interviews
in the console.
interviews
## Try also
## view(interviews)
## head(interviews)
# A tibble: 131 × 6
key_ID village interview_date no_membrs respondent_wall_type no_meals
<dbl> <chr> <dttm> <dbl> <chr> <dbl>
1 1 God 2016-11-17 00:00:00 3 muddaub 2
2 1 God 2016-11-17 00:00:00 7 muddaub 2
3 3 God 2016-11-17 00:00:00 10 burntbricks 2
4 4 God 2016-11-17 00:00:00 7 burntbricks 2
5 5 God 2016-11-17 00:00:00 7 burntbricks 2
6 6 God 2016-11-17 00:00:00 3 muddaub 2
7 7 God 2016-11-17 00:00:00 6 muddaub 3
8 8 Chirodzo 2016-11-16 00:00:00 12 burntbricks 2
9 9 Chirodzo 2016-11-16 00:00:00 8 burntbricks 3
10 10 Chirodzo 2016-12-16 00:00:00 12 burntbricks 3
# ℹ 121 more rows
Note
read_csv()
assumes that fields are delimited by commas. However, in several countries, the comma is used as a decimal separator and the semicolon (;) is used as a field delimiter. If you want to read in this type of files in R, you can use theread_csv2
function. It behaves exactly likeread_csv
but uses different parameters for the decimal and the field separators. If you are working with another format, they can be both specified by the user. Check out the help forread_csv()
by typing?read_csv
to learn more. There is also theread_tsv()
for tab-separated data files, andread_delim()
allows you to specify more details about the structure of your file.
Note that read_csv()
actually loads the data as a tibble.
A tibble is an extension of R
data frames used by the tidyverse
. When
the data is read using read_csv()
, it is stored in an object of class
tbl_df
, tbl
, and data.frame
. You can see the class of an object with
class(interviews)
[1] "spec_tbl_df" "tbl_df" "tbl" "data.frame"
As a tibble
, the type of data included in each column is listed in an
abbreviated fashion below the column names. For instance, here key_ID
is a
column of floating point numbers (abbreviated <dbl>
for the word ‘double’),
village
is a column of characters (<chr>
) and the interview_date
is a
column in the “date and time” format (<dttm>
).
Inspecting data frames
When calling a tbl_df
object (like interviews
here), there is already a lot
of information about our data frame being displayed such as the number of rows,
the number of columns, the names of the columns, and as we just saw the class of
data stored in each column. However, there are functions to extract this
information from data frames. Here is a non-exhaustive list of some of these
functions. Let’s try them out!
Size:
dim(interviews)
- returns a vector with the number of rows as the first element, and the number of columns as the second element (the dimensions of the object)nrow(interviews)
- returns the number of rowsncol(interviews)
- returns the number of columns
Content:
head(interviews)
- shows the first 6 rowstail(interviews)
- shows the last 6 rows
Names:
names(interviews)
- returns the column names (synonym ofcolnames()
fordata.frame
objects)
Summary:
str(interviews)
- structure of the object and information about the class, length and content of each columnsummary(interviews)
- summary statistics for each columnglimpse(interviews)
- returns the number of columns and rows of the tibble, the names and class of each column, and previews as many values will fit on the screen. Unlike the other inspecting functions listed above,glimpse()
is not a “base R” function so you need to have thedplyr
ortibble
packages loaded to be able to execute it.
Note: most of these functions are “generic.” They can be used on other types of objects besides data frames or tibbles.
Indexing and subsetting data frames
Our interviews
data frame has rows and columns (it has 2 dimensions).
In practice, we may not need the entire data frame; for instance, we may only
be interested in a subset of the observations (the rows) or a particular set
of variables (the columns). If we want to
extract some specific data from it, we need to specify the “coordinates” we
want from it. Row numbers come first, followed by column numbers.
Tip
Indexing a
tibble
with[
or[[
or$
always results in atibble
. However, note this is not true in general for data frames, so be careful! Different ways of specifying these coordinates can lead to results with different classes. This is covered in the Software Carpentry lesson R for Reproducible Scientific Analysis.
## first element in the first column of the tibble
interviews[1, 1]
# A tibble: 1 × 1
key_ID
<dbl>
1 1
## first element in the 6th column of the tibble
interviews[1, 6]
# A tibble: 1 × 1
no_meals
<dbl>
1 2
## first column of the tibble (as a vector)
interviews[[1]]
[1] 1 1 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
[19] 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
[37] 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 21 54
[55] 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 127
[73] 133 152 153 155 178 177 180 181 182 186 187 195 196 197 198 201 202 72
[91] 73 76 83 85 89 101 103 102 78 80 104 105 106 109 110 113 118 125
[109] 119 115 108 116 117 144 143 150 159 160 165 166 167 174 175 189 191 192
[127] 126 193 194 199 200
## first column of the tibble
interviews[1]
# A tibble: 131 × 1
key_ID
<dbl>
1 1
2 1
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10
# ℹ 121 more rows
## first three elements in the 7th column of the tibble
interviews[1:3, 7]
Error in `interviews[1:3, 7]`:
! Can't subset columns past the end.
ℹ Location 7 doesn't exist.
ℹ There are only 6 columns.
## the 3rd row of the tibble
interviews[3, ]
# A tibble: 1 × 6
key_ID village interview_date no_membrs respondent_wall_type no_meals
<dbl> <chr> <dttm> <dbl> <chr> <dbl>
1 3 God 2016-11-17 00:00:00 10 burntbricks 2
## equivalent to head_interviews <- head(interviews)
head_interviews <- interviews[1:6, ]
:
is a special function that creates numeric vectors of integers in increasing
or decreasing order, test 1:10
and 10:1
for instance.
You can also exclude certain indices of a data frame using the “-
” sign:
interviews[, -1] # The whole tibble, except the first column
# A tibble: 131 × 5
village interview_date no_membrs respondent_wall_type no_meals
<chr> <dttm> <dbl> <chr> <dbl>
1 God 2016-11-17 00:00:00 3 muddaub 2
2 God 2016-11-17 00:00:00 7 muddaub 2
3 God 2016-11-17 00:00:00 10 burntbricks 2
4 God 2016-11-17 00:00:00 7 burntbricks 2
5 God 2016-11-17 00:00:00 7 burntbricks 2
6 God 2016-11-17 00:00:00 3 muddaub 2
7 God 2016-11-17 00:00:00 6 muddaub 3
8 Chirodzo 2016-11-16 00:00:00 12 burntbricks 2
9 Chirodzo 2016-11-16 00:00:00 8 burntbricks 3
10 Chirodzo 2016-12-16 00:00:00 12 burntbricks 3
# ℹ 121 more rows
interviews[-c(7:131), ] # Equivalent to head(interviews)
# A tibble: 6 × 6
key_ID village interview_date no_membrs respondent_wall_type no_meals
<dbl> <chr> <dttm> <dbl> <chr> <dbl>
1 1 God 2016-11-17 00:00:00 3 muddaub 2
2 1 God 2016-11-17 00:00:00 7 muddaub 2
3 3 God 2016-11-17 00:00:00 10 burntbricks 2
4 4 God 2016-11-17 00:00:00 7 burntbricks 2
5 5 God 2016-11-17 00:00:00 7 burntbricks 2
6 6 God 2016-11-17 00:00:00 3 muddaub 2
tibble
s can be subset by calling indices (as shown previously), but also by
calling their column names directly:
interviews["village"] # Result is a tibble
interviews[, "village"] # Result is a tibble
interviews[["village"]] # Result is a vector
interviews$village # Result is a vector
In RStudio, you can use the autocompletion feature to get the full and correct names of the columns.
Exercise
Create a tibble (
interviews_100
) containing only the data in row 100 of theinterviews
dataset.Notice how
nrow()
gave you the number of rows in the tibble?
- Use that number to pull out just that last row in the tibble.
- Compare that with what you see as the last row using
tail()
to make sure it’s meeting expectations.- Pull out that last row using
nrow()
instead of the row number.- Create a new tibble (
interviews_last
) from that last row.Using the number of rows in the interviews dataset that you found in question 2, extract the row that is in the middle of the dataset. Store the content of this middle row in an object named
interviews_middle
. (hint: This dataset has an odd number of rows, so finding the middle is a bit trickier than dividing n_rows by 2. Use the median( ) function and what you’ve learned about sequences in R to extract the middle row!Combine
nrow()
with the-
notation above to reproduce the behavior ofhead(interviews)
, keeping just the first through 6th rows of the interviews dataset.Solution
## 1. interviews_100 <- interviews[100, ] ## 2. # Saving `n_rows` to improve readability and reduce duplication n_rows <- nrow(interviews) interviews_last <- interviews[n_rows, ] ## 3. interviews_middle <- interviews[median(1:n_rows), ] ## 4. interviews_head <- interviews[-(7:n_rows), ]
Key Points
Use read_csv to read tabular data in R.