Chapter 4 Data Cleaning and Data Management
Data cleaning is one of the most important stages of any analytical workflow. In real-world projects, raw data are rarely ready for analysis immediately after import. Datasets often contain inconsistent variable names, missing values, formatting issues, duplicate records, encoding problems, or variables stored in unsuitable formats.
This chapter introduces several common data management tasks using the tidyverse. You will learn how to import multiple file types, inspect datasets, identify common data quality issues, reshape data into tidy formats, and recode variables into more useful forms.
Although the datasets used in this course are simplified for teaching purposes, the workflow closely reflects many real-world data cleaning tasks encountered in epidemiology, environmental health, and population data analysis.
4.1 Preparing the R environment
Before beginning any analysis, the required libraries should be loaded. For this chapter, we will primarily use packages from the tidyverse ecosystem, along with ggplot2 and readxl.
If these packages are not already installed on your computer, they can be installed using:
Once installed, the libraries can be loaded into the R session.
4.2 Exploring project files
Before importing data, it is often useful to inspect the contents of the project folders. The list.files() function allows you to display all files stored within a directory.
## [1] "Corr_2016 copy.csv"
## [2] "Corr_2016.csv"
## [3] "Data Dictionary - Blank.xlsx"
## [4] "Data Dictionary - Filled.xlsx"
## [5] "deaths_2016.xlsx"
## [6] "Population_Estimates.csv"
## [7] "titanic.csv"
## [8] "Titantic_DataDictionary.xlsx"
## [9] "Weather_data.csv"
You can also search for specific file types using the pattern argument.
## [1] "Corr_2016 copy.csv" "Corr_2016.csv"
## [3] "Population_Estimates.csv" "titanic.csv"
## [5] "Weather_data.csv"
## [1] "Data Dictionary - Blank.xlsx"
## [2] "Data Dictionary - Filled.xlsx"
## [3] "deaths_2016.xlsx"
## [4] "Titantic_DataDictionary.xlsx"
Functions such as list.files() are frequently overlooked by beginners, but they become extremely useful when working with large projects that contain many datasets or outputs.
Some datasets may also be stored in compressed .zip files. These files should typically be extracted before analysis.
The exdir argument specifies the folder where the extracted files should be saved. Since files only need to be unzipped once, this code is often commented out after the extraction is complete.
When working in R Markdown documents, avoid repeatedly running file extraction code unless necessary. Re-extracting files every time the document is knitted can slow down workflows and create duplicate files.
4.3 Description of the datasets
This course uses four datasets that simulate a simplified population health analysis workflow.
The mortality dataset contains records of deaths, including demographic information and ICD-10 cause-of-death codes. Although the dataset is based on realistic structures and counts, the data themselves are fictitious for teaching purposes.
The population dataset contains estimates of population counts by age group, sex, and Health Service Delivery Area in British Columbia.
The correspondence dataset links lower geographic units to larger health regions and service delivery areas. Correspondence files are commonly used in population health and environmental research because they allow different geographic datasets to be connected.
The environmental dataset contains weather-related variables by postal code, including measures related to temperature and cold exposure.
The overall analytical goal is to combine these datasets in order to calculate mortality rates and explore potential environmental relationships.
4.4 Importing data into R
Different file formats require different import functions. The readr package is commonly used for .csv files, while Excel files require functions from the readxl package.
The following code imports the datasets used in this chapter.
mort <- read_excel(
"./Raw Data/deaths_2016.xlsx"
)
pop <- read_csv(
"./Raw Data/Population_Estimates.csv"
)
corr <- read_csv(
"./Raw Data/Corr_2016.csv",
locale = readr::locale(
encoding = "latin1"
)
)
env <- read_csv(
"./Raw Data/Weather_data.csv"
)Notice that the correspondence file uses a specific text encoding. Encoding problems are common when datasets contain accented characters or multilingual text.
4.5 Inspecting datasets
After importing data, it is important to examine the structure and contents of each dataset before beginning analysis.
Several functions provide useful high-level summaries.
## tibble [60,143 × 13] (S3: tbl_df/tbl/data.frame)
## $ ID : num [1:60143] 1e+06 1e+06 1e+06 1e+06 1e+06 ...
## $ Sex : chr [1:60143] "M" "F" "M" "M" ...
## $ Cause_of_death : chr [1:60143] "D47" "C34" "C26" "C16" ...
## $ dbuid2016 : num [1:60143] 1.31e+10 4.81e+10 3.52e+10 2.47e+10 2.46e+10 ...
## $ Location_of_death: num [1:60143] 5 3 6 1 6 5 4 1 3 2 ...
## $ Marital_status : num [1:60143] 1 1 2 1 1 1 1 1 1 2 ...
## $ Postalcode : chr [1:60143] "E1A1E9" "T2E0T2" "M4J1L1" "H9H1B4" ...
## $ B_year : num [1:60143] 1943 1943 1953 1923 1931 ...
## $ B_month : num [1:60143] 7 5 8 6 8 11 5 8 7 2 ...
## $ B_day : num [1:60143] 8 22 9 14 8 4 20 18 23 7 ...
## $ D_year : num [1:60143] 2016 2016 2016 2016 2016 ...
## $ D_month : num [1:60143] 9 12 4 11 4 2 2 6 5 1 ...
## $ D_day : num [1:60143] 22 14 21 21 4 6 9 13 25 16 ...
## ID Sex Cause_of_death
## Min. :1000001 Length:60143 Length:60143
## 1st Qu.:1016187 Class :character Class :character
## Median :1034842 Mode :character Mode :character
## Mean :1036699
## 3rd Qu.:1056221
## Max. :1080708
##
## dbuid2016 Location_of_death Marital_status
## Min. :1.001e+10 Min. :1.000 Min. :1.0
## 1st Qu.:2.466e+10 1st Qu.:2.000 1st Qu.:1.0
## Median :3.521e+10 Median :4.000 Median :1.0
## Mean :3.642e+10 Mean :3.498 Mean :1.5
## 3rd Qu.:4.715e+10 3rd Qu.:5.000 3rd Qu.:2.0
## Max. :6.208e+10 Max. :6.000 Max. :2.0
## NA's :4
## Postalcode B_year B_month
## Length:60143 Min. :1827 Min. : 1.000
## Class :character 1st Qu.:1933 1st Qu.: 4.000
## Mode :character Median :1941 Median : 7.000
## Mean :1942 Mean : 6.513
## 3rd Qu.:1951 3rd Qu.:10.000
## Max. :2016 Max. :28.000
## NA's :24 NA's :26
## B_day D_year D_month
## Min. : 1.00 Min. :2016 Min. : 1.000
## 1st Qu.: 8.00 1st Qu.:2016 1st Qu.: 4.000
## Median :16.00 Median :2016 Median : 7.000
## Mean :15.74 Mean :2016 Mean : 6.521
## 3rd Qu.:23.00 3rd Qu.:2016 3rd Qu.: 9.000
## Max. :31.00 Max. :2016 Max. :21.000
## NA's :26
## D_day
## Min. : 1.00
## 1st Qu.: 8.00
## Median :16.00
## Mean :15.78
## 3rd Qu.:23.00
## Max. :31.00
##
## Rows: 51
## Columns: 25
## $ ...1 <dbl> 0, 0, 0, 11, 11, 11…
## $ `Health Service Delivery Area` <chr> "British Columbia",…
## $ Year <dbl> 2016, 2016, 2016, 2…
## $ Gender <chr> "M", "F", "T", "M",…
## $ `<1` <dbl> 22997, 21760, 44757…
## $ `04-Jan` <dbl> 93211, 88300, 18151…
## $ `09-May` <dbl> 121341, 112777, 234…
## $ `14-Oct` <dbl> 119586, 112424, 232…
## $ `15-19` <dbl> 140451, 132226, 272…
## $ `20-24` <dbl> 170968, 156161, 327…
## $ `25-29` <dbl> 159609, 159389, 318…
## $ `30-34` <dbl> 162416, 166826, 329…
## $ `35-39` <dbl> 155936, 157628, 313…
## $ `40-44` <dbl> 151311, 154187, 305…
## $ `45-49` <dbl> 161823, 166603, 328…
## $ `50-54` <dbl> 172367, 179295, 351…
## $ `55-59` <dbl> 172667, 178878, 351…
## $ `60-64` <dbl> 156654, 159977, 316…
## $ `65-69` <dbl> 139068, 142469, 281…
## $ `70-74` <dbl> 99527, 103484, 2030…
## $ `75-79` <dbl> 69574, 77160, 14673…
## $ `80-84` <dbl> 49268, 57639, 10690…
## $ `85-89` <dbl> 28898, 40789, 69687…
## $ `90+` <dbl> 13366, 28648, 42014…
## $ Total <dbl> 2361038, 2396620, 4…
## ...1 Health Service Delivery Area
## Min. : 0.00 Length:51
## 1st Qu.:14.00 Class :character
## Median :31.00 Mode :character
## Mean :29.06
## 3rd Qu.:42.00
## Max. :53.00
## Year Gender <1
## Min. :2016 Length:51 Min. : 276
## 1st Qu.:2016 Class :character 1st Qu.: 714
## Median :2016 Mode :character Median : 1425
## Mean :2016 Mean : 3510
## 3rd Qu.:2016 3rd Qu.: 2802
## Max. :2016 Max. :44757
## 04-Jan 09-May 14-Oct
## Min. : 1356 Min. : 1755 Min. : 1811
## 1st Qu.: 3118 1st Qu.: 4194 1st Qu.: 3910
## Median : 5663 Median : 8002 Median : 7808
## Mean : 14236 Mean : 18362 Mean : 18197
## 3rd Qu.: 11025 3rd Qu.: 15384 3rd Qu.: 15350
## Max. :181511 Max. :234118 Max. :232010
## 15-19 20-24 25-29
## Min. : 1919 Min. : 1577 Min. : 1640
## 1st Qu.: 4240 1st Qu.: 4214 1st Qu.: 4169
## Median : 8836 Median : 9193 Median : 8646
## Mean : 21386 Mean : 25657 Mean : 25019
## 3rd Qu.: 17866 3rd Qu.: 22940 3rd Qu.: 21291
## Max. :272677 Max. :327129 Max. :318998
## 30-34 35-39 40-44
## Min. : 2069 Min. : 2092 Min. : 2008
## 1st Qu.: 4415 1st Qu.: 4572 1st Qu.: 4483
## Median : 8830 Median : 9490 Median : 9551
## Mean : 25823 Mean : 24593 Mean : 23961
## 3rd Qu.: 22644 3rd Qu.: 21896 3rd Qu.: 21345
## Max. :329242 Max. :313564 Max. :305498
## 45-49 50-54 55-59
## Min. : 2040 Min. : 2137 Min. : 2038
## 1st Qu.: 4859 1st Qu.: 5431 1st Qu.: 5684
## Median : 9930 Median : 10666 Median : 10972
## Mean : 25759 Mean : 27581 Mean : 27572
## 3rd Qu.: 23211 3rd Qu.: 24455 3rd Qu.: 22498
## Max. :328426 Max. :351662 Max. :351545
## 60-64 65-69 70-74
## Min. : 1550 Min. : 1119 Min. : 784
## 1st Qu.: 5349 1st Qu.: 5006 1st Qu.: 3628
## Median : 10698 Median : 10011 Median : 7256
## Mean : 24834 Mean : 22081 Mean : 15922
## 3rd Qu.: 19486 3rd Qu.: 16891 3rd Qu.: 12128
## Max. :316631 Max. :281537 Max. :203011
## 75-79 80-84 85-89
## Min. : 538 Min. : 351 Min. : 196
## 1st Qu.: 2332 1st Qu.: 1612 1st Qu.: 973
## Median : 5026 Median : 3867 Median : 2636
## Mean : 11509 Mean : 8385 Mean : 5466
## 3rd Qu.: 9268 3rd Qu.: 6582 3rd Qu.: 4543
## Max. :146734 Max. :106907 Max. :69687
## 90+ Total
## Min. : 114.0 Min. : 32171
## 1st Qu.: 536.5 1st Qu.: 70681
## Median : 1394.0 Median : 145321
## Mean : 3295.2 Mean : 373150
## 3rd Qu.: 2955.0 3rd Qu.: 329569
## Max. :42014.0 Max. :4757658
## Rows: 420,963
## Columns: 6
## $ dbuid2016 <dbl> 10010165001, 10010165002, 100101650…
## $ csduid2016 <dbl> 1001519, 1001519, 1001519, 1001519,…
## $ hruid2017 <dbl> 1011, 1011, 1011, 1011, 1011, 1011,…
## $ hrname_english <chr> "Eastern Regional Health Authority"…
## $ hrname_french <chr> "Eastern Regional Health Authority"…
## $ dbpop2016 <dbl> 160, 25, 268, 53, 71, 217, 39, 120,…
## dbuid2016 csduid2016 hruid2017
## Min. :1.001e+10 Min. :1001101 Min. :1011
## 1st Qu.:2.466e+10 1st Qu.:2466023 1st Qu.:2414
## Median :3.558e+10 Median :3558090 Median :3595
## Mean :3.856e+10 Mean :3855572 Mean :3856
## 3rd Qu.:4.806e+10 3rd Qu.:4806016 3rd Qu.:4832
## Max. :6.208e+10 Max. :6208098 Max. :6201
##
## hrname_english hrname_french dbpop2016
## Length:420963 Length:420963 Min. : 0.00
## Class :character Class :character 1st Qu.: 5.00
## Mode :character Mode :character Median : 26.00
## Mean : 69.44
## 3rd Qu.: 77.00
## Max. :7607.00
## NA's :35
## spc_tbl_ [116,011 × 8] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ POSTALCODE12: chr [1:116011] "V0C1E0" "V0C1W0" "V0C2X0" "V0C2Z0" ...
## $ WTHNRC12_01 : num [1:116011] 27.1 30.3 28.2 28.7 23.1 ...
## $ WTHNRC12_02 : num [1:116011] -46.7 -46 -42.3 -44.9 -35 ...
## $ WTHNRC12_03 : num [1:116011] -2.605 -1.868 -1.963 -1.286 0.338 ...
## $ WTHNRC12_04 : num [1:116011] 2.91 3.92 4.01 4.32 4.33 ...
## $ WTHNRC12_05 : num [1:116011] -8.12 -7.65 -7.93 -6.89 -3.65 ...
## $ WTHNRC12_06 : num [1:116011] 11.04 11.57 11.94 11.2 7.98 ...
## $ WTHNRC12_07 : num [1:116011] 171 198 248 175 310 ...
## - attr(*, "spec")=
## .. cols(
## .. POSTALCODE12 = col_character(),
## .. WTHNRC12_01 = col_double(),
## .. WTHNRC12_02 = col_double(),
## .. WTHNRC12_03 = col_double(),
## .. WTHNRC12_04 = col_double(),
## .. WTHNRC12_05 = col_double(),
## .. WTHNRC12_06 = col_double(),
## .. WTHNRC12_07 = col_double()
## .. )
## - attr(*, "problems")=<externalptr>
## POSTALCODE12 WTHNRC12_01 WTHNRC12_02
## Length:116011 Min. :19.28 Min. :-46.74
## Class :character 1st Qu.:31.07 1st Qu.:-11.44
## Mode :character Median :31.28 Median : -8.40
## Mean :31.62 Mean :-11.67
## 3rd Qu.:31.76 3rd Qu.: -7.49
## Max. :39.07 Max. : -4.48
## WTHNRC12_03 WTHNRC12_04 WTHNRC12_05
## Min. :-2.605 Min. : 2.913 Min. :-8.122
## 1st Qu.: 9.693 1st Qu.:13.454 1st Qu.: 5.750
## Median :10.140 Median :13.887 Median : 6.612
## Mean : 9.573 Mean :13.521 Mean : 5.625
## 3rd Qu.:10.449 3rd Qu.:14.006 3rd Qu.: 6.893
## Max. :10.551 Max. :16.227 Max. : 7.132
## WTHNRC12_06 WTHNRC12_07
## Min. : 4.336 Min. : 104.6
## 1st Qu.: 6.936 1st Qu.:1072.1
## Median : 7.151 Median :1316.4
## Mean : 7.896 Mean :1110.7
## 3rd Qu.: 7.935 3rd Qu.:1399.0
## Max. :13.498 Max. :3076.5
The str() function displays the structure of the dataset, including variable types and dimensions. The summary() function provides summary statistics and quick descriptive information.
Another useful function is glimpse() from dplyr.
## Rows: 60,143
## Columns: 13
## $ ID <dbl> 1000001, 1000002, 1000003, 10000…
## $ Sex <chr> "M", "F", "M", "M", "M", "F", "F…
## $ Cause_of_death <chr> "D47", "C34", "C26", "C16", "C22…
## $ dbuid2016 <dbl> 13070175019, 48060924001, 352045…
## $ Location_of_death <dbl> 5, 3, 6, 1, 6, 5, 4, 1, 3, 2, 5,…
## $ Marital_status <dbl> 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 2,…
## $ Postalcode <chr> "E1A1E9", "T2E0T2", "M4J1L1", "H…
## $ B_year <dbl> 1943, 1943, 1953, 1923, 1931, 19…
## $ B_month <dbl> 7, 5, 8, 6, 8, 11, 5, 8, 7, 2, 1…
## $ B_day <dbl> 8, 22, 9, 14, 8, 4, 20, 18, 23, …
## $ D_year <dbl> 2016, 2016, 2016, 2016, 2016, 20…
## $ D_month <dbl> 9, 12, 4, 11, 4, 2, 2, 6, 5, 1, …
## $ D_day <dbl> 22, 14, 21, 21, 4, 6, 9, 13, 25,…
Compared with str(), glimpse() often provides a cleaner and easier-to-read overview of the data frame.
Initial data inspection is one of the most important parts of data cleaning because it helps identify:
- missing values;
- unusual variable names;
- incorrect data types;
- inconsistent formatting; and
- unexpected values.
4.6 Exploring and validating the data
A useful next step is performing simple exploratory checks to understand the datasets more fully.
For example, we can identify all health regions in the correspondence dataset.
corr %>%
distinct(hrname_english) %>%
mutate(
hrname_english = iconv(
hrname_english,
from = "latin1",
to = "UTF-8",
sub = ""
)
) %>%
arrange(hrname_english)## # A tibble: 96 × 1
## hrname_english
## <chr>
## 1 Athabasca Health Authority
## 2 Brant County Health Unit
## 3 Calgary Zone
## 4 Central Regional Health Authority
## 5 Central Vancouver Island Health Service Delivery Area
## 6 Central Zone
## 7 Chatham-Kent Health Unit
## 8 City of Hamilton Health Unit
## 9 City of Toronto Health Unit
## 10 Cypress Regional Health Authority
## # ℹ 86 more rows
When working with multilingual datasets, encoding issues may cause characters to display incorrectly. Re-importing the file using the appropriate encoding often resolves these problems.
corr <- read_csv(
"./Raw Data/Corr_2016.csv",
locale = readr::locale(
encoding = "latin1"
)
)
corr %>%
distinct(hrname_english) %>%
arrange(hrname_english)## # A tibble: 96 × 1
## hrname_english
## <chr>
## 1 Athabasca Health Authority
## 2 Brant County Health Unit
## 3 Calgary Zone
## 4 Central Regional Health Authority
## 5 Central Vancouver Island Health Service Delivery Area
## 6 Central Zone
## 7 Chatham-Kent Health Unit
## 8 City of Hamilton Health Unit
## 9 City of Toronto Health Unit
## 10 Cypress Regional Health Authority
## # ℹ 86 more rows
We can also calculate the number of health regions in the dataset.
There are 96 health regions in the correspondence dataset.
Additional exploratory summaries can help validate the imported data.
## # A tibble: 3 × 2
## Sex n
## <chr> <int>
## 1 F 28630
## 2 M 31500
## 3 <NA> 13
## # A tibble: 1 × 1
## Total_below_1
## <dbl>
## 1 179028
These simple checks help verify that the datasets were imported correctly and that the variables contain plausible values.
4.7 Cleaning variable names and reshaping data
One common issue in real-world datasets is poorly formatted variable names. Variables that begin with numbers or contain symbols can make coding more difficult.
For example:
## [1] "...1"
## [2] "Health Service Delivery Area"
## [3] "Year"
## [4] "Gender"
## [5] "<1"
## [6] "04-Jan"
## [7] "09-May"
## [8] "14-Oct"
## [9] "15-19"
## [10] "20-24"
## [11] "25-29"
## [12] "30-34"
## [13] "35-39"
## [14] "40-44"
## [15] "45-49"
## [16] "50-54"
## [17] "55-59"
## [18] "60-64"
## [19] "65-69"
## [20] "70-74"
## [21] "75-79"
## [22] "80-84"
## [23] "85-89"
## [24] "90+"
## [25] "Total"
## [1] "...1"
## [2] "Health Service Delivery Area"
## [3] "Year"
## [4] "Gender"
## [5] "<1"
## [6] "04-Jan"
## [7] "09-May"
## [8] "14-Oct"
## [9] "15-19"
## [10] "20-24"
## [11] "25-29"
## [12] "30-34"
## [13] "35-39"
## [14] "40-44"
## [15] "45-49"
## [16] "50-54"
## [17] "55-59"
## [18] "60-64"
## [19] "65-69"
## [20] "70-74"
## [21] "75-79"
## [22] "80-84"
## [23] "85-89"
## [24] "90+"
## [25] "Total"
Some age-group variables contain names such as "04-Jan" or "09-May", which are not ideal variable names for analysis.The janitor package is also useful for cleaning messy column names and inspecting datasets Firke (2026).
One approach is to rename problematic variables directly.
Another important data-cleaning task is reshaping data into a tidy format. In tidy data, each variable has its own column, each observation has its own row, and each value occupies a single cell.
The pivot_longer() function is commonly used to convert wide datasets into long format.
pop_long <- pop %>%
select(
-any_of(c("X1", "Total"))
) %>%
pivot_longer(
cols = -any_of(
c(
"Year",
"Gender",
"Health Service Delivery Area"
)
),
names_to = "Age",
values_to = "Value"
)
pop_long## # A tibble: 1,071 × 5
## `Health Service Delivery Area` Year Gender Age Value
## <chr> <dbl> <chr> <chr> <dbl>
## 1 British Columbia 2016 M ...1 0
## 2 British Columbia 2016 M <1 22997
## 3 British Columbia 2016 M 04-Jan 93211
## 4 British Columbia 2016 M 09-May 121341
## 5 British Columbia 2016 M 14-Oct 119586
## 6 British Columbia 2016 M 15-19 140451
## 7 British Columbia 2016 M 20-24 170968
## 8 British Columbia 2016 M 25-29 159609
## 9 British Columbia 2016 M 30-34 162416
## 10 British Columbia 2016 M 35-39 155936
## # ℹ 1,061 more rows
The resulting dataset stores age groups within a single variable rather than across many separate columns.
After reshaping the data, some values may still require cleaning or recoding. The case_when() function is often used for this purpose.
pop_long <- pop_long %>%
mutate(
Age = case_when(
Age == "04-Jan" ~ "01-04",
Age == "09-May" ~ "05-09",
Age == "14-Oct" ~ "10-14",
TRUE ~ Age
)
)The final line, TRUE ~ Age, is important because it preserves all values that do not match the earlier conditions.
If this line is removed, unmatched rows become missing values.
pop %>%
select(
-any_of(c("X1", "Total"))
) %>%
pivot_longer(
cols = -any_of(
c(
"Year",
"Gender",
"Health Service Delivery Area"
)
),
names_to = "Age",
values_to = "Value"
) %>%
mutate(
Age = case_when(
Age == "04-Jan" ~ "01-04",
Age == "09-May" ~ "05-09",
Age == "14-Oct" ~ "10-14"
)
)## # A tibble: 1,071 × 5
## `Health Service Delivery Area` Year Gender Age Value
## <chr> <dbl> <chr> <chr> <dbl>
## 1 British Columbia 2016 M <NA> 0
## 2 British Columbia 2016 M <NA> 22997
## 3 British Columbia 2016 M 01-04 93211
## 4 British Columbia 2016 M 05-09 121341
## 5 British Columbia 2016 M 10-14 119586
## 6 British Columbia 2016 M <NA> 140451
## 7 British Columbia 2016 M <NA> 170968
## 8 British Columbia 2016 M <NA> 159609
## 9 British Columbia 2016 M <NA> 162416
## 10 British Columbia 2016 M <NA> 155936
## # ℹ 1,061 more rows
Always verify the output after recoding variables. Small coding mistakes can unintentionally convert valid values into missing values.
4.8 Practical considerations for R Markdown workflows
When working with R Markdown documents, remember that every code chunk is executed again during knitting. Code that modifies files, unzips folders, or downloads data should therefore be used carefully.
As projects grow larger, keeping code organized and avoiding repeated operations becomes increasingly important for reproducible workflows.
You should also begin thinking about how analyses are documented. A good R Markdown document not only produces correct outputs, but also explains the reasoning behind each analytical step.
4.9 Chapter summary
In this chapter, you learned several important data cleaning and management techniques using the tidyverse. You imported multiple datasets, explored project files, inspected dataset structures, handled encoding issues, cleaned variable names, reshaped data into tidy formats, and recoded variables using case_when().
These skills form the foundation of reproducible data analysis workflows and will be used extensively throughout the remainder of the course.