100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Statistical Analysis in R

Learn how to compute descriptive statistics, run hypothesis tests, and fit regression models using R's base stats package.

Practical RIntermediate10 min readJul 10, 2026
Analogies

Why R for Statistical Analysis

R was built by statisticians, for statisticians — the language ships with a stats package in base R that covers everything from descriptive summaries to hypothesis tests and linear models without installing a single extra package. Because R is vectorized, functions like mean(), sd(), and t.test() operate directly on whole columns of a data frame, so you rarely need explicit loops to summarize data. This design lets an analyst move from raw data to a defensible statistical conclusion in a handful of lines, which is why R remains the default tool in biostatistics, econometrics, and academic research.

🏏

Cricket analogy: Like a purpose-built spin-bowling pitch at Chepauk that rewards specialists such as Ravichandran Ashwin, R's stats package is tuned for statisticians so a single t.test() call replaces overs of manual calculation.

Descriptive Statistics and Data Distributions

The summary() function on a numeric vector or data frame returns the minimum, first quartile, median, mean, third quartile, and maximum in a single call, giving a fast five-number-plus-mean overview of any variable. Pairing this with sd() for spread, IQR() for the interquartile range, and hist() or density() for visual shape lets you check skewness and detect outliers before running any formal test — a step many beginners skip and later regret when a t-test assumption is silently violated.

🏏

Cricket analogy: Checking summary() before modeling is like reading a batter's career strike rate, average, and highest score on Cricinfo before picking them for a T20 side — skip it and you might select someone with one huge but unrepresentative innings.

R's distribution functions follow a consistent d, p, q, r prefix convention for every named distribution: dnorm() gives the density at a point, pnorm() gives the cumulative probability, qnorm() gives the quantile for a given probability, and rnorm() generates random draws. The same pattern applies to dbinom/pbinom/qbinom/rbinom for the binomial, dpois for Poisson, and dt/pt for the t-distribution used internally by t.test(), so once you learn the convention for one distribution you effectively know it for all of them.

🏏

Cricket analogy: The d/p/q/r prefix system is like the consistent over-by-over scoring notation used across every format — Test, ODI, T20 — once you learn how a wicket or six is logged in one format, IPL scorecards read the same way.

Hypothesis Testing

The t.test() function handles one-sample, two-sample, and paired t-tests through its x, y, and paired arguments, returning a t-statistic, degrees of freedom, p-value, and 95% confidence interval in one object. A p-value below your chosen significance level, typically 0.05, suggests the observed difference is unlikely under the null hypothesis of no effect, but R also reports the confidence interval, which is often more informative because it shows the plausible range and direction of the effect rather than a single accept/reject verdict.

🏏

Cricket analogy: Running t.test() on two batsmen's scores is like the third umpire reviewing a run-out with ball-tracking — you don't just get an out/not-out call, you get the margin (the confidence interval) showing how close it really was.

ANOVA and Chi-Square Tests

When comparing means across three or more groups, aov(response ~ group, data = df) runs an analysis of variance and summary() on the resulting object reports an F-statistic and p-value testing whether any group mean differs; a significant result is typically followed by TukeyHSD() to find which specific pairs differ. For categorical data, chisq.test() takes a contingency table built with table() and tests whether two categorical variables are independent, which is the standard tool for questions like whether purchase rate differs by region.

🏏

Cricket analogy: Running aov() to compare batting averages across four IPL franchises is like the league comparing team run-rates across Mumbai Indians, Chennai Super Kings, Royal Challengers, and Kolkata — then TukeyHSD() pinpoints exactly which two teams differ.

r
# One-sample and two-sample t-tests
scores_a <- c(72, 85, 90, 68, 77, 95, 88)
scores_b <- c(65, 70, 74, 60, 68, 72, 71)
t.test(scores_a, scores_b, paired = FALSE)

# One-way ANOVA across three groups
df <- data.frame(
  score = c(scores_a, scores_b, c(80, 82, 79, 85, 90, 77, 83)),
  group = rep(c("A", "B", "C"), each = 7)
)
model <- aov(score ~ group, data = df)
summary(model)
TukeyHSD(model)

# Chi-square test of independence
purchase_table <- table(df$group, df$score > 80)
chisq.test(purchase_table)

# Linear regression
fit <- lm(score ~ group, data = df)
summary(fit)

P-values only tell you the probability of seeing data this extreme if the null hypothesis were true — they do not tell you the probability the null hypothesis is true, nor the size of the effect. When running many tests at once (e.g., comparing 20 groups pairwise), apply p.adjust(p_values, method = "BH") (Benjamini-Hochberg) or "bonferroni" to control the false discovery rate; without correction, a 5% significance level guarantees false positives as the number of tests grows.

Correlation and Linear Regression

cor(x, y) computes Pearson's correlation coefficient by default, cor.test() adds a significance test and confidence interval, and switching method = "spearman" gives a rank-based correlation robust to non-linear monotonic relationships. For prediction, lm(y ~ x1 + x2, data = df) fits an ordinary least squares model, and summary(fit) reports each coefficient's estimate, standard error, t-value, and p-value alongside the model's R-squared, which measures the proportion of variance in the outcome explained by the predictors.

🏏

Cricket analogy: Running cor.test() between a bowler's economy rate and match wins is like Cricinfo analysts checking whether Jasprit Bumrah's tight overs statistically correlate with victories, with the confidence interval showing how strong that link really is.

A strong correlation coefficient never proves causation — ice cream sales and drowning deaths correlate because both rise in summer, not because one causes the other. Before trusting an lm() model, check its assumptions: plot residuals(fit) against fitted values to check for non-linearity and non-constant variance, and use plot(fit) to see the diagnostic panels (Q-Q plot, leverage, Cook's distance) that flag influential outliers skewing the coefficients.

  • summary(), sd(), and IQR() give a fast descriptive overview of a variable before any formal test is run.
  • R's distribution functions follow a consistent d/p/q/r prefix convention (density, cumulative probability, quantile, random draw) across every named distribution.
  • t.test() supports one-sample, two-sample, and paired designs and returns both a p-value and a confidence interval.
  • aov() plus TukeyHSD() compares means across three or more groups and identifies which specific pairs differ.
  • chisq.test() on a table()-built contingency table tests independence between two categorical variables.
  • lm() fits linear regression models, and summary(fit) reports coefficients, p-values, and R-squared.
  • Always check regression assumptions with plot(fit) and correct for multiple testing with p.adjust() before trusting p-values.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#RProgrammingStudyNotes#StatisticalAnalysisInR#Statistical#Analysis#Descriptive#Statistics#StudyNotes#SkillVeris#ExamPrep