Starting with Data
Last updated on 2024-11-19 | Edit this page
Estimated time: 80 minutes
The two main goals for this lessons are:
- To make sure that learners are comfortable with working with data frames, and can use the bracket notation to select slices/columns.
Overview
Questions
- 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?
- How are dates represented in R and how can I change the format?
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 values from data frames.
- Change how character strings are handled in a data frame.
- Examine and change date formats.
What are data frames?
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 Clicks YouTube data
We will be working with a dataset of 200 Youtube posts. Here is some background to the dataset.
An initial dataset of 314 posts were returned by the YouTube API in response to the following query: “Query: clicks south africa* hair (ad OR advertisement) -click” covering videos posted during the period 2020 - 2023 The dataset was prepared using a spreadsheet and OpenRefine to identify missing values, change variable names to “snake case”, change tags and topics to lowercase, and to use semicolons to separate tags and topics. Owing to the ambiguity of the keyword “clicks”, many irrelevant results were returned. The dataset was reviewed and any posts which were not related to the controversy, or which did not relate to issues about body politics and racism were excluded. This resulted in a dataset of 200 posts selected for computational analysis. In case you need a refresher, here are some details about how YouTube API derives the variables in this dataset.
We will be using a subset of the dataset that was prepared in
OpenRefine (data/youtube-27082024-open-refine-200-na.csv
).
In this dataset, the missing data is encoded as “na”, each row holds
information for a single YouTube video, and the columns represent |
column_name | description |
---|---|
‘position’ | |
‘randomise’ | |
‘channel_id’ | |
‘channel_title’ | |
‘video_id’ | |
‘url’ | |
‘published_at’ | |
‘published_at_sql’ | |
‘year’ | |
‘month’ | |
‘day’ | |
‘video_title’ | |
‘video_description’ | |
‘tags’ | |
‘video_category_label’ | |
‘topic_categories’ | |
‘duration_sec’ | |
‘definition’ | |
‘caption’ | |
‘default_language’ | |
‘default_l_audio_language’ | |
‘thumbnail_maxres’ | |
‘licensed_content’ | |
‘location_description’ | |
‘view_count’ | |
‘like_count’ | |
‘favorite_count’ | |
‘comment_count’ | |
‘controversy’ | |
‘clicks’ | |
‘body_politics’ | |
‘eff_lead’ |
Importing data
You are going to 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 proceeding, however, this is a good opportunity to talk about
conflicts. Certain packages we load can end up introducing function
names that are already in use by pre-loaded R packages. For instance,
when we load the tidyverse package below, we will introduce two
conflicting functions: filter()
and lag()
.
This happens because filter
and lag
are
already functions used by the stats package (already pre-loaded in R).
What will happen now is that if we, for example, call the
filter()
function, R will use the
dplyr::filter()
version and not the
stats::filter()
one. This happens because, if conflicted,
by default R uses the function from the most recently loaded package.
Conflicted functions may cause you some trouble in the future, so it is
important that we are aware of them so that we can properly handle them,
if we want.
To do so, we just need the following functions from the conflicted package:
-
conflicted::conflict_scout()
: Shows us any conflicted functions. -
conflict_prefer("function", "package_prefered")
: Allows us to choose the default function we want from now on.
It is also important to know that we can, at any time, just call the
function directly from the package we want, such as
stats::filter()
.
Even with the use of an RStudio project, it can be difficult to learn
how to specify paths to file locations. Enter the here
package! The here package creates paths relative to the top-level
directory (your RStudio project). These relative paths work
regardless of where the associated source file lives inside
your project, like analysis projects with data and reports in different
subdirectories. This is an important contrast to using
setwd()
, which depends on the way you order your files on
your computer.
Before we can use the read_csv()
and here()
functions, we need to load the tidyverse and here packages.
Also, if you recall, the missing data is encoded as “na” in the
dataset. We’ll tell it to the function, so R will automatically convert
all the “na” entries in the dataset into NA
.
R
library(tidyverse)
library(here)
videos <- read_csv(
here("data", "youtube-27082024-open-refine-200-na.csv"),
na = "na")
In the above code, we notice the here()
function takes
folder and file names as inputs (e.g., "data"
,
"youtube-27082024-open-refine-200-na.csv"
), each enclosed
in quotations (""
) and separated by a comma. The
here()
will accept as many names as are necessary to
navigate to a particular file (e.g.,
here("analysis", "data", "surveys", "clean", "youtube-27082024-open-refine-200-na.csv")
).
The here()
function can accept the folder and file names
in an alternate format, using a slash (“/”) rather than commas to
separate the names. The two methods are equivalent, so that
here("data", "youtube-27082024-open-refine-200-na.csv")
and
here("data/youtube-27082024-open-refine-200-na.csv")
produce the same result. (The slash is used on all operating systems;
backslashes are not used.)
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
different kind of data frame called 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:
videos
in the console.
R
videos
## Try also
## view(videos)
## head(videos)
OUTPUT
# A tibble: 200 × 32
position randomise channel_id channel_title video_id url
<dbl> <dbl> <chr> <chr> <chr> <chr>
1 112 409 UCI3RT5PGmdi1KVp9FG_CneA eNCA iPUAl1j… http…
2 50 702 UCI3RT5PGmdi1KVp9FG_CneA eNCA YUmIAd_… http…
3 149 313 UCMwDXpWEVQVw4ZF7z-E4NoA StellenboschNews … v8XfpOi… http…
4 167 384 UCsqKkYLOaJ9oBwq9rxFyZMw SOUTH AFRICAN POL… lnLdo2k… http…
5 195 606 UC5G5Dy8-mmp27jo6Frht7iQ Umgosi Entertainm… XN6toca… http…
6 213 423 UCC1udUghY9dloGMuvZzZEzA The Tea World rh2Nz78… http…
7 145 452 UCaCcVtl9O3h5en4m-_edhZg Celeb LaLa Land 1l5GZ0N… http…
8 315 276 UCAurTjb6Ewz21vjfTs1wZxw NOSIPHO NZAMA j4Y022C… http…
9 190 321 UCBlX1mnsIFZRqsyRNvpW_rA Zandile Mhlambi gf2YNN6… http…
10 214 762 UClY87IoUANFZtswyC9GeecQ Beauty recipes AGJmRd4… http…
# ℹ 190 more rows
# ℹ 26 more variables: published_at <dttm>, published_at_sql <chr>, year <dbl>,
# month <dbl>, day <dbl>, video_title <chr>, video_description <chr>,
# tags <chr>, video_category_label <chr>, topic_categories <chr>,
# duration_sec <dbl>, definition <chr>, caption <lgl>,
# default_language <chr>, default_l_audio_language <chr>,
# thumbnail_maxres <chr>, licensed_content <dbl>, …
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 the read_csv2
function. It behaves exactly like read_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 for read_csv()
by typing
?read_csv
to learn more. There is also the
read_tsv()
for tab-separated data files, and
read_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
R
class(videos)
OUTPUT
[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’),
channel_title
is a column of characters
(<chr>
) and the published_at
is a column
in the “date and time” format (<dttm>
).
Inspecting data frames
When calling a tbl_df
object (like videos
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(videos)
- 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(videos)
- returns the number of rows -
ncol(videos)
- returns the number of columns
Content:
-
head(videos)
- shows the first 6 rows -
tail(videos)
- shows the last 6 rows
Names:
-
names(videos)
- returns the column names (synonym ofcolnames()
fordata.frame
objects)
Summary:
-
str(videos)
- structure of the object and information about the class, length and content of each column -
summary(videos)
- summary statistics for each column -
glimpse(videos)
- 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.
Subsetting data frames
Our videos
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
access some specific data from it, we need to specify the “coordinates”
(i.e., indices) we want from it. Row numbers come first, followed by
column numbers.
Tip
Subsetting a tibble
with [
always results
in a tibble
. 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.
R
## first element in the first column of the tibble
videos[1, 1]
OUTPUT
# A tibble: 1 × 1
position
<dbl>
1 112
R
## first element in the 6th column of the tibble
videos[1, 6]
OUTPUT
# A tibble: 1 × 1
url
<chr>
1 https://www.youtube.com/watch?v=iPUAl1jywdU
R
## first column of the tibble (as a vector)
videos[[1]]
OUTPUT
[1] 112 50 149 167 195 213 145 315 190 214 263 30 63 65 2 3 4 7
[19] 9 13 14 16 17 24 36 66 72 132 118 299 40 43 46 54 58 80
[37] 88 93 99 103 123 124 152 169 174 181 200 215 217 218 227 236 244 250
[55] 253 272 282 284 291 257 1 29 38 92 135 139 144 153 170 176 183 187
[73] 194 197 207 221 226 262 271 273 278 290 294 295 306 307 311 11 44 57
[91] 60 64 69 70 78 96 107 121 130 308 5 8 10 21 23 25 28 34
[109] 52 56 61 62 71 73 74 81 86 91 98 100 110 120 126 131 133 136
[127] 141 142 143 146 150 154 159 161 164 166 173 178 182 184 191 192 202 208
[145] 210 219 220 223 231 233 234 238 243 245 261 264 275 276 277 283 285 288
[163] 292 300 304 305 309 313 6 15 18 19 22 27 31 33 35 37 39 41
[181] 42 67 77 82 83 85 87 94 158 168 193 254 293 104 155 162 204 209
[199] 246 255
R
## first column of the tibble
videos[1]
OUTPUT
# A tibble: 200 × 1
position
<dbl>
1 112
2 50
3 149
4 167
5 195
6 213
7 145
8 315
9 190
10 214
# ℹ 190 more rows
R
## first three elements in the 7th column of the tibble
videos[1:3, 7]
OUTPUT
# A tibble: 3 × 1
published_at
<dttm>
1 2020-09-14 16:16:44
2 2020-09-11 05:34:52
3 2020-09-10 11:59:51
R
## the 3rd row of the tibble
videos[3, ]
OUTPUT
# A tibble: 1 × 32
position randomise channel_id channel_title video_id url published_at
<dbl> <dbl> <chr> <chr> <chr> <chr> <dttm>
1 149 313 UCMwDXpWE… Stellenbosch… v8XfpOi… http… 2020-09-10 11:59:51
# ℹ 25 more variables: published_at_sql <chr>, year <dbl>, month <dbl>,
# day <dbl>, video_title <chr>, video_description <chr>, tags <chr>,
# video_category_label <chr>, topic_categories <chr>, duration_sec <dbl>,
# definition <chr>, caption <lgl>, default_language <chr>,
# default_l_audio_language <chr>, thumbnail_maxres <chr>,
# licensed_content <dbl>, location_description <chr>, view_count <dbl>,
# like_count <dbl>, favorite_count <dbl>, comment_count <dbl>, …
R
## equivalent to head_videos <- head(videos)
head_videos <- videos[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:
R
videos[, -1] # The whole tibble, except the first column
OUTPUT
# A tibble: 200 × 31
randomise channel_id channel_title video_id url published_at
<dbl> <chr> <chr> <chr> <chr> <dttm>
1 409 UCI3RT5PGmdi1KVp9… eNCA iPUAl1j… http… 2020-09-14 16:16:44
2 702 UCI3RT5PGmdi1KVp9… eNCA YUmIAd_… http… 2020-09-11 05:34:52
3 313 UCMwDXpWEVQVw4ZF7… Stellenbosch… v8XfpOi… http… 2020-09-10 11:59:51
4 384 UCsqKkYLOaJ9oBwq9… SOUTH AFRICA… lnLdo2k… http… 2020-09-07 11:08:43
5 606 UC5G5Dy8-mmp27jo6… Umgosi Enter… XN6toca… http… 2020-09-08 12:45:36
6 423 UCC1udUghY9dloGMu… The Tea World rh2Nz78… http… 2020-09-09 15:03:43
7 452 UCaCcVtl9O3h5en4m… Celeb LaLa L… 1l5GZ0N… http… 2020-09-08 18:43:58
8 276 UCAurTjb6Ewz21vjf… NOSIPHO NZAMA j4Y022C… http… 2021-06-22 10:18:51
9 321 UCBlX1mnsIFZRqsyR… Zandile Mhla… gf2YNN6… http… 2020-09-07 18:50:42
10 762 UClY87IoUANFZtswy… Beauty recip… AGJmRd4… http… 2022-11-26 09:08:54
# ℹ 190 more rows
# ℹ 25 more variables: published_at_sql <chr>, year <dbl>, month <dbl>,
# day <dbl>, video_title <chr>, video_description <chr>, tags <chr>,
# video_category_label <chr>, topic_categories <chr>, duration_sec <dbl>,
# definition <chr>, caption <lgl>, default_language <chr>,
# default_l_audio_language <chr>, thumbnail_maxres <chr>,
# licensed_content <dbl>, location_description <chr>, view_count <dbl>, …
R
videos[-c(7:131), ] # Equivalent to head(videos)
OUTPUT
# A tibble: 75 × 32
position randomise channel_id channel_title video_id url
<dbl> <dbl> <chr> <chr> <chr> <chr>
1 112 409 UCI3RT5PGmdi1KVp9FG_CneA eNCA iPUAl1j… http…
2 50 702 UCI3RT5PGmdi1KVp9FG_CneA eNCA YUmIAd_… http…
3 149 313 UCMwDXpWEVQVw4ZF7z-E4NoA StellenboschNews … v8XfpOi… http…
4 167 384 UCsqKkYLOaJ9oBwq9rxFyZMw SOUTH AFRICAN POL… lnLdo2k… http…
5 195 606 UC5G5Dy8-mmp27jo6Frht7iQ Umgosi Entertainm… XN6toca… http…
6 213 423 UCC1udUghY9dloGMuvZzZEzA The Tea World rh2Nz78… http…
7 154 806 UCzvl9exW-o8IsySPU-ezgoQ TheKingzRSA m4KitiL… http…
8 159 496 UC5G5Dy8-mmp27jo6Frht7iQ Umgosi Entertainm… QigpfXd… http…
9 161 678 UCFnUvf-Lg1nIc5wvl9lx8zQ Mzansi Hotspot cuXJVNI… http…
10 164 181 UCITau47AeZDY9sO77TA3OUA ITV News aqSmGuy… http…
# ℹ 65 more rows
# ℹ 26 more variables: published_at <dttm>, published_at_sql <chr>, year <dbl>,
# month <dbl>, day <dbl>, video_title <chr>, video_description <chr>,
# tags <chr>, video_category_label <chr>, topic_categories <chr>,
# duration_sec <dbl>, definition <chr>, caption <lgl>,
# default_language <chr>, default_l_audio_language <chr>,
# thumbnail_maxres <chr>, licensed_content <dbl>, …
tibble
s can be subset by calling indices (as shown
previously), but also by calling their column names directly:
R
videos["channel_title"] # Result is a tibble
videos[, "channel_title"] # Result is a tibble
videos[["channel_title"]] # Result is a vector
videos$channel_title # 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 (
videos_100
) containing only the data in row 100 of thevideos
dataset.
Now, continue using videos
for each of the following
activities:
- 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 (
videos_last
) from that last row.
Using the number of rows in the videos 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
videos_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(videos)
, keeping just the first through 6th rows of the videos dataset.
R
## 1.
videos_100 <- videos[100, ]
## 2.
# Saving `n_rows` to improve readability and reduce duplication
n_rows <- nrow(videos)
videos_last <- videos[n_rows, ]
## 3.
videos_middle <- videos[median(1:n_rows), ]
ERROR
Error in `videos[median(1:n_rows), ]`:
! Can't subset rows with `median(1:n_rows)`.
✖ Can't convert from `i` <double> to <integer> due to loss of precision.
R
## 4.
videos_head <- videos[-(7:n_rows), ]
Formatting Dates
One of the most common issues that new (and experienced!) R users
have is converting date and time information into a variable that is
appropriate and usable during analyses. A best practice for dealing with
date data is to ensure that each component of your date is available as
a separate variable. In our dataset, we have a column
published_at
which contains information about the year,
month, and day that the interview was conducted. Let’s convert those
dates into three separate columns.
R
str(videos)
We are going to use the package
lubridate
, , which is included in the
tidyverse
installation and should be
loaded by default. However, if we deal with older versions of tidyverse
(2022 and ealier), we can manually load it by typing
library(lubridate)
.
If necessary, start by loading the required package:
R
library(lubridate)
The lubridate function ymd()
takes a vector representing
year, month, and day, and converts it to a Date
vector.
Date
is a class of data recognized by R as being a date and
can be manipulated as such. The argument that the function requires is
flexible, but, as a best practice, is a character vector formatted as
“YYYY-MM-DD”.
Let’s extract our published_at
column and inspect the
structure:
R
dates <- videos$published_at
str(dates)
OUTPUT
POSIXct[1:200], format: "2020-09-14 16:16:44" "2020-09-11 05:34:52" "2020-09-10 11:59:51" ...
When we imported the data in R, read_csv()
recognized
that this column contained date information. We can now use the
day()
, month()
and year()
functions to extract this information from the date, and create new
columns in our data frame to store it:
R
videos$day <- day(dates)
videos$month <- month(dates)
videos$year <- year(dates)
videos
OUTPUT
# A tibble: 200 × 32
position randomise channel_id channel_title video_id url
<dbl> <dbl> <chr> <chr> <chr> <chr>
1 112 409 UCI3RT5PGmdi1KVp9FG_CneA eNCA iPUAl1j… http…
2 50 702 UCI3RT5PGmdi1KVp9FG_CneA eNCA YUmIAd_… http…
3 149 313 UCMwDXpWEVQVw4ZF7z-E4NoA StellenboschNews … v8XfpOi… http…
4 167 384 UCsqKkYLOaJ9oBwq9rxFyZMw SOUTH AFRICAN POL… lnLdo2k… http…
5 195 606 UC5G5Dy8-mmp27jo6Frht7iQ Umgosi Entertainm… XN6toca… http…
6 213 423 UCC1udUghY9dloGMuvZzZEzA The Tea World rh2Nz78… http…
7 145 452 UCaCcVtl9O3h5en4m-_edhZg Celeb LaLa Land 1l5GZ0N… http…
8 315 276 UCAurTjb6Ewz21vjfTs1wZxw NOSIPHO NZAMA j4Y022C… http…
9 190 321 UCBlX1mnsIFZRqsyRNvpW_rA Zandile Mhlambi gf2YNN6… http…
10 214 762 UClY87IoUANFZtswyC9GeecQ Beauty recipes AGJmRd4… http…
# ℹ 190 more rows
# ℹ 26 more variables: published_at <dttm>, published_at_sql <chr>, year <dbl>,
# month <dbl>, day <int>, video_title <chr>, video_description <chr>,
# tags <chr>, video_category_label <chr>, topic_categories <chr>,
# duration_sec <dbl>, definition <chr>, caption <lgl>,
# default_language <chr>, default_l_audio_language <chr>,
# thumbnail_maxres <chr>, licensed_content <dbl>, …
Notice the three new columns at the end of our data frame.
In our example above, the published_at
column was read
in correctly as a Date
variable but generally that is not
the case. Date columns are often read in as character
variables and one can use the as_date()
function to convert
them to the appropriate Date/POSIXct
format.
Let’s say we have a vector of dates in character format:
R
char_dates <- c("7/31/2012", "8/9/2014", "4/30/2016")
str(char_dates)
OUTPUT
chr [1:3] "7/31/2012" "8/9/2014" "4/30/2016"
We can convert this vector to dates as :
R
as_date(char_dates, format = "%m/%d/%Y")
OUTPUT
[1] "2012-07-31" "2014-08-09" "2016-04-30"
Argument format
tells the function the order to parse
the characters and identify the month, day and year. The format above is
the equivalent of mm/dd/yyyy. A wrong format can lead to parsing errors
or incorrect results.
For example, observe what happens when we use a lower case y instead of upper case Y for the year.
R
as_date(char_dates, format = "%m/%d/%y")
WARNING
Warning: 3 failed to parse.
OUTPUT
[1] NA NA NA
Here, the %y
part of the format stands for a two-digit
year instead of a four-digit year, and this leads to parsing errors.
Or in the following example, observe what happens when the month and day elements of the format are switched.
R
as_date(char_dates, format = "%d/%m/%y")
WARNING
Warning: 3 failed to parse.
OUTPUT
[1] NA NA NA
Since there is no month numbered 30 or 31, the first and third dates cannot be parsed.
We can also use functions ymd()
, mdy()
or
dmy()
to convert character variables to date.
R
mdy(char_dates)
OUTPUT
[1] "2012-07-31" "2014-08-09" "2016-04-30"
Key Points
- Use read_csv to read tabular data in R.