1 Understanding and wrangling data

1.1 Introduction to the dataset.

We will be introducing birthwt dataset from the MASS package which was collected at Baystate Medical Center, Springfield, Mass during 1986 on Risk Factors Associated with Low Infant Birth Weight.

Important

The same dataset can be loaded directly from the MASS package. However, for additional learnings, we have provided the dataset in varied formats for the participants to learn data import during the group activities.

1.2 Create project

We would like to reiterate that creating a project and required folders at the beginning of a research work is a gold standard database management strategy. Go ahead and create your own projects and folders for the activity.

1.3 Load libraries

To use functions from varied packages/ libraries other than baseR, we need to explicitly load those libraries. For this session, following libraries will be used:-

Note

The loading of libraries into your current session can be optional. In case you are going to use a function only once from a library, it is computationally better to explicity call the function using ::. Discuss!

  1. rio: A Swiss-Army Knife for Data I/O (Click to learn more)
  2. tidyverse: R packages for Data Science (Click to learn more)
  3. skimr: For quick summary statistics (Click to learn more)
  4. here: A Simpler Way to Find Your Files (Click to learn more)
  5. MASS: Functions and datasets to support Venables and Ripley, “Modern Applied Statistics with S” (4th edition, 2002) (Click to learn more)
Code
library(tidyverse)
library(gtsummary)

1.4 Load data

Code
df <- MASS::birthwt

1.5 Know the class of your data

R possesses a simple generic function mechanism which can be used for an object-oriented style of programming (OOP). Method dispatch takes place based on the class of the first argument to the generic function.

Important

When dealing with rectagular spreadsheets, it is a good habit to set the class of the dataset to tibble for a tidy analysis.

Code
class(df)
[1] "data.frame"

1.6 Setting dataset class to tibble

Code
df <- df |> as_tibble()
class(df)
[1] "tbl_df"     "tbl"        "data.frame"

1.7 Loading data from a folder

import function from the rio package read a data.frame from a file. Exceptions to this rule are Rdata, RDS, and JSON input file formats, which return the originally saved object without changing its class. To set a class, we can use an argument setclass.

Code
df <- rio::import(here::here("data",
                             "lbw.dta"),
                  setclass = "tibble")
class(df)
[1] "tbl_df"     "tbl"        "data.frame"

1.8 Understand structure of the data.

1.8.1 Using the glimpse function.

glimpse() is like a transposed version of print(): columns run down the page, and data runs across. This makes it possible to see every column in a data frame. It’s a little like str() applied to a data frame but it tries to show you as much data as possible.

Code
glimpse(df)
Rows: 189
Columns: 10
$ low   <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ age   <dbl> 19, 33, 20, 21, 18, 21, 22, 17, 29, 26, 19, 19, 22, 30, 18, 18, …
$ lwt   <dbl> 182, 155, 105, 108, 107, 124, 118, 103, 123, 113, 95, 150, 95, 1…
$ race  <dbl> 2, 3, 1, 1, 1, 3, 1, 3, 1, 1, 3, 3, 3, 3, 1, 1, 2, 1, 3, 1, 3, 1…
$ smoke <dbl> 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0…
$ ptl   <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0…
$ ht    <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ ui    <dbl> 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1…
$ ftv   <dbl> 0, 3, 1, 2, 0, 0, 1, 1, 1, 0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 1, 2, 3…
$ bwt   <dbl> 2523, 2551, 2557, 2594, 2600, 2622, 2637, 2637, 2663, 2665, 2722…
Important

Understanding the data structure helps you understand the way data cleaning/wrangling and subsequent analysis is required. Here, the birthwt data frame has 189 rows and 10 columns. All the variables are dbl (means double precision, a quantitative variable that is essentially continuous - taking decimal values).

1.8.2 Using the summary function

summary() is a generic function used to produce result summaries of the results of various model fitting functions. The function invokes particular methods which depend on the class of the first argument.

Code
summary(df)
      low              age             lwt             race      
 Min.   :0.0000   Min.   :14.00   Min.   : 80.0   Min.   :1.000  
 1st Qu.:0.0000   1st Qu.:19.00   1st Qu.:110.0   1st Qu.:1.000  
 Median :0.0000   Median :23.00   Median :121.0   Median :1.000  
 Mean   :0.3122   Mean   :23.24   Mean   :129.8   Mean   :1.847  
 3rd Qu.:1.0000   3rd Qu.:26.00   3rd Qu.:140.0   3rd Qu.:3.000  
 Max.   :1.0000   Max.   :45.00   Max.   :250.0   Max.   :3.000  
     smoke             ptl               ht                ui        
 Min.   :0.0000   Min.   :0.0000   Min.   :0.00000   Min.   :0.0000  
 1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.00000   1st Qu.:0.0000  
 Median :0.0000   Median :0.0000   Median :0.00000   Median :0.0000  
 Mean   :0.3915   Mean   :0.1958   Mean   :0.06349   Mean   :0.1481  
 3rd Qu.:1.0000   3rd Qu.:0.0000   3rd Qu.:0.00000   3rd Qu.:0.0000  
 Max.   :1.0000   Max.   :3.0000   Max.   :1.00000   Max.   :1.0000  
      ftv              bwt      
 Min.   :0.0000   Min.   : 709  
 1st Qu.:0.0000   1st Qu.:2414  
 Median :0.0000   Median :2977  
 Mean   :0.7937   Mean   :2945  
 3rd Qu.:1.0000   3rd Qu.:3487  
 Max.   :6.0000   Max.   :4990  

1.8.3 Using the skim function.

skim() is an alternative to summary(), quickly providing a broad overview of a data frame. It handles data of all types, dispatching a different set of summary functions based on the types of columns in the data frame.

Code
skimr::skim(df)
Data summary
Name df
Number of rows 189
Number of columns 10
_______________________
Column type frequency:
numeric 10
________________________
Group variables None

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
low 0 1 0.31 0.46 0 0 0 1 1 ▇▁▁▁▃
age 0 1 23.24 5.30 14 19 23 26 45 ▇▇▃▁▁
lwt 0 1 129.81 30.58 80 110 121 140 250 ▆▇▂▁▁
race 0 1 1.85 0.92 1 1 1 3 3 ▇▁▂▁▆
smoke 0 1 0.39 0.49 0 0 0 1 1 ▇▁▁▁▅
ptl 0 1 0.20 0.49 0 0 0 0 3 ▇▁▁▁▁
ht 0 1 0.06 0.24 0 0 0 0 1 ▇▁▁▁▁
ui 0 1 0.15 0.36 0 0 0 0 1 ▇▁▁▁▂
ftv 0 1 0.79 1.06 0 0 0 1 6 ▇▂▁▁▁
bwt 0 1 2944.59 729.21 709 2414 2977 3487 4990 ▁▅▇▆▁

1.9 Data wrangling.

dplyr is a package grouped inside tidyverse collection of packages. dplyr package is very useful to “munge”, “wrangle”, or “transform” your data. It is a grammar of data manipulation. It provides a consistent set of verbs that help you solve the most common data manipulation challenges.

Tip

The common data wrangling functions include:

  1. rename(): To change the names of the variables.
  2. select(): To reduce the size of dataset by selecting certain variables (or columns).
  3. mutate(): To generate new variable/ transform existing variables.
  4. arrange(): To sort observation of a variable.
  5. filter(): To group observations based on certain criteria.
  6. group_by(): To reduce variables to groups in order to estimate summary statistic.

1.9.1 Renaming variable names

rename() provides several advantages over traditional renaming methods, especially in the context of data manipulation.

  • The syntax for rename() is concise and intuitive. You specify the new column name first, making it easier to read and understand.
  • rename() integrates well with the pipe operator (|>), which allows chaining multiple data manipulation steps.
  • rename() allows you to rename only the columns you need without affecting others. This is convenient when working with large datasets.
  • If you try to rename a column that doesn’t exist, rename() throws an informative error, helping catch typos early. This behavior is safer compared to manually changing column names, where typos might go unnoticed.
Code
df <- df |> 
  rename(child_weight_cat = low,
         maternal_age = age,
         maternal_weight = lwt,
         number_preterm = ptl,
         hypertension = ht,
         uterine_irritability = ui,
         health_visits = ftv,
         birth_weight = bwt)
names(df)
 [1] "child_weight_cat"     "maternal_age"         "maternal_weight"     
 [4] "race"                 "smoke"                "number_preterm"      
 [7] "hypertension"         "uterine_irritability" "health_visits"       
[10] "birth_weight"        

1.9.2 Select only maternal weight, smoking, and birth weight

Code
df |> 
  select(maternal_weight,
         smoke,
         birth_weight)
# A tibble: 189 × 3
   maternal_weight smoke birth_weight
             <dbl> <dbl>        <dbl>
 1             182     0         2523
 2             155     0         2551
 3             105     1         2557
 4             108     1         2594
 5             107     1         2600
 6             124     0         2622
 7             118     0         2637
 8             103     0         2637
 9             123     1         2663
10             113     1         2665
# ℹ 179 more rows

1.9.3 Mutate smoking history, race, and child_weight_cat as categorical/factor variable

The function factor is used to encode a vector as a factor. The same can be used within the mutate function as an argument for data transformation.

Try using if else!

df |> 
  mutate(
    smoke_cat = if_else(
      smoke == 1, "Smoker", "Non-Smoker"))
Code
df <- df |> 
  mutate(smoke_cat = factor(smoke, 
                            levels = c(0,1),
                            labels = c("Non-smoker",
                                       "Smoker")),
         race_cat = factor(race,
                           levels = c(1,2,3),
                           labels = c("White",
                                      "Black",
                                      "Other")),
         child_weight_cat = factor(child_weight_cat,
                                   levels = c(0,1),
                                   labels = c("Normal", "Low Birth Weight")))
df |> 
  select(smoke_cat, race_cat, child_weight_cat) |> 
  summary()
      smoke_cat    race_cat          child_weight_cat
 Non-smoker:115   White:96   Normal          :130    
 Smoker    : 74   Black:26   Low Birth Weight: 59    
                  Other:67                           

1.9.4 Arrange maternal age to show lowest age group

Code
df |> 
  arrange(maternal_age) |> 
  head()
# A tibble: 6 × 12
  child_weight_cat maternal_age maternal_weight  race smoke number_preterm
  <fct>                   <dbl>           <dbl> <dbl> <dbl>          <dbl>
1 Normal                     14             135     1     0              0
2 Low Birth Weight           14             101     3     1              1
3 Low Birth Weight           14             100     3     0              0
4 Normal                     15              98     2     0              0
5 Low Birth Weight           15             110     1     0              0
6 Low Birth Weight           15             115     3     0              0
# ℹ 6 more variables: hypertension <dbl>, uterine_irritability <dbl>,
#   health_visits <dbl>, birth_weight <dbl>, smoke_cat <fct>, race_cat <fct>

1.9.5 Arrange maternal age to show lowest age group

Code
df |> 
  arrange(-maternal_age) |> 
  head()
# A tibble: 6 × 12
  child_weight_cat maternal_age maternal_weight  race smoke number_preterm
  <fct>                   <dbl>           <dbl> <dbl> <dbl>          <dbl>
1 Normal                     45             123     1     0              0
2 Normal                     36             202     1     0              0
3 Normal                     36             175     1     0              0
4 Normal                     35             121     2     1              1
5 Normal                     35             170     1     0              1
6 Low Birth Weight           34             187     2     1              0
# ℹ 6 more variables: hypertension <dbl>, uterine_irritability <dbl>,
#   health_visits <dbl>, birth_weight <dbl>, smoke_cat <fct>, race_cat <fct>

1.9.6 Filter dataset for only mothers with Black race

Code
df |> 
  filter(race_cat == "Black")
# A tibble: 26 × 12
   child_weight_cat maternal_age maternal_weight  race smoke number_preterm
   <fct>                   <dbl>           <dbl> <dbl> <dbl>          <dbl>
 1 Normal                     19             182     2     0              0
 2 Normal                     15              98     2     0              0
 3 Normal                     26             168     2     1              0
 4 Normal                     17             113     2     0              0
 5 Normal                     17             113     2     0              0
 6 Normal                     35             121     2     1              1
 7 Normal                     25             125     2     0              0
 8 Normal                     21             185     2     1              0
 9 Normal                     23             130     2     0              0
10 Normal                     22             158     2     0              1
# ℹ 16 more rows
# ℹ 6 more variables: hypertension <dbl>, uterine_irritability <dbl>,
#   health_visits <dbl>, birth_weight <dbl>, smoke_cat <fct>, race_cat <fct>

1.9.7 Group dataset by category of maternal race.

Most data operations are done on groups defined by variables. group_by() takes an existing tbl and converts it into a grouped tbl where operations are performed “by group”.

Code
df |> 
  group_by(race_cat)
# A tibble: 189 × 12
# Groups:   race_cat [3]
   child_weight_cat maternal_age maternal_weight  race smoke number_preterm
   <fct>                   <dbl>           <dbl> <dbl> <dbl>          <dbl>
 1 Normal                     19             182     2     0              0
 2 Normal                     33             155     3     0              0
 3 Normal                     20             105     1     1              0
 4 Normal                     21             108     1     1              0
 5 Normal                     18             107     1     1              0
 6 Normal                     21             124     3     0              0
 7 Normal                     22             118     1     0              0
 8 Normal                     17             103     3     0              0
 9 Normal                     29             123     1     1              0
10 Normal                     26             113     1     1              0
# ℹ 179 more rows
# ℹ 6 more variables: hypertension <dbl>, uterine_irritability <dbl>,
#   health_visits <dbl>, birth_weight <dbl>, smoke_cat <fct>, race_cat <fct>

1.10 Summary.

In this session, we worked on understanding the data structure, variable names, class, and used dplyr verbs for data wrangling. Once the dataset is ready for analysis, we move towards Exploratory Data Analysis. It is an iterative process for a robust analysis later.