CS1B — R Programming
Exam Notes Based on Past Paper Analysis
IAI Actuarial Statistics · Papers 2019–2024
📌 HOW TO USE: Read top-to-bottom. Every code block is exam-ready — copy the pattern, swap
the variable/dataset names. Focus on sections marked ★★★ first.
MODULE 1 — Data Import & Environment Setup
★★★ ALWAYS FIRST: Every paper gives a dataset. Load it correctly before anything else.
1.1 Loading Different File Types
CSV files (most common in CS1B)
# Method 1: Basic (uses working directory)
df <- [Link]("[Link]")
# Method 2: With full path (safer in exam)
df <- [Link]("C:/Users/admin/Desktop/CS1B/[Link]")
# Method 3: With options (use when CSV has issues)
df <- [Link]("[Link]", header=TRUE, stringsAsFactors=FALSE)
# Method 4: Tab-separated .txt files
df <- [Link]("[Link]", header=TRUE, sep="\t")
# Method 5: Space-separated .txt files
df <- [Link]("[Link]", header=TRUE)
.Rdata / .RData files
# Loads all objects saved in that session directly into environment
load("[Link]") # Objects appear by their saved names
ls() # See what was loaded
Vectors / matrices given inline in question
# When data is given directly in the question:
x <- c(23, 45, 12, 67, 34, 89, 56) # numeric vector
y <- c('A','B','A','C','B','A') # character vector
mat <- matrix(c(1,2,3,4,5,6), nrow=2, ncol=3)
1.2 Always Inspect Data First (Mandatory habit)
str(df) # Structure: column names, types, preview
head(df) # First 6 rows
head(df, 10) # First 10 rows
tail(df) # Last 6 rows
dim(df) # (rows, cols)
nrow(df) # number of rows
ncol(df) # number of columns
names(df) # column names
colnames(df) # same as names()
summary(df) # min/max/mean/quartiles for each column
1.3 Accessing Columns & Subsetting
# Python equiv: df['col'] or [Link]
df$ColumnName # Access one column (returns vector)
df[["ColumnName"]] # Same, double bracket
df[, 2] # Access by column index
df[1:5, ] # First 5 rows, all columns
df[df$Age > 30, ] # Filter rows (like df[[Link] > 30] in pandas)
df[df$Sex == 'M', 'Age'] # Filter rows AND select column
# Useful: which() gives indices
which(df$Score > 90) # Row numbers where condition is TRUE
df[which(df$Score > 90), ] # Same as above but explicit
# Table of a categorical column
table(df$Category) # Frequency table
[Link](table(df$Cat)) # Proportion table (sums to 1)
1.4 Creating & Modifying Data
# Add new column (like df['new_col'] = ... in pandas)
df$LogClaim <- log(df$ClaimAmount)
df$Category <- ifelse(df$Return > 0, "Positive", "Negative")
# Remove a column
df$unwanted <- NULL
# Rename: colnames is a vector you can assign to
colnames(df)[3] <- "NewName"
MODULE 2 — Descriptive Statistics & Data Summarisation ★★★
2.1 Basic Summary Statistics
x <- df$ClaimAmount # Work with a vector
mean(x) # Arithmetic mean
median(x) # Median
var(x) # Sample variance (divides by n-1)
sd(x) # Standard deviation = sqrt(var(x))
min(x); max(x) # Range endpoints
range(x) # c(min, max) vector
sum(x) # Sum
length(x) # Count of elements (= n)
# Quantiles
quantile(x) # 0%, 25%, 50%, 75%, 100%
quantile(x, 0.9) # 90th percentile
quantile(x, c(0.25, 0.75)) # Q1 and Q3
IQR(x) # Interquartile range = Q3 - Q1
# Round output (exam often asks to n decimal places)
round(mean(x), 3) # Round to 3 decimal places
round(mean(x), 0) # Round to integer
2.2 Correlation
# Pearson (default) — measures linear association
cor(x, y) # Pearson correlation
cor(x, y, method='pearson') # Explicit
# Spearman — rank-based, robust to outliers
cor(x, y, method='spearman')
# Kendall's tau
cor(x, y, method='kendall')
# Full correlation matrix for data frame
cor(df[ , c('col1','col2','col3')])
round(cor(df[ , 2:5]), 3) # Round all to 3 dp
# Test if correlation is significant
[Link](x, y) # H0: true correlation = 0
[Link](x, y, method='spearman')
# Read: t-stat, df, p-value, 95% CI, estimate
2.3 Contingency Tables (appears every exam)
# Build from two categorical columns
tbl <- table(df$Sector1, df$Sector2) # rows x cols
tbl # Print table
# Proportions
[Link](tbl) # Cell proportions (sum = 1)
[Link](tbl, 1) # Row proportions
[Link](tbl, 2) # Column proportions
# Chi-squared test on the table
[Link](tbl)
# Output: X-squared, df, p-value
MODULE 3 — Plotting & Visualisation ★★★
EXAM TIP: Plotting questions almost always ask you to add labels, titles, and sometimes lines.
Always use xlab, ylab, main.
3.1 Histogram
# Basic histogram
hist(x)
# Exam-standard histogram with all options
hist(x,
breaks = 20, # Number of bins (or try "FD" for auto)
freq = FALSE, # freq=FALSE → y-axis is DENSITY (needed to
overlay curve)
col = "lightblue", # Fill colour
border = "black", # Bar border colour
main = "Histogram of Claim Amounts",
xlab = "Claim Amount (£)",
ylab = "Density")
# Add theoretical density curve on top (after freq=FALSE)
curve(dnorm(x, mean=mean(data), sd=sd(data)), add=TRUE, col='red', lwd=2)
# Add empirical density (kernel density estimate)
lines(density(x), col='blue', lwd=2)
3.2 Q-Q Plot (Normality Check) ★★★
COMMON QUESTION: "Plot a Q-Q plot and comment on normality." — appears in nearly every
past paper.
# Q-Q plot with reference line
qqnorm(x, main='Normal Q-Q Plot of Returns')
qqline(x, col='red', lwd=2) # Adds the reference line
# HOW TO INTERPRET (write in answer):
# Points close to red line → data is approximately normal
# Heavy tails (points bow away at ends) → heavier than normal
# S-shape → skewed distribution
# Systematic curve → non-normal
3.3 Boxplot
# Simple boxplot
boxplot(x, main='Boxplot of Returns', ylab='Monthly Return (%)')
# Grouped boxplot (by category) — very common in exam
boxplot(Return ~ Sector, data=df,
main = "Returns by Sector",
xlab = "Sector",
ylab = "Return (%)",
col = c("lightblue","lightgreen","lightyellow"),
las = 1) # las=1: horizontal y-axis labels
# Interpreting boxplot (write in answer):
# Middle line = median
# Box = IQR (Q1 to Q3)
# Whiskers = 1.5 * IQR beyond box
# Points beyond whiskers = potential outliers
3.4 Scatter Plot
# Basic scatter plot
plot(x, y)
# Full options
plot(df$Age, df$ClaimAmount,
main = "Claim Amount vs Age",
xlab = "Age",
ylab = "Claim Amount",
pch = 16, # point type: 16=filled circle, 1=hollow, 4=cross
col = "navy",
cex = 0.8) # point size scaling
# Add regression line on scatter plot
abline(lm(y ~ x), col='red', lwd=2)
# Add any line: abline(intercept, slope)
abline(0, 1, col='green', lty=2) # y = x reference line
abline(h=0, col='gray') # Horizontal line at y=0
abline(v=mean(x), col='blue', lty=3)# Vertical line at mean
3.5 Bar Chart
counts <- table(df$Category)
# barplot takes a named vector or table
barplot(counts,
main = "Frequency by Category",
xlab = "Category",
ylab = "Frequency",
col = "steelblue",
las = 1,
horiz = FALSE) # horiz=TRUE for horizontal bars
3.6 Multiple Plots in One Window
# 2 plots side by side
par(mfrow = c(1, 2)) # 1 row, 2 columns
hist(x, main='Histogram')
qqnorm(x, main='Q-Q Plot'); qqline(x, col='red')
# 2x2 grid (4 plots)
par(mfrow = c(2, 2))
plot(model) # regression diagnostic: uses all 4 slots
# Reset to single plot
par(mfrow = c(1, 1))
3.7 Scree Plot (Principal Components)
NOTE: The Nov 2019 IAI paper specifically asked for a scree plot using PCA. Know this.
# Run PCA
pca_result <- prcomp(df_numeric, scale.=TRUE)
# Scree plot
plot(pca_result, # or:
type="l", # type="b" for both points and lines
main="Scree Plot")
# Manual scree plot using variance
variance <- pca_result$sdev^2
prop_var <- variance / sum(variance)
plot(prop_var, type="b",
xlab="Principal Component",
ylab="Proportion of Variance",
main="Scree Plot")
# Pairwise correlation among PCs (they should be 0)
pca_scores <- pca_result$x # n x p matrix of scores
round(cor(pca_scores), 3) # Should be identity matrix
3.8 Plotting Theoretical Distributions
# Plot Normal PDF
x_range <- seq(-4, 4, by=0.01)
plot(x_range, dnorm(x_range, mean=0, sd=1), type="l",
main="Standard Normal PDF", xlab="x", ylab="f(x)", col="blue")
# Add Poisson PMF as bar chart
k <- 0:15
plot(k, dpois(k, lambda=3), type="h", # type="h" = vertical lines
(histogram-like)
lwd=3, col="darkgreen",
main="Poisson(3) PMF", xlab="k", ylab="P(X=k)")
# Plot CDF
plot(x_range, pnorm(x_range), type="l",
main="Normal CDF", xlab="x", ylab="F(x)")
MODULE 4 — Probability Distributions ★★★
PATTERN: d=density/PMF, p=CDF, q=quantile/inverse-CDF, r=random sample. Same for ALL
distributions.
4.1 The d/p/q/r Pattern
# NORMAL — most tested
dnorm(x, mean=mu, sd=sigma) # f(x) — height of PDF at x
pnorm(x, mean=mu, sd=sigma) # F(x) — P(X <= x)
qnorm(p, mean=mu, sd=sigma) # x s.t. P(X<=x)=p (inverse CDF)
rnorm(n, mean=mu, sd=sigma) # Generate n random values
# Upper tail: P(X > x)
1 - pnorm(x, mean, sd)
pnorm(x, mean, sd, [Link]=FALSE) # Equivalent
# POISSON
dpois(k, lambda) # P(X = k)
ppois(k, lambda) # P(X <= k)
qpois(p, lambda) # Smallest k s.t. P(X<=k) >= p
rpois(n, lambda) # Simulate
# BINOMIAL
dbinom(k, size=n, prob=p) # P(X = k)
pbinom(k, size=n, prob=p) # P(X <= k)
qbinom(p, size=n, prob=p) # Quantile
rbinom(n, size=n, prob=p) # Simulate
# EXPONENTIAL
dexp(x, rate=lambda) # rate = 1/mean
pexp(x, rate=lambda)
qexp(p, rate=lambda)
rexp(n, rate=lambda)
# GAMMA
dgamma(x, shape=alpha, rate=beta) # rate = 1/scale
pgamma(x, shape=alpha, rate=beta)
# CHI-SQUARED
dchisq(x, df=k)
pchisq(x, df=k)
qchisq(p, df=k) # Critical value for chi-sq test
# t-DISTRIBUTION
dt(x, df=n)
qt(p, df=n) # Critical value for t-test
pt(t_stat, df=n) # p-value from t-statistic
# F-DISTRIBUTION
qf(p, df1, df2) # F critical value
pf(F_stat, df1, df2)# p-value
# NEGATIVE BINOMIAL
dnbinom(k, size=r, prob=p) # P(X=k) where X = failures before r
successes
# BETA
dbeta(x, shape1=a, shape2=b) # x in [0,1]
# UNIFORM
dunif(x, min=a, max=b)
punif(x, min=a, max=b)
4.2 Simulation (appears in EVERY paper)
# ALWAYS set seed first — exam checks reproducibility
[Link](42) # Or whatever number question specifies
# Simulate and compute statistics
sim <- rnorm(10000, mean=100, sd=15)
mean(sim) # Should be close to 100
var(sim) # Should be close to 225
# Simulate and compute probability
[Link](100)
claims <- rexp(10000, rate=1/500) # mean=500
mean(claims > 700) # Estimated P(X > 700)
# Simulate sum of random variables
[Link](1)
n_sim <- 100000
S <- replicate(n_sim, sum(rexp(10, rate=0.5))) # Sum of 10 Exp(0.5)
hist(S, freq=FALSE, main='Distribution of S')
# Using for-loop for simulation
[Link](42)
results <- numeric(10000) # Pre-allocate (faster than append)
for(i in 1:10000) {
sample_vals <- rnorm(30, mean=50, sd=10)
results[i] <- mean(sample_vals)
}
mean(results); var(results)
MODULE 5 — Statistical Inference ★★★
5.1 Hypothesis Tests for Proportions
PAST PAPER: Nov 2019 paper asked: test if proportion of negative Sensex months < 50%. This
exact pattern repeats.
# Exact binomial test
[Link](x=40, n=100, p=0.5, # x=successes, n=trials, p=H0
value
alternative="less", # "less","greater","[Link]"
[Link]=0.95)
# Read: p-value, 95% CI, estimate
# Proportions test (large sample, approximate)
[Link](x=40, n=100, p=0.5,
alternative="less",
correct=FALSE) # correct=TRUE for Yates continuity correction
# Two-sample proportion test
[Link](x=c(x1, x2), n=c(n1, n2))
5.2 t-Tests
# One-sample t-test: H0: mu = mu0
[Link](x, mu=0) # Test if mean = 0
[Link](x, mu=100, alternative='greater') # H1: mu > 100
# Two-sample t-test: H0: mu1 = mu2
[Link](x, y, [Link]=TRUE) # Assumes equal variances
[Link](x, y, [Link]=FALSE) # Welch test (default)
[Link](x, y, paired=TRUE) # Paired t-test
# Extract components programmatically
result <- [Link](x, mu=0)
result$statistic # t-statistic
result$[Link] # p-value
result$[Link] # 95% confidence interval
result$estimate # sample mean
5.3 Chi-Squared Tests
# Chi-squared goodness of fit
observed <- c(25, 30, 45) # Observed counts
expected_probs <- c(1/3, 1/3, 1/3) # H0 probabilities
[Link](observed, p=expected_probs)
# Chi-squared test of independence (from contingency table)
tbl <- table(df$Var1, df$Var2)
[Link](tbl)
[Link](tbl)$expected # Check expected counts (need >= 5)
# F-test for equality of variances
[Link](x, y) # H0: var(x) = var(y)
[Link](x, y)$[Link]
5.4 Non-Parametric Tests
# Wilcoxon signed-rank test (non-param equivalent of one-sample t)
[Link](x, mu=0)
# Wilcoxon rank-sum test (non-param equivalent of two-sample t)
[Link](x, y)
# Kolmogorov-Smirnov test (tests if data follows a distribution)
[Link](x, 'pnorm', mean=mean(x), sd=sd(x)) # Test normality
[Link](x, y) # Two-sample KS test
5.5 Maximum Likelihood Estimation (MLE) with optim()
# Poisson MLE example
data <- c(2, 3, 1, 4, 2, 5, 3, 2, 1, 4)
neg_log_lik <- function(lambda, data) {
-sum(dpois(data, lambda, log=TRUE))
}
result <- optim(par=2, fn=neg_log_lik, data=data, method='Brent',
lower=0.001, upper=100)
result$par # MLE of lambda
# Note: For Poisson, MLE = mean(data) analytically
# Use optim() only when no closed-form exists
# Normal MLE (both mu and sigma)
neg_ll_normal <- function(params, data) {
mu <- params[1]
sigma <- params[2]
if(sigma <= 0) return(Inf)
-sum(dnorm(data, mean=mu, sd=sigma, log=TRUE))
}
result2 <- optim(par=c(mean(x), sd(x)), fn=neg_ll_normal, data=x)
result2$par # c(mu_hat, sigma_hat)
5.6 Confidence Intervals
# From [Link] output
[Link](x)$[Link] # 95% CI for mean
[Link](x, [Link]=0.99)$[Link] # 99% CI
# Manual 95% CI for mean (large sample)
n <- length(x)
se <- sd(x) / sqrt(n)
ci <- c(mean(x) - 1.96*se, mean(x) + 1.96*se)
# Manual CI using t critical value (small sample)
t_crit <- qt(0.975, df=n-1) # 97.5th percentile of t(n-1)
ci <- c(mean(x) - t_crit*se, mean(x) + t_crit*se)
# CI for proportion
[Link](x=40, n=100)$[Link]
MODULE 6 — Linear Regression ★★★ (30% of paper)
6.1 Fitting & Reading Output
MOST IMPORTANT: Always know how to read summary(lm_model) fully. Every component gets
asked.
# Fit model
model <- lm(y ~ x, data=df) # Simple: y = b0 + b1*x
model <- lm(y ~ x1 + x2, data=df) # Multiple regression
# Full output — MEMORISE what each bit means
summary(model)
# Output breakdown:
# Residuals: min/Q1/median/Q3/max of residuals
# Coefficients:
# (Intercept) Est [Link] t-value Pr(>|t|) ***
# x Est [Link] t-value Pr(>|t|) ***
# Residual std error = sigma_hat (on df=n-p-1)
# R-squared: proportion of variance explained
# Adjusted R-sq: penalises for extra predictors
# F-statistic: overall model significance
# Individual extractors
coef(model) # Coefficients vector
coef(model)[1] # Intercept only
coef(model)[2] # Slope (first predictor)
confint(model) # 95% CI for each coefficient
confint(model, level=0.99)
residuals(model) # Raw residuals (y - y_hat)
fitted(model) # Fitted values y_hat
sigma(model) # Residual standard error
AIC(model) # Akaike information criterion
BIC(model) # Bayesian information criterion
logLik(model) # Log-likelihood
6.2 Multiple Regression & Model Building
# Full model
full_model <- lm(y ~ x1 + x2 + x3 + x4, data=df)
# Add interactions
model_int <- lm(y ~ x1 * x2, data=df) # x1 + x2 + x1:x2
model_int <- lm(y ~ x1 + x2 + x1:x2, data=df) # Same
# Categorical predictors (factors)
df$Sector <- [Link](df$Sector) # Convert to factor first
model_cat <- lm(y ~ x + Sector, data=df) # Sector creates dummy vars
# R auto-creates k-1 dummies; baseline = first alphabetic level
# Polynomial regression
model_poly <- lm(y ~ x + I(x^2), data=df) # I() protects ^ from formula
rules
# Log transformation
model_log <- lm(log(y) ~ x, data=df)
model_log <- lm(y ~ log(x), data=df)
# Stepwise model selection (backward by default)
step(full_model) # AIC-based selection
step(full_model, direction='backward')
step(null_model, scope=list(upper=full_model), direction='forward')
6.3 Prediction
# Point prediction
new_data <- [Link](x1=25, x2=3.5) # Must use SAME column names as
training
predict(model, newdata=new_data)
# Confidence interval for MEAN response (narrower)
predict(model, newdata=new_data, interval='confidence', level=0.95)
# Output: fit, lwr, upr
# Prediction interval for NEW individual observation (wider)
predict(model, newdata=new_data, interval='prediction', level=0.95)
# Key difference:
# Confidence interval = interval for E[Y|x]
# Prediction interval = interval for individual Y|x (always wider)
6.4 Residual Diagnostics ★★★
EXAM PATTERN: Every regression question ends with: 'Plot residuals and comment on model
adequacy.'
# 4-panel diagnostic plot (standard in every exam)
par(mfrow=c(2,2))
plot(model)
par(mfrow=c(1,1))
# Panel 1 — Residuals vs Fitted:
# Good: random scatter around y=0
# Bad: curved pattern (nonlinearity), funnel shape (heteroscedasticity)
# Panel 2 — Normal Q-Q of residuals:
# Good: points on diagonal line
# Bad: S-shape (skewed), heavy tails (thick tails)
# Panel 3 — Scale-Location (sqrt(|residuals|) vs fitted):
# Good: horizontal line, constant spread
# Bad: upward trend (variance increases with fitted values)
# Panel 4 — Cook's Distance / Leverage:
# Points beyond dashed line = influential observations
# Individual residual plots
plot(fitted(model), residuals(model), main='Residuals vs Fitted')
abline(h=0, col='red', lty=2)
qqnorm(residuals(model)); qqline(residuals(model), col='red')
# Standardised residuals
rstandard(model) # Standardised
rstudent(model) # Studentised (deletes each obs)
# Leverage and influence
hatvalues(model) # Hat matrix diagonal (leverage)
[Link](model) # Cook's D: overall influence
MODULE 7 — Generalised Linear Models (GLM) ★★
7.1 Fitting GLMs
# General syntax
model <- glm(y ~ x1 + x2, family=FAMILY, data=df)
# ── LOGISTIC REGRESSION (binary outcome y = 0/1) ──
log_model <- glm(y ~ x1 + x2, family=binomial, data=df)
log_model <- glm(y ~ x1 + x2, family=binomial(link='logit'), data=df)
# ── POISSON REGRESSION (count outcome) ──
pois_model <- glm(y ~ x1 + x2, family=poisson, data=df)
pois_model <- glm(y ~ x1 + x2, family=poisson(link='log'), data=df)
# ── GAMMA REGRESSION (positive continuous, right-skewed) ──
gam_model <- glm(y ~ x1 + x2, family=Gamma(link='log'), data=df)
# ── GAUSSIAN = ordinary linear regression ──
glm(y ~ x, family=gaussian) # Same as lm()
# Summary
summary(log_model)
# Output includes: Coefficients (on link scale!), z-values (not t!),
# Null deviance, Residual deviance, AIC
7.2 Interpreting GLM Output
# LOGISTIC: Coefficients are LOG-ODDS
coef(log_model) # Log-odds scale
exp(coef(log_model)) # ODDS RATIOS (more interpretable)
exp(confint(log_model)) # CI for odds ratios
# POISSON: Coefficients are LOG of rate
exp(coef(pois_model)) # Rate ratios / incidence rate ratios
# Deviance (lower = better fit)
deviance(model) # Residual deviance
model$[Link] # Null model deviance (intercept only)
model$deviance # Same as deviance(model)
model$[Link] # Degrees of freedom
# AIC comparison between models
AIC(model1); AIC(model2) # Lower AIC = better
# Deviance test (like F-test for GLMs)
anova(model_small, model_large, test='Chisq')
# Predict on original scale (not link scale)
predict(log_model, type='response') # Probabilities
predict(pois_model, type='response') # Expected counts
predict(log_model, newdata=df_new, type='response')
7.3 GLM Residuals
# Pearson residuals
residuals(model, type='pearson')
# Deviance residuals (default)
residuals(model, type='deviance')
residuals(model) # Same as deviance residuals
# Plot residuals
plot(fitted(model), residuals(model, type='pearson'),
main='Pearson Residuals vs Fitted',
xlab='Fitted Values', ylab='Pearson Residuals')
abline(h=0, col='red', lty=2)
MODULE 8 — Bayesian Statistics in R ★
8.1 Conjugate Prior Updates
# ── BETA-BINOMIAL (most common) ──
# Prior: theta ~ Beta(alpha0, beta0)
# Data: x successes in n trials
# Posterior: theta ~ Beta(alpha0 + x, beta0 + n - x)
alpha0 <- 2; beta0 <- 5 # Prior parameters
x <- 8; n <- 20 # Observed: 8 successes in 20 trials
alpha_post <- alpha0 + x
beta_post <- beta0 + (n - x)
# Posterior mean (Bayes estimator under squared error loss)
post_mean <- alpha_post / (alpha_post + beta_post)
# Posterior mode
post_mode <- (alpha_post - 1) / (alpha_post + beta_post - 2)
# 95% Credible interval
qbeta(c(0.025, 0.975), alpha_post, beta_post)
# ── GAMMA-POISSON ──
# Prior: lambda ~ Gamma(alpha0, beta0)
# Data: total count s = sum(x_i) over n observations
# Posterior: lambda ~ Gamma(alpha0 + s, beta0 + n)
alpha0 <- 3; beta0 <- 1
s <- sum(data); n <- length(data)
alpha_post <- alpha0 + s
beta_post <- beta0 + n
post_mean <- alpha_post / beta_post
qgamma(c(0.025, 0.975), shape=alpha_post, rate=beta_post)
# ── NORMAL-NORMAL ──
# Prior: mu ~ N(mu0, sigma0^2)
# Data: x_bar from n obs, known sigma^2
# Posterior mean:
post_mean <- (mu0/sigma0^2 + n*x_bar/sigma^2) / (1/sigma0^2 + n/sigma^2)
8.2 Credibility Theory
# Credibility premium formula
# P_cred = Z * x_bar + (1 - Z) * mu_prior
# where Z = n / (n + k), k = sigma^2_process / sigma^2_between
n <- 5 # Number of observations/years
k <- 4 # Credibility parameter
Z <- n / (n + k) # Credibility factor
x_bar <- 120 # Observed mean
mu_prior <- 100 # Prior/collective mean
P_cred <- Z * x_bar + (1 - Z) * mu_prior
P_cred
MODULE 9 — Control Flow, Functions & Loops
FOR PYTHON PROGRAMMERS: Syntax is different but concepts are identical. Main gotcha: R
indexing starts at 1, not 0.
9.1 Conditionals & Loops
# if-else (same concept as Python, different syntax)
if (x > 0) {
print("positive")
} else if (x == 0) {
print("zero")
} else {
print("negative")
}
# ifelse() — vectorised (like [Link] in Python)
result <- ifelse(x > 0, "Gain", "Loss") # Works on entire vector
# for loop
for (i in 1:10) {
cat(i, '\n') # cat() is like print() for simple output
}
# Loop over vector elements
for (val in c(2, 5, 8, 11)) {
cat('Square:', val^2, '\n')
}
# while loop
i <- 1
while (i <= 5) {
print(i)
i <- i + 1
}
9.2 Writing Functions
# Basic function (like def in Python)
my_mean <- function(x) {
return(sum(x) / length(x))
}
my_mean(c(1, 2, 3, 4, 5)) # Call it
# Multiple arguments with defaults
conf_interval <- function(x, level=0.95) {
n <- length(x)
se <- sd(x) / sqrt(n)
alph <- 1 - level
t_c <- qt(1 - alph/2, df=n-1)
c(lower = mean(x) - t_c*se,
upper = mean(x) + t_c*se)
}
conf_interval(data) # Uses default 95%
conf_interval(data, 0.99) # 99% CI
9.3 Apply Family (Vectorised Operations)
# apply() — over rows (MARGIN=1) or columns (MARGIN=2) of matrix/df
apply(df_numeric, 2, mean) # Column means (like [Link]() in pandas)
apply(df_numeric, 1, sum) # Row sums
apply(df_numeric, 2, function(x) round(sd(x), 3))
# sapply() — apply function to each element of a vector, returns vector
sapply(1:10, function(x) x^2) # c(1,4,9,16,...,100)
sapply(df, class) # Data type of each column
# lapply() — like sapply but returns list
lapply(df, mean)
# tapply() — apply by group (like groupby in pandas)
tapply(df$Salary, df$Department, mean) # Mean salary per department
tapply(df$Returns, df$Sector, sd) # SD of returns per sector
MODULE 10 — Quick Reference & Exam Patterns
10.1 Recurring Past Paper Patterns
PATTERN A — Nov 2019: Load CSV → basic stats → proportion test → contingency table →
chi-sq test → scree plot from PCA
# Pattern A template
df <- [Link]('Indices_Returns.csv')
# Step 1: Proportion
n_neg <- sum(df$Sensex < 0)
n <- nrow(df)
prop <- n_neg / n
# Step 2: Test proportion
[Link](n_neg, n, p=0.5, alternative='less', [Link]=0.95)
[Link](n_neg, n, p=0.5, alternative='less', [Link]=0.99)
# Step 3: Classify and make contingency table
df$FI_cat <- ifelse(df$FI > 0, 'Positive', 'Negative')
df$IT_cat <- ifelse(df$IT > 0, 'Positive', 'Negative')
tbl <- table(df$FI_cat, df$IT_cat)
[Link](tbl)
# Step 4: PCA
pca <- prcomp(df[ , numeric_cols], scale.=TRUE)
summary(pca)
round(cor(pca$x), 3)
plot(pca, type='l', main='Scree Plot')
PATTERN B — Regression paper: Load data → EDA → fit lm → summary → residual plots →
predict with interval → compare models by AIC
# Pattern B template
df <- [Link]('insurance_data.csv')
str(df); summary(df)
# EDA
par(mfrow=c(1,2))
hist(df$ClaimAmt, freq=FALSE, main='Claim Amounts', xlab='Amount')
qqnorm(df$ClaimAmt); qqline(df$ClaimAmt, col='red')
# Fit models
m1 <- lm(ClaimAmt ~ Age, data=df)
m2 <- lm(ClaimAmt ~ Age + Sex, data=df)
m3 <- lm(ClaimAmt ~ Age + Sex + Age:Sex, data=df)
# Compare
AIC(m1, m2, m3) # Lower is better
# Best model summary + diagnostics
summary(m2)
par(mfrow=c(2,2)); plot(m2); par(mfrow=c(1,1))
# Prediction
new <- [Link](Age=45, Sex='M')
predict(m2, newdata=new, interval='prediction')
PATTERN C — GLM paper: Binary/count outcome → fit glm → interpret coefficients → deviance
→ predict probabilities
# Pattern C template (logistic)
df <- [Link]('claim_occurrence.csv')
# Fit
model <- glm(HasClaim ~ Age + Premium + Region,
family=binomial, data=df)
summary(model)
# Coefficients: log-odds scale
# exp() gives odds ratios
exp(coef(model))
exp(confint(model))
# Deviance test
null_mod <- glm(HasClaim ~ 1, family=binomial, data=df)
anova(null_mod, model, test='Chisq')
# Predict probabilities
pred_probs <- predict(model, type='response')
hist(pred_probs, main='Predicted Probabilities')
10.2 Python → R Translation
What you want Python / pandas R equivalent
Load CSV pd.read_csv('[Link]') [Link]('[Link]')
Shape [Link] dim(df)
Column names [Link] names(df)
First rows [Link]() head(df)
Data types [Link] str(df)
Select column df['col'] or [Link] df$col
Filter rows df[df.x > 2] df[df$x > 2, ]
Mean [Link]() mean(df$col)
Std dev [Link]() sd(df$col)
Group mean [Link]('g') tapply(df$x, df$g, mean)
['x'].mean()
Print print(x) print(x) or cat(x)
Range 1–10 range(1,11) 1:10
List comprehension [f(x) for x in xs] sapply(xs, f)
Null check [Link](x) [Link](x)
Drop NAs [Link]() [Link](df)
String format f'val={v:.3f}' sprintf('val=%.3f', v)
10.3 Key Functions Master List
# ── DATA ────────────────────────────────────────────────────
[Link]() [Link]() load() str() head() summary() dim()
names() table() [Link]() [Link]() [Link]() [Link]()
# ── STATS ───────────────────────────────────────────────────
mean() median() var() sd() quantile() IQR() cor() [Link]()
sum() cumsum() prod() length() which() sort() order()
# ── DISTRIBUTIONS (d/p/q/r) ─────────────────────────────────
dnorm pnorm qnorm rnorm dpois ppois qpois rpois
dbinom pbinom qbinom rbinom dexp pexp qexp rexp
dgamma pgamma dchisq pchisq qchisq dt pt qt
# ── INFERENCE ───────────────────────────────────────────────
[Link]() [Link]() [Link]() [Link]() [Link]()
[Link]() [Link]() [Link]() optim() nlm()
# ── REGRESSION ──────────────────────────────────────────────
lm() glm() summary() coef() confint() predict() residuals()
fitted() AIC() BIC() anova() step() sigma() hatvalues()
rstandard() rstudent() [Link]() deviance()
# ── PLOTTING ────────────────────────────────────────────────
hist() plot() boxplot() barplot() qqnorm() qqline()
lines() points() abline() curve() density() par(mfrow=)
# ── SIMULATION ──────────────────────────────────────────────
[Link]() replicate() sample() runif() rbinom()
# ── UTILITY ─────────────────────────────────────────────────
round() ceiling() floor() abs() sqrt() log() exp()
paste() paste0() sprintf() cat() print() c() seq()
numeric() integer() character() logical() matrix() cbind() rbind()
apply() sapply() lapply() tapply() replicate() Reduce()
10.4 Last-Minute Exam Checklist
• Load data FIRST with [Link]() — do this before writing any other code
• Always [Link](42) before any simulation question
• For regression, always run summary(model) and read ALL parts
• Residual plots: run par(mfrow=c(2,2)); plot(model) — comment on each panel
• GLM coefficients are on the link scale — use exp(coef(model)) for logistic
• For proportion tests: use [Link]() (exact) — specify alternative='less' or 'greater'
• t-test and ALL tests: state H0, H1, p-value, conclusion in plain English
• Confidence interval ≠ Prediction interval — know which one is wider and why
• Bayesian: state prior family → posterior family → compute posterior mean as estimate
• Model comparison: lower AIC = better; lower deviance = better fit