Exploratory Data Analysis (EDA) is the practice of examining a dataset before applying formal statistical models or hypothesis tests. The term was coined by John Tukey (1977), who argued that analysts should first look at the data rather than jump straight to confirming or rejecting hypotheses. HCI user studies often produce messy, human-generated data: participants behave in unexpected ways, sensors drop readings, and experimental conditions interact with individual differences. EDA helps you catch these issues early. Specifically, EDA answers questions like:
Performing EDA should be part of your research muscle memory. Run quick analyses as soon as you have data. In quantitative HCI work, this means visualizing data during pilot study phase, after the first few participants’ data arrive, and again once the full dataset is collected. EDA is not optional: skipping it risks running tests on data that violate assumptions, contain errors, or hide patterns that change your interpretation.
Below, we walk through various techniques for EDA using a simulated dataset from a text-entry user study. We compare two input methods (standard keyboard vs. swipe keyboard) on typing speed (words per minute) and error rate.
Let’s create the dataset. We simulate realistic keystroke timing data using tibble(). In practice, you would load your own data using read_csv().
library(tidyverse) # includes dplyr, ggplot2, tidyr, readr
set.seed(42)
n <- 60 # 60 participants, 30 per condition
keystroke_data <- tibble(
participant_id = paste0("P", sprintf("%03d", 1:n)),
condition = rep(c("standard", "swipe"), each = n / 2),
wpm = c(rnorm(30, mean = 52, sd = 8),
rnorm(30, mean = 45, sd = 10)),
error_rate = c(rnorm(30, mean = 0.03, sd = 0.015),
rnorm(30, mean = 0.06, sd = 0.02)),
age = sample(18:55, n, replace = TRUE),
experience_level = sample(c("novice", "intermediate", "expert"),
n, replace = TRUE,
prob = c(0.4, 0.35, 0.25))
)
# Ensure error_rate is non-negative
keystroke_data <- keystroke_data %>%
mutate(error_rate = pmax(error_rate, 0))
Always start by examining the structure and first few rows:
glimpse(keystroke_data)
# Rows: 60
# Columns: 6
# $ participant_id <chr> "P001", "P002", "P003", "P004", "P005", "P006", "P007", "P00…
# $ condition <chr> "standard", "standard", "standard", "standard", "standard", …
# $ wpm <dbl> 62.96767, 47.48241, 54.90503, 57.06290, 55.23415, 51.15100, …
# $ error_rate <dbl> 0.02449148, 0.03277846, 0.03872736, 0.05099605, 0.01909062, …
# $ age <int> 34, 44, 47, 53, 37, 28, 34, 26, 34, 30, 48, 18, 34, 50, 45, …
# $ experience_level <chr> "expert", "novice", "intermediate", "novice", "expert", "nov…
Check for missing values and duplicates
keystroke_data %>%
summarise(across(everything(), ~ sum(is.na(.))))
keystroke_data %>%
filter(duplicated(participant_id)) %>%
nrow()
Use group_by() and summarise() to compute descriptive statistics for each input method:
summary_stats <- keystroke_data %>%
group_by(condition) %>%
summarise(
n = n(),
mean_wpm = mean(wpm),
sd_wpm = sd(wpm),
median_wpm = median(wpm),
iqr_wpm = IQR(wpm),
mean_err = mean(error_rate),
sd_err = sd(error_rate)
)
summary_stats