Programming is a systematic way to think about a problem — turning a fuzzy question into precise definitions and a chain of answers. Learning R is how we build that mental model.
There’s no point using AI if you’re trying to build that mental model yourself.
This week
Project mixer: meet your classmates and their project ideas
Statistics vs. data mining: where does the hypothesis go?
Exploratory Data Analysis, the modern way (PCS)
Graphical Summaries (base + ggplot2)
🤝 Project Mixer
Stand up and move around (10 minutes)
Three rounds of 3 minutes. Each round, find someone you haven’t talked to yet, ideally someone sitting on the other side of the room.
Each of you shares (about 1 minute each):
Your name, program, and one thing you do when you’re not doing statistics
A topic, dataset, or paper you’re thinking about for the final project (half-formed is fine)
One skill you bring: a domain you know, a dataset you have access to, coding, writing, …
Then ask each other:“What question would you want to answer with that data?”
Last minute: write down the names of the people you met and whose ideas overlap with yours.
Tip
Track 2 (pairs): this is a good time to find a partner. Track 1 (PhD, individual): find someone whose paper uses methods close to yours; they can be your sounding board. Proposals are due in Week 6.
Numerical Summaries
Data Types
A variable may be classified as:
Quantitative: numeric or integer
Ordinal: an ordered categorical variable (e.g., low/medium/high)
Qualitative: categorical, nominal, or factors
A dataframe may contain variables of different types.
Data may have additional structure as well. For example, when data are collected over time, we have time series data which is indexed by time. Similarly, when data are collected over a spatial domain, we have spatial data which is indexed by a location.
Data Summary
Example: body and brain size of mammals (two quantitative variables)
data(mammals, package ="MASS") # loads the data without attaching MASShead(mammals) # you can use ?MASS::mammals to get more info
body brain
Arctic fox 3.385 44.5
Owl monkey 0.480 15.5
Mountain beaver 1.350 8.1
Cow 465.000 423.0
Grey wolf 36.330 119.5
Goat 27.660 115.0
summary(mammals) # very skewed data
body brain
Min. : 0.005 Min. : 0.14
1st Qu.: 0.600 1st Qu.: 4.25
Median : 3.342 Median : 17.25
Mean : 198.790 Mean : 283.13
3rd Qu.: 48.203 3rd Qu.: 166.00
Max. :6654.000 Max. :5712.00
Basic Graphical Summaries
The raw data are very skewed; on the log scale the pattern appears:
body brain
body 1.0000000 0.9341638
brain 0.9341638 1.0000000
cor(log(mammals))
body brain
body 1.0000000 0.9595748
brain 0.9595748 1.0000000
Bivariate Data by Group
Example: Palmer penguins — 344 penguins of three species measured on three islands in Antarctica
# palmerpenguins is loaded in the setup chunk; ?palmerpenguins::penguins for details# bill_length_mm, bill_depth_mm: bill (culmen) dimensions# species: Adelie, Chinstrap, or Gentoohead(penguins)
# A tibble: 6 × 8
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
<fct> <fct> <dbl> <dbl> <int> <int>
1 Adelie Torgersen 39.1 18.7 181 3750
2 Adelie Torgersen 39.5 17.4 186 3800
3 Adelie Torgersen 40.3 18 195 3250
4 Adelie Torgersen NA NA NA NA
5 Adelie Torgersen 36.7 19.3 193 3450
6 Adelie Torgersen 39.3 20.6 190 3650
# ℹ 2 more variables: sex <fct>, year <int>
species bill_length_mm bill_depth_mm
Adelie :152 Min. :32.10 Min. :13.10
Chinstrap: 68 1st Qu.:39.23 1st Qu.:15.60
Gentoo :124 Median :44.45 Median :17.30
Mean :43.92 Mean :17.15
3rd Qu.:48.50 3rd Qu.:18.70
Max. :59.60 Max. :21.50
NAs :2 NAs :2
Boxplots of bill length by species
boxplot(bill_length_mm ~ species, penguins)
Example Continued
Another way to display this data is through a scatterplot
This reversal is Simpson’s paradox: a summary of the pooled data can point the opposite way from every group inside it. Always ask whether a grouping variable is hiding in your data.
📝 Self-Guided: Conditional Plots
Conditional plots with coplot() and lattice::xyplot() are not covered live — see the Self-Guided Practice section at the end of these slides.
Activity 1: Exploring Penguin Body Mass
Working with your neighbor (8 minutes)
Let’s explore the penguins dataset from the palmerpenguins package:
library(palmerpenguins)
Your tasks:
Calculate the mean and standard deviation of body_mass_g (what do you need to do about missing values?)
Create side-by-side boxplots of body_mass_g by species
Calculate the mean body mass for each species using by() or tapply()
What percentage of Gentoo penguins are heavier than the heaviest Adelie?
Post your findings on Ed Discussion!
Multivariate Data
Example: the same penguins data, now looking at all four body measurements at once; 344 penguins; 7 variables
Variable
Description
species
Adelie, Chinstrap, or Gentoo
island
Biscoe, Dream, or Torgersen
bill_length_mm
Bill length (mm)
bill_depth_mm
Bill depth (mm)
flipper_length_mm
Flipper length (mm)
body_mass_g
Body mass (g)
year
Study year (2007–2009)
Example
Note the missing values in the measurements
peng <-as.data.frame(penguins)peng$sex <-NULL# not used in this coursesummary(peng)
species island bill_length_mm bill_depth_mm
Adelie :152 Biscoe :168 Min. :32.10 Min. :13.10
Chinstrap: 68 Dream :124 1st Qu.:39.23 1st Qu.:15.60
Gentoo :124 Torgersen: 52 Median :44.45 Median :17.30
Mean :43.92 Mean :17.15
3rd Qu.:48.50 3rd Qu.:18.70
Max. :59.60 Max. :21.50
NAs :2 NAs :2
flipper_length_mm body_mass_g year
Min. :172.0 Min. :2700 Min. :2007
1st Qu.:190.0 1st Qu.:3550 1st Qu.:2007
Median :197.0 Median :4050 Median :2008
Mean :200.9 Mean :4202 Mean :2008
3rd Qu.:213.0 3rd Qu.:4750 3rd Qu.:2009
Max. :231.0 Max. :6300 Max. :2009
NAs :2 NAs :2
Example Continued
Missing values:
The by function allows us to consider summaries by group
mean(peng$body_mass_g)
[1] NA
mean(peng$body_mass_g, na.rm=T)
[1] 4201.754
by(data=peng$flipper_length_mm, INDICES = peng$species, FUN=mean, na.rm=T)
Flipper length and body mass have a strong positive correlation
round(cor(meas), 2)
bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
bill_length_mm 1 NA NA NA
bill_depth_mm NA 1 NA NA
flipper_length_mm NA NA 1 NA
body_mass_g NA NA NA 1
species island bill_length_mm bill_depth_mm flipper_length_mm
4 Adelie Torgersen NA NA NA
272 Gentoo Biscoe NA NA NA
body_mass_g year
4 NA 2007
272 NA 2009
Activity 2: Penguin Flippers and Body Size
Partner work (10 minutes)
Investigate the relationship between flipper length and body mass:
peng <-as.data.frame(palmerpenguins::penguins)
Your tasks:
Create a scatterplot of body_mass_g (x-axis) vs flipper_length_mm (y-axis)
Calculate the correlation between body_mass_g and flipper_length_mm (remember to handle missing values!)
Color points by species: plot(..., col=as.integer(species))
Add a legend to distinguish the three species
Calculate the correlation separately for each species using by()
Bonus: Is the pooled correlation stronger or weaker than the within-species ones? Compare with what happened to bill length vs. bill depth. What does this tell you about pooling groups?
Share your insights on Ed Discussion!
Categorical Data Analysis
Working with Categorical Variables
Categorical data represents groups or categories rather than numerical measurements.
Discuss: What p-value would convince you the die is unfair?
Part 2: Contingency Tables
Testing Independence Between Two Variables
Question: Is there an association between two categorical variables?
Example: Using the penguins data
Species (Adelie, Chinstrap, Gentoo)
Island (Biscoe, Dream, Torgersen)
Do the three species live on the same islands, or does knowing the island tell you something about the species?
Building a Contingency Table
# Cross-tabulate two factorsspec_island <-table(penguins$species, penguins$island,dnn =c("Species", "Island"))spec_island
Island
Species Biscoe Dream Torgersen
Adelie 44 56 52
Chinstrap 0 68 0
Gentoo 124 0 0
# Add the margins (row and column totals)addmargins(spec_island)
Island
Species Biscoe Dream Torgersen Sum
Adelie 44 56 52 152
Chinstrap 0 68 0 68
Gentoo 124 0 0 124
Sum 168 124 52 344
The zeros are already a strong hint: Chinstraps were only sampled on Dream, Gentoos only on Biscoe.
Visualizing Contingency Tables
# Proportions by row (where does each species live?)round(prop.table(spec_island, margin =1), 2)
Island
Species Biscoe Dream Torgersen
Adelie 0.29 0.37 0.34
Chinstrap 0.00 1.00 0.00
Gentoo 1.00 0.00 0.00
# Mosaic plotmosaicplot(spec_island, color =c("#B3CDE3", "#CCEBC5", "#FED9A6"),main ="Palmer Penguins: Species by Island",xlab ="Species", ylab ="Island")
Reading mosaic plots:
Width = proportion of total in that category
Height = conditional proportion
Chi-Square Test of Independence
Hypotheses: - \(H_0\): Species and Island are independent - \(H_A\): There is an association between Species and Island
Test statistic:\[X^2 = \sum_{\text{all cells}} \frac{(\text{observed} - \text{expected})^2}{\text{expected}}\]
Under \(H_0\), this follows \(\chi^2_{(r-1)(c-1)}\) distribution
# Visualizemosaicplot(spec_size, shade =TRUE,main ="Species vs Body Size",las =1)
Observation: Strong association: Gentoo penguins fill the “heavy” class; Adelie and Chinstrap are mostly “light” or “medium”
Activity 5: Complete Categorical Analysis
Partner work (15 minutes)
Adelie is the only species sampled on all three islands. Use it to answer:
Is the island where Adelie penguins were sampled associated with the study year? With their body size?
adelie <-subset(penguins, species =="Adelie")island_year <-table(adelie$island, adelie$year)island_size <-table(adelie$island, adelie$size)
Your tasks:
Create shaded mosaic plots for both tables
Perform chi-square tests for each
Check the expected counts (chisq.test(...)$expected). Are any below 5? If so, try fisher.test()
Calculate the proportions: prop.table(island_year, margin = 1)
Discussion questions: - Which table shows evidence of association, and which doesn’t? - What does a large p-value let you conclude, and what doesn’t it? - How would you report these findings?
The math behind chisq.test() and validity assumptions are already marked “Self-study guide” in the original material — see the Self-Guided Practice section at the end of these slides.
Summary: Categorical Data Analysis
Test Type
Question
R Function
Key Output
Goodness of Fit
Do observed frequencies match expected?
chisq.test(x)
p-value
Independence
Are two variables associated?
chisq.test(table)
p-value, residuals
Visualization
See patterns
mosaicplot(..., shade=T)
Color-coded residuals
Workflow:
Create table with table() or use built-in dataset
Visualize with barplot() or mosaicplot()
Test with chisq.test()
Examine residuals to understand patterns
Report findings with context
Practical Tips
Good practices:
✓ Always visualize before testing
✓ Check expected counts (should be ≥ 5)
✓ Examine residuals to understand where associations exist
✓ Report both statistical and practical significance
Common mistakes:
✗ Ignoring small expected counts
✗ Testing without looking at the data first
✗ Only reporting p-values without interpretation
✗ Treating ordinal categories as nominal
Statistics, Data Mining, and Where the Hypothesis Goes
Two things you can do with a dataset
Before we explore any data, we need to be clear about what kind of question we’re asking. Breiman put it like this:
There are two goals in analyzing the data:
Prediction. To be able to predict what the responses are going to be to future input variables;
Inference. To infer how nature is associating the response variables to the input variables.
— Breiman (2001), Statistical Modeling: The Two Cultures, as quoted in Donoho (2017)
Donoho (2017), 50 Years of Data Science, organizes the field around these two cultures: generative modeling (classical statistics: assume a model, make inferences about the mechanism) and predictive modeling (machine learning, data mining: stay silent about the mechanism, judge by accuracy).
Same data. Two completely different jobs, and two different places where the hypothesis lives.
So where is the hypothesis?
Statistics (generative)
Data science / data mining (predictive)
When the hypothesis appears
First. Stated before the data are seen
Last, or never. The pattern is found in the data
What you commit to up front
A model, a null hypothesis, a design
A target to predict and a loss function
What counts as success
Evidence against the null; an interpretable parameter
Accuracy on data you have never seen
Who adjudicates
A probability calculation under the assumed model
A held-out test set
Core risk
The model is wrong
The pattern doesn’t generalize
Typical output
“The effect is 14 points, 95% CI [11, 17.5]”
“This rule predicts with 87% accuracy”
The key asymmetry: classical inference is valid because the hypothesis came first. Data mining gives that up, and buys its credibility back a different way, with a test set. If a pattern came out of the data, testing it on those same data is not a test. That is what EDA + PCS (next) are for.
Exploratory Data Analysis, the Modern Way
What EDA is for
EDA is where you generate hypotheses, find anomalies, and learn what the data actually contain — before any model.
We’ll follow Yu & Barter, Veridical Data Science (MIT Press, 2024), which is free online at vdsbook.com. “Veridical” means truthful: the book’s whole question is how to tell which of your findings are real.
Their vocabulary, which we’ll use for the rest of the quarter:
the DSLC — the data science life cycle, the actual sequence of steps
exploratory vs. explanatory analysis
the question-and-answer workflow
PCS — predictability, computability, stability
The Data Science Life Cycle (DSLC)
The DSLC “starts with problem formulation, data collection, and data cleaning and traverses through multiple types of data analyses to arrive at an assessment of some data-derived results, which is then communicated to a domain audience.”
— Yu & Barter (2024), Ch. 2
domain question
↓
data collection ←──────────────┐
↓ │
data cleaning & preparation │ every arrow is a
↓ │ JUDGMENT CALL you
EDA ←───────────┐ │ must write down
↓ │ iterate │
analysis / model ┘ │
↓ │
PCS evaluation ─────────────────┘
↓
communication → updated domain knowledge
EDA is not a stage you finish. You come back to it every time the model surprises you.
Exploratory vs. explanatory
Exploratory
“…preliminary data visualizations… produced fairly quickly and roughly.”
Audience: you
Dozens of them per session
Default settings are fine
Most of them will be boring — that’s the point
Explanatory
“…the process of producing polished presentation-quality figures that will be used to communicate… particularly interesting patterns and trends.”
Audience: everyone else
One or two per report
One clear takeaway message each
Worth an hour of work
Yu & Barter (2024), Ch. 5
Don’t polish while exploring, and don’t ship a default plot. Knowing which one you’re making saves hours.
The question-and-answer workflow
Not: “plot everything and stare at it.”
Instead: use domain knowledge to ask a specific question, answer it with one summary or figure, and let the answer suggest the next question.
Q: How do graduation rates differ between private and public colleges?
↓
A: Private colleges' median is about 14 points higher.
↓
Q: Wait -- is a 118% graduation rate possible?
↓
A: No. One college is an error. Does it change the 14 points?
↓
Q: Are these schools even comparable in size?
↓ ...
Keep the project goal in view, or the chain never ends. Each answer should either change what you’d do next, or get written down and dropped.
Covariance is scale-dependent (that huge number means nothing on its own). Correlation is standardized to \([-1, 1]\) — and only summarizes the linear part. Always plot first.
Activity 6: A question-and-answer EDA
Partner work (15 minutes)
Using college, run three rounds of the Q&A workflow on this domain question:
Do colleges that spend more per student graduate more students?
For each round, write down (a) the question, (b) the one summary or plot that answers it, (c) the next question it raised.
Suggested starting points — Expend, S.F.Ratio, PhD, perc.alumni, Top10perc, and the rates you create yourself.
Required in your three rounds:
at least one check for impossible or suspicious values
at least one comparison that needs a rate, not a count
one sentence at the end: “This is an exploratory finding. To confirm it I would need ______.”
Is the Finding Real? The PCS Framework
Three principles
Yu & Barter’s answer to “which of my findings are real”:
Predictability
Does the result reemerge in new or held-out data, and is it consistent with what the domain already knows?
Computability
Can someone else rerun it? Is the code, the workflow, and every judgment call documented and feasible to execute?
Stability
Does the result survive the choices you made — in cleaning, in sampling, in modeling, in plotting?
“You can show that your results are predictable by computationally demonstrating that they reemerge in relevant future data and are in line with domain knowledge” and stable “by computationally demonstrating that they do not change dramatically when appropriate changes are made throughout the DSLC.” — Yu & Barter (2024), Ch. 1
Predictability in action
The cheapest possible version: split the data, find the pattern in one half, check it in the other. (This is the CTF idea, shrunk to fit one afternoon.)
# A tibble: 2 × 4
half No Yes gap
<chr> <dbl> <dbl> <dbl>
1 explore 55 68 13
2 hold out 55 70 15
13 points in the half we explored, 15 in the half we held back. The finding reemerged. If it had vanished, we’d have found a pattern in noise.
Tip
Do the split before you start exploring, and don’t look at the held-out half until you’re done. That’s the whole discipline.
Computability in action
Boring, and the reason your result exists at all tomorrow:
Every number in your report comes from code in the document, not from something you typed in by hand
set.seed() before anything random
Raw data read, never edited — all cleaning happens in code
Every judgment call is a line of code with a comment saying why
sessionInfo() or renv so the package versions are on the record
# JUDGMENT CALL: Cazenovia College reports Grad.Rate = 118, which is# impossible. We drop it. See the stability table below -- the 14-point# gap is unchanged whether we drop, cap, or keep this row.college_clean <-filter(college, Grad.Rate <=100)
This is what you’re graded on in the project.
Stability: three kinds of perturbation
Perturb this
Concretely
Asks
The data
bootstrap, drop a subgroup, add noise
would another sample have shown this?
Your judgment calls
drop vs. cap vs. NA; mean vs. median; which rows to include
did I find this, or did I choose it?
The visualization
binwidth, axis limits, chart type, color scale
am I seeing this, or drawing it?
“Investigate stability through reasonable alternative visualization choices… design choices significantly impact conclusions.”
— Yu & Barter (2024), Ch. 5
“Reasonable” is doing a lot of work here. A perturbation counts only if you would have been willing to make that choice before seeing where it led.
Stability in action: judgment calls
One function, so every row of the table is computed the same way:
gap <-function(d, stat = median) { s <-tapply(d$Grad.Rate, d$Private, stat)unname(s["Yes"] - s["No"])}tibble(choice =c("all data, median", "drop Grad.Rate > 100", "cap at 100","all data, mean", "only colleges with 1000+ undergrads"),gap =c(gap(college),gap(filter(college, Grad.Rate <=100)),gap(mutate(college, Grad.Rate =pmin(Grad.Rate, 100))),gap(college, mean),gap(filter(college, F.Undergrad >=1000))))
# A tibble: 5 × 2
choice gap
<chr> <dbl>
1 all data, median 14
2 drop Grad.Rate > 100 14
3 cap at 100 14
4 all data, mean 13.0
5 only colleges with 1000+ undergrads 17
Reading that table
The impossible value did not matter at all — 14 points either way. Write that down and stop worrying about it.
Mean instead of median: 13 instead of 14. Stable.
Restrict to larger colleges: 17 points. Not stable — and that is the most interesting line in the table.
An unstable result is not a failure, it’s an important finding. The gap depends on size, which means “private vs. public” was partly standing in for “small vs. large”. That could be your next question.
This is exactly what a single p-value would have hidden from you.
Stability in action: perturbing the data
Resample the colleges 1,000 times and recompute the gap each time:
ggplot(tibble(boot_gap), aes(boot_gap)) +geom_histogram(binwidth =1, fill ="grey40") +labs(x ="Private - public median gap (bootstrap)", y =NULL) +theme_minimal(base_size =13)
11 to 17.5 points. Never zero — the sign of the effect is not in doubt, even though its size is only known to within about 6 points.
Stability in action: perturbing the plot
Same variable, same data, two defensible binwidths:
ggplot(college, aes(Grad.Rate)) +geom_histogram(binwidth =2, fill ="grey40") +labs(title ="binwidth = 2", x ="Graduation rate", y =NULL) +theme_minimal(base_size =14)ggplot(college, aes(Grad.Rate)) +geom_histogram(binwidth =10, fill ="grey40") +labs(title ="binwidth = 10", x ="Graduation rate", y =NULL) +theme_minimal(base_size =14)
Is the distribution bumpy or smooth? The data didn’t decide that, you did. Any feature that appears at one binwidth and not another should not be a finding.
Activity 7: Run a stability check
Partner work (15 minutes)
Take the finding from your Activity 6 Q&A chain — whatever it was — and put it through PCS.
State it in one sentence, with a number in it.
Stability (judgment calls): list the choices you made (rows excluded, mean vs. median, how you handled missing values, any cutoff). Recompute your number under three alternative choices. Build the table.
Stability (data): bootstrap it 1,000 times and report the 2.5% and 97.5% quantiles.
Predictability: recompute it in the "hold out" half. Does it reemerge?
Verdict: stable, unstable, or unstable in an interesting way?
Report back in one slide-worth of words. If your finding fell apart, say so — that’s a successful stability check, not a failed analysis.
The EDA workflow we’ll use all quarter
1. STATE the domain question
↓
2. SPLIT the data (explore / hold out)*
↓
3. CLEAN -- and write down every judgment call
↓
4. ASK a specific question ──┐
↓ │
5. ANSWER with one summary │ repeat
or rough exploratory plot │
↓ │
6. LET the answer raise the ─┘
next question
↓
7. PCS the finding: predictable? computable? stable?
↓
8. POLISH one explanatory figure and say plainly
whether this is exploratory or confirmed
EDA comes before formal hypothesis testing — and it determines whether that test means anything.
Activity 8: Complete EDA challenge
Partner work (15 minutes)
data(mtcars)
Domain question: does engine power cost you fuel efficiency, and by how much?
Question: phrase it as something mtcars can answer with hp and mpg
Explore: scatterplot, correlation, and one grouped summary (try cyl)
Impossible values / anomalies: anything suspicious? (rownames(mtcars) is worth a look)
Comparability: is mpg comparable across cars of very different wt? What would you compare instead?
Stability: does your headline number survive (a) dropping the single most extreme car, (b) median instead of mean, (c) a bootstrap?
One sentence: exploratory finding, or something you’d defend?
Present: your number, and the one perturbation that moved it most.
Resources for EDA
Books:
Yu, B. & Barter, R. (2024). Veridical Data Science. MIT Press — vdsbook.com (Ch. 1–2 for PCS and the DSLC, Ch. 5 for EDA)
Wickham, Çetinkaya-Rundel & Grolemund. R for Data Science (2e) — r4ds.hadley.nz
Tukey, J. (1977). Exploratory Data Analysis — the original
Papers worth your afternoon:
Donoho, D. (2017). “50 Years of Data Science.” JCGS 26(4), 745–766
Breiman, L. (2001). “Statistical Modeling: The Two Cultures.” Statistical Science 16(3)
Tukey, J. (1962). “The Future of Data Analysis.” Ann. Math. Statist. 33(1)
This entire reference block is already marked “Self-study guide” in the original material — see the Self-Guided Practice section at the end of these slides.
ggplot2: Modern Data Visualization
Why ggplot2?
Advantages over base R:
Consistent syntax across plot types
Layered approach builds plots incrementally
Automatic legends and color schemes
Better defaults for publication-quality graphics
Easier faceting (multiple subplots)
Philosophy: Grammar of Graphics - build plots by combining data, aesthetics, and geometric objects
The ggplot2 Framework
Three essential components:
Data – your dataset
Aesthetics (aes()) – map variables to visual properties
Base R: - See reference slides (gray background) - ?plot, ?par for help
Practice: The best way to learn is by creating many plots!
📝 Self-Guided Practice (Wednesday Session)
The following slides are not covered live. Work through them on your own, or bring questions to the Wednesday self-guided session.
Conditional Plots
The function coplot displays several plots on the same scale. The syntax y~x|a means plots of y vs. x are conditional on a (order from bottom and from left)
If some expected counts < 5, use Fisher’s exact test instead
Random sampling: Data should come from random samples
Warning signs: - Small expected counts → Fisher’s exact test - Non-independent observations → Different methods needed
# Fisher's exact test for small samplesfisher.test(matrix(c(3, 1, 1, 5), nrow =2))
Fisher's Exact Test for Count Data
data: matrix(c(3, 1, 1, 5), nrow = 2)
p-value = 0.1905
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
0.40161 930.24692
sample estimates:
odds ratio
10.29391
Extracting Outliers
Self-study guide
boxplot() returns everything it drew, so you can get the outliers back:
# Which group they belong to (1 = No, 2 = Yes for Private)b.output$group
[1] 1 1 1 1 2 2 2 2 2 2 2 2
Boxplots identify outliers as values beyond 1.5 × IQR from the quartiles. b.output$stats holds the quartiles and medians, one column per group.
Common Transformations
Self-study guide
Different relationships need different transformations:
Pattern
Transformation
When to Use
Exponential growth
log(y)
Data growing multiplicatively
Power relationship
log(x) and log(y)
Both variables span large ranges
Square root
sqrt(y)
Count data with increasing variance
Inverse
1/x or 1/y
Asymptotic relationships
Rule of thumb: If residuals show patterns, try transforming!
Base R Graphics: Reference Material
⚠️ Self-Study Guide - Not Covered in Detail in Class
The following slides contain useful reference material for customizing base R graphics. Review these on your own time.
Reference: Adjusting Plot Attributes
Self-study guide
Parameters in the function plot:
plot(Year, HR, xlab ="Season",ylab ="Avg HR Hit Per Team Per Game",main ="Home Run Hitting in the MLB Across Seasons",sub =" (a) ")
Reference: Changing Plot Type and Symbols
Self-study guide
plot(Year, HR, xlab ="Season", type ="b",ylab ="Avg HR Hit Per Team Per Game",main ="Home Run Hitting in the MLB Across Seasons")
Common type values: - "p" for points (default) - "l" for lines - "b" for both - "h" for histogram-like vertical lines
Reference: Adding Layers
Self-study guide
plot(Year, HR, xlab ="Season", pch =19, cex =0.9,ylab ="Avg HR Hit Per Team Per Game",main ="Home Run Hitting in the MLB Across Seasons")lines(lowess(Year, HR), lwd =2)
par(mfrow =c(2,1))fit <-lowess(Year, HR, f =1/12)Residual <- HR - fit$yplot(Year, HR, xlab ="Season",ylab ="Avg HR Hit Per Team Per Game")plot(Year, Residual, xlab ="Season", ylab ="Residuals")