Purpose

This worksheet introduces exploratory data analysis (EDA) in RStudio. First, you will follow a complete example using one synthetic brigade. Then you will choose your own brigade and variables and repeat the analysis.

The data are synthetic. The records describe fictional Soldiers and are for classroom practice only.

1. Set up RStudio

Install the required packages once

This worksheet uses four packages:

  • rmarkdown creates the finished HTML report.
  • readr imports the CSV file.
  • dplyr filters and summarizes data.
  • ggplot2 creates graphs.

Run the next chunk only if the packages are not already installed. Installation normally needs to happen only once on a computer.

required_packages <- c("rmarkdown", "readr", "dplyr", "ggplot2")

missing_packages <- required_packages[
  !required_packages %in% rownames(installed.packages())
]

if (length(missing_packages) > 0) {
  install.packages(missing_packages)
} else {
  message("All required packages are already installed.")
}

Load the packages for this session

Run this chunk each time you open RStudio. The rmarkdown package does not need a library() line because RStudio uses it when you click Knit.

library(readr)
library(dplyr)
library(ggplot2)

Run code in R Markdown

Use the green Run button in the upper-right corner of a code chunk to run that chunk. You can also place your cursor inside a chunk and press Ctrl+Shift+Enter.

R ignores any text after a # in a code chunk. These comments explain what the code does.

# R can perform calculations.
4 * 5
## [1] 20
# <- stores a value with a name.
example_scores <- c(28, 31, 34, 35, 37)

# A function performs an action.
mean(example_scores)
## [1] 33
max(example_scores)
## [1] 37

2. Import the synthetic CSV

Keep army_brigades_synthetic.csv in the same folder as this R Markdown file.

csv_name <- "army_brigades_synthetic.csv"
data_file <- csv_name

# If RStudio is working from another folder, look beside the open Rmd.
if (!file.exists(data_file) &&
    requireNamespace("rstudioapi", quietly = TRUE) &&
    rstudioapi::isAvailable()) {
  
  rmd_path <- rstudioapi::getSourceEditorContext()$path
  
  if (nzchar(rmd_path)) {
    data_file <- file.path(dirname(rmd_path), csv_name)
  }
}

if (!file.exists(data_file)) {
  stop(
    "Could not find army_brigades_synthetic.csv. ",
    "Place the CSV in the same folder as this R Markdown file."
  )
}

army_data <- readr::read_csv(data_file, show_col_types = FALSE)

If this chunk produces a “file not found” error, move the CSV into the same folder as the .Rmd file and run the chunk again.

3. Examine the dataset

A row is one observation. A column is one variable.

# Display the first six rows.
head(army_data)
## # A tibble: 6 × 46
##   uic_hierarchy     CorpsName DivisionName BrigadeName brigade battalion company
##   <chr>             <chr>     <chr>        <chr>       <chr>   <chr>     <chr>  
## 1 W9LG6N;W55434;W1… III ARMO… 1ST INFANTR… 6TH BCT 1S… 6BCT    2-63 IN   F CO 2…
## 2 W9LG6N;W55434;WP… III ARMO… 1ST INFANTR… 3RD BCT 1S… 3BCT    4-8 FA    E CO 4…
## 3 W9LG6N;W55434;W1… III ARMO… 1ST INFANTR… 2ND BCT 1S… 2BCT    5-82 CAV  C CO 5…
## 4 W9LG6N;W55434;W1… III ARMO… 1ST INFANTR… 6TH BCT 1S… 6BCT    2-63 IN   A CO 2…
## 5 W9LG6N;W55434;WR… III ARMO… 1ST INFANTR… 4TH BCT 1S… 4BCT    4-26 FA   B CO 4…
## 6 W9LG6N;W55434;W1… III ARMO… 1ST INFANTR… 2ND BCT 1S… 2BCT    5-82 CAV  F CO 5…
## # ℹ 39 more variables: sex <chr>, age <dbl>, age_bracket <chr>,
## #   time_in_active_service_years <dbl>, skill_level <chr>,
## #   primary_specialty_code <chr>, asi_and_sqi <chr>, languages_cleaned <chr>,
## #   height <dbl>, weight <dbl>, body_comp_pass <lgl>, body_fat_percent <dbl>,
## #   aft_date <date>, aft_pass <lgl>, aft_result_summary <chr>,
## #   aft_score_total <dbl>, aft_score_plank <dbl>, aft_score_push_up <dbl>,
## #   aft_score_sprint_drag_carry <dbl>, aft_score_2mi_run <dbl>, …
# Find the number of rows and columns.
dim(army_data)
## [1] 34472    46
# List all column names.
names(army_data)
##  [1] "uic_hierarchy"                     "CorpsName"                        
##  [3] "DivisionName"                      "BrigadeName"                      
##  [5] "brigade"                           "battalion"                        
##  [7] "company"                           "sex"                              
##  [9] "age"                               "age_bracket"                      
## [11] "time_in_active_service_years"      "skill_level"                      
## [13] "primary_specialty_code"            "asi_and_sqi"                      
## [15] "languages_cleaned"                 "height"                           
## [17] "weight"                            "body_comp_pass"                   
## [19] "body_fat_percent"                  "aft_date"                         
## [21] "aft_pass"                          "aft_result_summary"               
## [23] "aft_score_total"                   "aft_score_plank"                  
## [25] "aft_score_push_up"                 "aft_score_sprint_drag_carry"      
## [27] "aft_score_2mi_run"                 "aft_score_alt_aerobic"            
## [29] "aft_raw_plank"                     "aft_raw_plank_seconds"            
## [31] "aft_raw_deadlift"                  "aft_deadlift_score"               
## [33] "aft_raw_push_up"                   "aft_raw_sprint_drag_carry"        
## [35] "aft_raw_sprint_drag_carry_seconds" "aft_raw_2mi_run"                  
## [37] "aft_raw_2mi_run_seconds"           "aft_raw_alt_aerobic"              
## [39] "m4"                                "pistol"                           
## [41] "m249"                              "m320"                             
## [43] "shotgun"                           "anti_tank"                        
## [45] "sniper"                            "smg"

The dataset contains both quantitative and categorical variables.

  • A quantitative variable records a numerical amount, such as m4, height, or aft_score_total.
  • A categorical variable places observations into groups, such as brigade, sex, or age_bracket.

Use the next two commands when deciding which variables to study later.

# Quantitative variables
names(army_data)[sapply(army_data, is.numeric)]
##  [1] "age"                               "time_in_active_service_years"     
##  [3] "height"                            "weight"                           
##  [5] "body_fat_percent"                  "aft_score_total"                  
##  [7] "aft_score_plank"                   "aft_score_push_up"                
##  [9] "aft_score_sprint_drag_carry"       "aft_score_2mi_run"                
## [11] "aft_score_alt_aerobic"             "aft_raw_plank_seconds"            
## [13] "aft_raw_deadlift"                  "aft_deadlift_score"               
## [15] "aft_raw_push_up"                   "aft_raw_sprint_drag_carry_seconds"
## [17] "aft_raw_2mi_run_seconds"           "m4"                               
## [19] "pistol"                            "m320"                             
## [21] "shotgun"                           "anti_tank"                        
## [23] "sniper"
# Categorical variables
names(army_data)[!sapply(army_data, is.numeric)]
##  [1] "uic_hierarchy"             "CorpsName"                
##  [3] "DivisionName"              "BrigadeName"              
##  [5] "brigade"                   "battalion"                
##  [7] "company"                   "sex"                      
##  [9] "age_bracket"               "skill_level"              
## [11] "primary_specialty_code"    "asi_and_sqi"              
## [13] "languages_cleaned"         "body_comp_pass"           
## [15] "aft_date"                  "aft_pass"                 
## [17] "aft_result_summary"        "aft_raw_plank"            
## [19] "aft_raw_sprint_drag_carry" "aft_raw_2mi_run"          
## [21] "aft_raw_alt_aerobic"       "m249"                     
## [23] "smg"

4. Follow a complete example

The example uses the synthetic 1BCT records. The primary variable is m4, and the grouping variable is age_bracket.

Select one brigade

The filter() function keeps rows that meet a condition. The == symbol means “is equal to.”

example_data <- army_data %>%
  filter(brigade == "1BCT")

# Confirm that the filter returned observations.
nrow(example_data)
## [1] 4301

The %>% symbol is called a pipe. Read it as “and then.” The code starts with army_data and then filters it to 1BCT.

Check for missing values

R displays a missing value as NA. Missing values are not zeroes.

# Count missing M4 scores.
sum(is.na(example_data$m4))
## [1] 267
# Count available M4 scores.
sum(!is.na(example_data$m4))
## [1] 4034

The $ symbol selects one column from a dataset. Therefore, example_data$m4 means “the m4 column in example_data.”

Calculate measures of center and spread

The mean and median describe the center of a distribution. Standard deviation and IQR describe its spread. The option na.rm = TRUE tells R to ignore missing values for that calculation.

example_data %>%
  summarize(
    observations = sum(!is.na(m4)),
    mean = mean(m4, na.rm = TRUE),
    median = median(m4, na.rm = TRUE),
    standard_deviation = sd(m4, na.rm = TRUE),
    iqr = IQR(m4, na.rm = TRUE),
    minimum = min(m4, na.rm = TRUE),
    maximum = max(m4, na.rm = TRUE)
  )
## # A tibble: 1 × 7
##   observations  mean median standard_deviation   iqr minimum maximum
##          <int> <dbl>  <dbl>              <dbl> <dbl>   <dbl>   <dbl>
## 1         4034  32.1     33               5.47    10      23      40

The mean is sensitive to unusually high or low observations. The median is more resistant to those values.

Think about it: Are the mean and median similar? What does that suggest about the center of the M4 score distribution?

Create a histogram

A histogram shows the shape of one quantitative variable. Each bar counts observations in an interval.

example_data %>%
  filter(!is.na(m4)) %>%
  ggplot(aes(x = m4)) +
  geom_histogram(
    bins = 10,
    fill = "steelblue",
    color = "white"
  ) +
  labs(
    title = "Distribution of M4 Scores in 1BCT",
    x = "M4 Score",
    y = "Number of Soldiers"
  ) +
  theme_minimal()

When describing a histogram, discuss its shape, center, spread, and unusual features.

Create a boxplot

A boxplot displays the median, the middle 50% of the observations, and possible outliers.

example_data %>%
  filter(!is.na(m4)) %>%
  ggplot(aes(x = m4)) +
  geom_boxplot(
    fill = "darkseagreen3",
    color = "gray25"
  ) +
  labs(
    title = "Boxplot of M4 Scores in 1BCT",
    x = "M4 Score",
    y = NULL
  ) +
  theme_minimal()

Points beyond the whiskers are possible outliers. A possible outlier deserves investigation; it is not automatically an error.

Compare groups

The group_by() function separates the observations into categories before calculating statistics.

example_data %>%
  filter(!is.na(m4), !is.na(age_bracket)) %>%
  group_by(age_bracket) %>%
  summarize(
    observations = n(),
    mean = mean(m4),
    median = median(m4),
    standard_deviation = sd(m4),
    .groups = "drop"
  )
## # A tibble: 7 × 5
##   age_bracket observations  mean median standard_deviation
##   <chr>              <int> <dbl>  <dbl>              <dbl>
## 1 16-20                186  27.5     26               4.52
## 2 20-25               1370  30.0     30               5.21
## 3 25-30               1220  32.4     33               5.23
## 4 30-35                682  33.9     35               5.01
## 5 35-40                337  34.9     36               4.58
## 6 40-45                198  35.9     37               4.16
## 7 45+                   41  37.1     38               3.38

Side-by-side boxplots make visual comparisons easier.

example_data %>%
  filter(!is.na(m4), !is.na(age_bracket)) %>%
  ggplot(aes(x = age_bracket, y = m4)) +
  geom_boxplot(fill = "tan2") +
  labs(
    title = "M4 Scores by Age Bracket in 1BCT",
    x = "Age Bracket",
    y = "M4 Score"
  ) +
  theme_minimal()

Think about it: Which group has the highest median? Which group has the greatest spread? Are the group sizes similar?

Examine the relationship between two variables

A scatterplot shows the relationship between two quantitative variables. Look for direction, strength, form, and unusual observations.

example_data %>%
  filter(!is.na(height), !is.na(weight), height < 100) %>%
  ggplot(aes(x = height, y = weight)) +
  geom_point(alpha = 0.5, color = "steelblue4") +
  labs(
    title = "Height and Weight in 1BCT",
    subtitle = "The plot excludes heights of 100 inches or more",
    x = "Height (inches)",
    y = "Weight (pounds)"
  ) +
  theme_minimal()

The height rule is shown in the code and subtitle. Never remove suspicious observations silently.

5. Check data quality

EDA includes looking for missing, impossible, or inconsistent values. The following checks flag known issues in the synthetic CSV without changing the data.

# Heights recorded as 100 inches or more
army_data %>%
  filter(height >= 100) %>%
  select(brigade, battalion, height, weight)
## # A tibble: 12 × 4
##    brigade battalion height weight
##    <chr>   <chr>      <dbl>  <dbl>
##  1 6BCT    4-32 FA      169    197
##  2 5BCT    2-26 AR      168    185
##  3 1BCT    5-16 CAV     183    227
##  4 5BCT    1-41 AR      182    193
##  5 1BCT    4-5 FA       182    187
##  6 DSB     3-32 BSB     185    200
##  7 1BCT    5-16 CAV     175    141
##  8 1BCT    3-8 IN       187    225
##  9 1BCT    5-16 CAV     161    175
## 10 1BCT    5-16 CAV     182    171
## 11 6BCT    1-16 IN      178    212
## 12 4BCT    2-41 IN      165    220
# Body-fat percentages below zero
army_data %>%
  filter(body_fat_percent < 0) %>%
  select(brigade, battalion, body_fat_percent)
## # A tibble: 42 × 3
##    brigade battalion body_fat_percent
##    <chr>   <chr>                <dbl>
##  1 4BCT    2-41 IN                 -1
##  2 1MEB    1-16 EN                 -2
##  3 2BCT    4-77 FA                 -5
##  4 1BCT    5-16 CAV                -3
##  5 CAB     2-16 AV                 -1
##  6 5BCT    2-26 AR                 -5
##  7 6BCT    HHB 6BCT                -4
##  8 CAB     HHB CAB                 -4
##  9 1BCT    5-16 CAV                -4
## 10 3BCT    5-26 CAV                -2
## # ℹ 32 more rows

An unusual value is not automatically wrong. State what you found and explain any decision to keep or exclude it from a particular calculation or graph.

6. Your turn: choose a brigade and variables

You will now repeat the EDA process using your own choices. Complete each step in order.

Step 1: Choose a brigade

Run this code to see the available brigade names and their sample sizes.

army_data %>%
  count(brigade) %>%
  arrange(brigade)
## # A tibble: 10 × 2
##    brigade     n
##    <chr>   <int>
##  1 1BCT     4301
##  2 1MEB     1599
##  3 2BCT     4100
##  4 3BCT     4399
##  5 4BCT     4251
##  6 5BCT     4052
##  7 6BCT     4201
##  8 CAB      2700
##  9 DIVARTY  2968
## 10 DSB      1901

Step 2: Choose your variables

You need:

  1. One brigade. CAB
  2. One primary quantitative variable. time in active service years
  3. One second quantitative variable. aft score 2 mile run
  4. One categorical grouping variable. company

Change the four selections in the next chunk. The first choice is inside quotation marks. The three column names are not inside quotation marks.

# CHANGE this brigade.
my_brigade <- "CAB"

# CHANGE m4, aft_score_total, and age_bracket to your variables.
# The new names on the left stay the same.
my_data <- army_data %>%
  filter(brigade == my_brigade) %>%
  transmute(
    my_variable = time_in_active_service_years,
    my_second_variable = aft_score_2mi_run,
    my_group = company
  )

The transmute() function gives your chosen columns simple names. The rest of the worksheet uses my_variable, my_second_variable, and my_group, so you only need to choose the original columns once.

Step 3: Check your choices

Run this chunk before continuing.

# This number must be greater than zero.
nrow(my_data)
## [1] 2700
# The first two variables should say num or int.
# The grouping variable should say chr, fct, or logi.
str(my_data)
## tibble [2,700 × 3] (S3: tbl_df/tbl/data.frame)
##  $ my_variable       : num [1:2700] 1.45 4.54 1.04 9.74 6.89 ...
##  $ my_second_variable: num [1:2700] NA 70 60 71 NA 0 97 0 NA 87 ...
##  $ my_group          : chr [1:2700] "F CO 3-41 AV" "C CO 1-26 AV" "F CO 3-41 AV" "F CO 3-41 AV" ...
if (nrow(my_data) == 0) {
  stop("Your brigade name did not match the data. Check its spelling.")
}

if (!is.numeric(my_data$my_variable) ||
    !is.numeric(my_data$my_second_variable)) {
  stop("Choose quantitative variables for my_variable and my_second_variable.")
}

if (is.numeric(my_data$my_group)) {
  stop("Choose a categorical variable for my_group.")
}

Record your choices in your own words:

  • Brigade: CAB
  • Primary quantitative variable: Time in active service years
  • Second quantitative variable: AFT score 2 mi run
  • Categorical grouping variable: Company
  • Why did you choose these variables? I wanted to evaluate if soldiers with more service years had a better AFT 2 mile run score compare to soldiers who are less experienced in military and physical training. For the categorical grouping, I wanted to see which company has the majority of good runners (better AFT 2 mile run scores).

Step 4: Examine missing values

my_data %>%
  summarize(
    primary_missing = sum(is.na(my_variable)),
    second_missing = sum(is.na(my_second_variable)),
    group_missing = sum(is.na(my_group))
  )
## # A tibble: 1 × 3
##   primary_missing second_missing group_missing
##             <int>          <int>         <int>
## 1               7            486             0

Write 1–2 sentences: Which selected variable has the most missing values? How might missing observations affect your analysis?

My second variable, AFT 2 mile run score, has the most missing values. It is due to the fact that not every soldier conducted the 2 mile run, and instead they did the alternate aerobic test. This will affect my analysis since I am evaluating which company and soldiers with certain years of active service have good AFT 2 mile run time and I still have to work with 486 missing values.

Step 5: Calculate descriptive statistics

my_data %>%
  summarize(
    observations = sum(!is.na(my_variable)),
    mean = mean(my_variable, na.rm = TRUE),
    median = median(my_variable, na.rm = TRUE),
    standard_deviation = sd(my_variable, na.rm = TRUE),
    iqr = IQR(my_variable, na.rm = TRUE),
    minimum = min(my_variable, na.rm = TRUE),
    maximum = max(my_variable, na.rm = TRUE)
  )
## # A tibble: 1 × 7
##   observations  mean median standard_deviation   iqr minimum maximum
##          <int> <dbl>  <dbl>              <dbl> <dbl>   <dbl>   <dbl>
## 1         2693  4.96   3.43               4.28  5.08   -2.83    20.7

Write 2–3 sentences: What is a typical value? How much variability is present? Are the mean and median similar?

The typical value for for the time in active service years is 4.96 years and the variability is 4.27 years more or less than 4.96 years. The mean and median are not similar and it could be due to the distribution being skewed or due to outliers. The outliers will greatly affect the the mean and not the median.

Step 6: Create a histogram

my_data %>%
  filter(!is.na(my_variable)) %>%
  ggplot(aes(x = my_variable)) +
  geom_histogram(
    bins = 12,
    fill = "steelblue",
    color = "white"
  ) +
  labs(
    title = paste("Distribution of My Variable in", my_brigade),
    x = "My Primary Variable",
    y = "Number of Soldiers"
  ) +
  theme_minimal()

Write 2–3 sentences: Describe the distribution’s shape, center, spread, and unusual features.

The distribution is normal but it is right skewed. The mean is greater than the median, which is why the primary variable is being pulled to the right. Most of the values are between 1-5 years, with typical value between 3-4 years. The spread is large and a few negative values and outliers are the unusual features.

Step 7: Create a boxplot

my_data %>%
  filter(!is.na(my_variable)) %>%
  ggplot(aes(x = my_variable)) +
  geom_boxplot(fill = "darkseagreen3") +
  labs(
    title = paste("Boxplot of My Variable in", my_brigade),
    x = "My Primary Variable",
    y = NULL
  ) +
  theme_minimal()

Write 1–2 sentences: Does the boxplot show possible outliers? How can you tell?

Yes, there are outliers and they appear as individual dots beyond the right whisker of the box plot. They indicate values more than 1.5 x IQR (over 3rd Quartile)

Step 8: Compare groups

my_data %>%
  filter(!is.na(my_variable), !is.na(my_group)) %>%
  group_by(my_group) %>%
  summarize(
    observations = n(),
    mean = mean(my_variable),
    median = median(my_variable),
    standard_deviation = sd(my_variable),
    .groups = "drop"
  )
## # A tibble: 24 × 5
##    my_group       observations  mean median standard_deviation
##    <chr>                 <int> <dbl>  <dbl>              <dbl>
##  1 A CO 1-26 AV             80  4.70   3.29               3.97
##  2 A CO 2-16 AV            119  5.01   3.01               4.49
##  3 A CO 3-41 AV            176  4.82   3.55               4.11
##  4 A CO 4-77 GSAB          215  4.86   3.44               4.34
##  5 B CO 1-26 AV            126  4.58   2.92               4.08
##  6 B CO 2-16 AV            124  5.53   4.70               4.52
##  7 B CO 3-41 AV             84  4.83   3.17               4.34
##  8 B CO 4-77 GSAB          184  4.98   3.28               4.14
##  9 C CO 1-26 AV            149  5.26   3.51               4.55
## 10 C CO 2-16 AV            104  4.41   3.13               3.77
## # ℹ 14 more rows
my_data %>%
  filter(!is.na(my_variable), !is.na(my_group)) %>%
  ggplot(aes(x = my_group, y = my_variable)) +
  geom_boxplot(fill = "tan2") +
  labs(
    title = paste("My Variable by Group in", my_brigade),
    x = "My Grouping Variable",
    y = "My Primary Variable"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 35, hjust = 1))

Write 2–3 sentences: Which group has the highest center? Which has the greatest spread? Are any groups much smaller than the others?

D CO 1-26 AV has the highest center (median). E CO 3-41 AV has the greatest spread (standard deviation). C CO 4-77 GSAB is the smallest group compared to others.

Step 9: Examine two quantitative variables

my_data %>%
  filter(!is.na(my_variable), !is.na(my_second_variable)) %>%
  ggplot(aes(x = my_second_variable, y = my_variable)) +
  geom_point(alpha = 0.5, color = "steelblue4") +
  labs(
    title = paste("Relationship between My Variables in", my_brigade),
    x = "My Second Quantitative Variable",
    y = "My Primary Quantitative Variable"
  ) +
  theme_minimal()

Write 2–3 sentences: Is the relationship positive, negative, or not apparent? Does it look strong or weak? Are any observations unusual?

There is no apparent strong positive/negative relationship, the points are widely scattered. Unusual observations include the cluster at the second variable (AFT 2 mile score) at 0 since many soldiers did not test the 2 mile run. There are also some negative primary variable (years of active service) which is unusual.

Step 10: Write a conclusion

Write one paragraph that answers all of the following:

  1. What did you learn about your primary variable?
  2. What did you learn by comparing groups?
  3. What relationship did you observe between the two quantitative variables?
  4. What missing-value or data-quality concern did you notice?
  5. What question would you investigate next?

I learned that the average years of active service (my primary variable) was around 5 years. By comparing models, I was able to evaluate if there is or is not any correlation between my groups that I wanted to compare. Since I had large amount of data, the points were mainly scattered when comparing the two quantitative variables. However, I can briefly see that the data points with less service years had a greater amount of better AFT 2 mile run. This could mean that the younger population (less active service years) can run faster and get a better score rather that population that has served for many years. I had a lot of missing values for my second variable (AFT 2 mile run score) and it is a concern since it causes me to evaluate less data. For my next investigation, I would question if “soldiers who have good marksmanship skills also have a high AFT score or not?” Through this evaluation, I can check if soldiers need more focus on physical training or more time shooting at the range.

Connect your EDA to the course project

This worksheet is not the full EDA required for your course project. It is an introduction to the basic R tools and exploratory techniques you will need. It is intended to get your foot in the door so that you can begin asking useful questions about your brigade’s data.

After completing this worksheet, you must conduct additional EDA that is specific to your own research question. Use what you discover to develop an original, operationally relevant question for a specific decision maker. Your project EDA should investigate the response and explanatory variables that matter to that question, justify your proxies, examine relevant data-quality concerns, and develop the graphical and numerical evidence needed to motivate the rest of your analysis.

Read AY27-1 MA206 Project Instructions.pdf for the complete project requirements. That document, not this introductory worksheet, is the authoritative source for what your full EDA and final project must contain.

The decision maker for this brigade is Brigade Commander Col Adam R. Bock who leads and makes decisions alongside Command Sergeant Major Njikoka Anderson.

The research question for my EDA: “Do M4 qualification scores differ across time in active service years among soldiers in the same MOS?” Variables from annex B used to research this question are primary specialty code, m4, and time in active service years. There will a primary quantitative variable (time in active service years), a secondary quantitative variable (m4 scores), and a categorical grouping variable (primary specialty code/MOS).

This research will give insight into how much soldiers are prepared for future operations based on lethality. The individual’s marksmanship performance based on their service years can show if more experience is related to better m4 scores. This research will also highlight which MOS has more tendency to have better m4 scores. With answers to this research question, the commander can make decisions for future trainings if needed and it will give them an insight about every MOS preparation.

The construct is to measure m4 marksmanship proficiency, which contributes to soldier lethality. It will be inferred by evaluating the time in active service years for each MOS in the brigade. All three proxies are imperfect due to missing values and conditions under which scores were recorded. For m4 scores, the qualification represents performance under testing conditions rather than combat so even though the scores give an insight into soldier’s marksmanship, it cannot directly guarantee that the performance score will reflect combat effectiveness. There are many other variables and data proxies that can also be used to reflect lethality. For time in active service years, there will be cases where more years reflect high scores, but there will be other cases where more years correlate to low scores. More experience would not necessarily mean better scores and preparedness.

For data cleaning, there will be some missing/negative values for time in active service years and m4 scores. This can lead to This will affect my analysis since I am evaluating which MOS have better m4 scores and if it is correlated to how many years they have served for. Instead of working with all of 2700 records of data for CAB, I would have disregard the data with the missing/negative variables which is 182 data records.

# CHANGE this brigade.
my_brigade <- "CAB"

# CHANGE m4, aft_score_total, and age_bracket to your variables.
# The new names on the left stay the same.
my_data <- army_data %>%
  filter(brigade == my_brigade) %>%
  transmute(
    my_variable = time_in_active_service_years,
    my_second_variable = m4,
    my_group = primary_specialty_code
  )
my_data %>%
  summarize(
    primary_missing = sum(is.na(my_variable)),
    second_missing = sum(is.na(my_second_variable)),
    group_missing = sum(is.na(my_group))
  )
## # A tibble: 1 × 3
##   primary_missing second_missing group_missing
##             <int>          <int>         <int>
## 1               7            175             0
my_data %>%
  summarize(
    observations = sum(!is.na(my_variable)),
    mean = mean(my_variable, na.rm = TRUE),
    median = median(my_variable, na.rm = TRUE),
    standard_deviation = sd(my_variable, na.rm = TRUE),
    iqr = IQR(my_variable, na.rm = TRUE),
    minimum = min(my_variable, na.rm = TRUE),
    maximum = max(my_variable, na.rm = TRUE)
  )
## # A tibble: 1 × 7
##   observations  mean median standard_deviation   iqr minimum maximum
##          <int> <dbl>  <dbl>              <dbl> <dbl>   <dbl>   <dbl>
## 1         2693  4.96   3.43               4.28  5.08   -2.83    20.7
my_data %>%
  summarize(
    observations = sum(!is.na(my_second_variable)),
    mean = mean(my_second_variable, na.rm = TRUE),
    median = median(my_second_variable, na.rm = TRUE),
    standard_deviation = sd(my_second_variable, na.rm = TRUE),
    iqr = IQR(my_second_variable, na.rm = TRUE),
    minimum = min(my_second_variable, na.rm = TRUE),
    maximum = max(my_second_variable, na.rm = TRUE)
  )
## # A tibble: 1 × 7
##   observations  mean median standard_deviation   iqr minimum maximum
##          <int> <dbl>  <dbl>              <dbl> <dbl>   <dbl>   <dbl>
## 1         2525  29.9     29               5.39    10      23      40

7. Submission checklist

  1. Replace PUT YOUR NAME HERE in the header with your name.
  2. Save the file as LastName_EDA.Rmd.
  3. Make your own brigade and variable choices in the cadet-choices chunk.
  4. Run the chunks in order and answer every writing prompt.
  5. Click Knit and choose Knit to HTML.
  6. Open the HTML file and confirm that your tables, graphs, and explanations appear.
  7. Upload the generated LastName_EDA.html file in Canvas to instructor folder MAJ Ozga → F1 - Project Folder.

If knitting fails, read the first error message. Check the spelling of your brigade and variable names, and confirm that the CSV is in the same folder as the R Markdown file.