B . C O M ( H ) · S E M V I · D S C - 6 .
1 B U S I N E S S A N A LY T I C S
The R Guidebook
for the Business Analytics Practical Exam
Every code pattern from the SOL, colour-coded.
Built around what actually shows up on the practical.
3 4 15 1
Units covered Question Viva Cheatsheet
(3, 4, 5) patterns questions at the back
Built for Saksham · April 2026
Read order: Setup → Patterns A–D → Cheatsheet
How to read this
This is built around question patterns, not theory. Last year's paper had four R-type questions, and the same
kinds will come back. Skim Setup, then study the four patterns, then the cheatsheet at the end.
Colour key for code
function, if keywords
mean, lm, c built-in functions
"text" strings
42 numbers
# comment comments — green italic, ignored by R
<- + == operators
Contents
01 Setup & first commands 2 02 The 4 things every script needs 3
03 Pattern A — Data frame + descriptive stats (Q4 4 04 Pattern B — Linear regression (Q2 type) 6
type)
05 Pattern C — Multiple regression + diagnostics 8 06 Pattern D — Wordcloud + sentiment (Q6 type) 10
(Q5 type)
07 Quick reference — data structures 12 08 Visualisations gallery 14
09 Top viva questions (15) 16 10 Common errors & fixes 17
11 Final cheatsheet (1 page) 18
SUBMISSION RITUAL
Folder named your full exam roll number. Inside, sub-folders Q.1 , Q.2 , etc. Each script saved as
Qn_subpart_last4digits.R . Always run summary(model) after lm() . Always write your
interpretation as # comments — last year's paper paid explicitly for that.
01 · Setup & first commands
Open RStudio. You see four panes.
Pane What it does
Source (top-left) Where you write a .R script. This is what you save and submit.
Console (bottom-left) Where R actually runs the code. Errors and output appear here.
Environment (top-right) Every variable you've created. Useful when you wonder "did df load?"
Files / Plots / Packages / File browser, plots appear here, list of installed packages, help.
Help (bottom-right)
BA Practical · R Guidebook 2 / 18
Two shortcuts you'll use 100 times
Shortcut What it does
Ctrl + Enter Run the current line (or selected lines)
Alt + − (minus) Inserts <- for assignment
The four lines that go at the top of every script
getwd() # which folder am I in?
setwd("C:/Users/you/Desktop/Q2") # always FORWARD slashes
ls() # list every variable in memory
rm(list = ls()) # wipe the slate clean
Packages: install once, library every time
[Link]("readxl") # ONCE per machine — downloads it
library(readxl) # EVERY session — makes it usable
VIVA FAVOURITE
Q. Difference between a package and a library? A package is a bundle of functions and data (e.g. readxl ). A
library is the directory on your computer where installed packages live. install once, library every time.
Loading data — three patterns
[Link]("[Link]") # CSV (built-in)
read_excel("[Link]", sheet = 1) # Excel (needs readxl)
data("mtcars") # built-in dataset
WATCH OUT
On Windows, file paths use forward slashes / in R, NOT backslashes. "C:\\Users\\..." works but is ugly.
Forward slashes always work: "C:/Users/you/[Link]" .
02 · The 4 things every R script needs
Every answer you write in the practical follows the same skeleton. Memorise it.
rm(list = ls()) # 1. clean slate
library(readxl) # 2. load packages you need
df <- [Link]("[Link]") # 3. load the data
str(df); summary(df) # always inspect immediately
# 4. now do the actual analysis...
# ... with COMMENTS explaining what you found, e.g.
# Mean attrition is 14.5%, median 13.2 — close, so distribution
# is roughly symmetric.
BA Practical · R Guidebook 3 / 18
1. Wipe environment 2. Load packages
rm(list = ls()) stops left-over variables from the previous library(readxl) for Excel. library(ggplot2) for plots.
question polluting this one. library(tm) for text. Don't mix this with
[Link]() — that's a one-time install.
3. Load and inspect data 4. Comment your interpretation
Always run str(df) and summary(df) on a freshly-loaded Use # liberally. The marker pays for interpretation, not just
data frame. It tells you column types, ranges, NAs. code. Q2 last year said "interpret the results as comments in
the R-Script."
MARKS -EARNING HABIT
After every important output ( summary(model) , cor(x,y) , vif(model) ) write 1–2 lines starting with #
saying what the number means. Examples:
# Slope = -3.17, p < 0.001 → heavier cars have lower mpg, highly significant.
# R-squared = 0.84 → the model explains 84% of variance in mpg.
03 · Pattern A Data frame + descriptive stats
This is the Q4 of last year's paper. Build a data frame with specific column types, run mean/SD/quartiles, draw three
plots, then covariance / correlation / R².
Step 1 — build the data frame
# ---- Q4 PATTERN: build a data frame with mixed types ----
rm(list = ls())
# 1. Each column is a vector. The CLASS of each must match
# what the question asks for.
empid <- [Link](c("E01","E02","E03","E04","E05"))
dept <- factor(c("HR","IT","Finance","Marketing","Operations"))
emname <- [Link](c("Aman","Beena","Chetan","Divya","Eshan"))
ey <- [Link](c(2L, 5L, 7L, 3L, 10L)) # integer, with L
wh <- c(40, 45, 50, 38, 42) # numeric
ms <- c(80000, 95000, 120000, 70000, 150000) # numeric
gs <- c(50000, 70000, 90000, 45000, 110000) # numeric
txs <- 0.10 * gs # 10% of gross
ns <- gs - txs # net = gross - tax
# 2. Glue columns into a data frame
db <- [Link](empid, dept, emname, ey, wh, ms, gs, txs, ns,
stringsAsFactors = FALSE)
str(db) # confirm classes — character / factor / integer / numeric
WHY EACH AS.X() MAT TERS
The question asks for specific classes. [Link]() forces text. factor() tags it as categorical.
[Link]() + the L suffix forces integer. Plain numbers are numeric (double). If you skip these, R may guess
wrong and you lose marks.
BA Practical · R Guidebook 4 / 18
Step 2 — descriptive stats
# Mean, SD, quartiles for every numeric column
numeric_cols <- db[, sapply(db, [Link])]
sapply(numeric_cols, mean) # column-wise means
sapply(numeric_cols, sd) # column-wise SDs
sapply(numeric_cols, quantile) # column-wise quartiles
# Or all in one line (the lazy way that often works)
summary(numeric_cols)
Statistic R function What it measures
Mean mean(x, [Link]=TRUE) Arithmetic average. Sensitive to outliers.
Median median(x, [Link]=TRUE) Middle value. Robust to outliers.
Mode see custom function below Most-frequent value. R has none built-in.
SD sd(x) Spread, in same units as data
Variance var(x) Spread, in squared units
IQR IQR(x) Q3 − Q1, robust spread
Quartiles quantile(x) 0%, 25%, 50%, 75%, 100%
Step 3 — plots (use base R, simpler than ggplot2 for Q4)
# Histogram of net salary
hist(db$ns,
col = "steelblue",
main = "Histogram of Net Salary",
xlab = "Net Salary (Rs.)",
breaks = 5)
# Box plot of experience years (notice: PROPERLY TITLED, as asked)
boxplot(db$ey,
col = "lightgreen",
main = "Box Plot of Experience (Years)",
ylab = "Experience (Years)")
# Scatter plot of weekly hours vs monthly sales
plot(db$wh, db$ms,
pch = 19, col = "blue",
main = "Weekly Hours vs Monthly Sales",
xlab = "Weekly Hours", ylab = "Monthly Sales (Rs.)")
BA Practical · R Guidebook 5 / 18
Step 4 — relationships between variables
# Covariance & correlation: experience vs net salary
cov_val <- cov(db$ey, db$ns)
cor_val <- cor(db$ey, db$ns)
cat("Covariance:", cov_val, "\n")
cat("Correlation:", cor_val, "\n")
# Comment for marks:
# Positive covariance → experience and net salary move together.
# Correlation ~ 0.95 → strong positive linear relationship.
# Coefficient of determination: monthly sales vs net salary
r_squared <- cor(db$ms, db$ns) ^ 2 # R^2 = r^2 for SLR
cat("R-squared:", r_squared, "\n")
# Comment for marks:
# R^2 ~ [Link] means YY% of variation in net salary is explained
# by monthly sales.
LIKELY VIVA
Q. Why correlation instead of covariance? Covariance has units (e.g., rupee-years), so values can't be compared
across pairs. Correlation is dimensionless and bounded between −1 and +1, so it's directly comparable.
Q. What does R² = 0.81 mean? 81% of the variation in Y is explained by X.
THE MODE TRAP
R has mean() and median() built in but no built-in mode() for the statistical mode (the function called
mode() exists but returns the storage type — wrong thing). You define it yourself:
get_mode <- function(v) {
uniqv <- unique(v)
uniqv[[Link](tabulate(match(v, uniqv)))]
}
get_mode(c(1, 2, 2, 3, 3, 3)) # returns 3
04 · Pattern B Simple linear regression
This is Q2 of last year's paper. Fit y ~ x with lm() , read the summary, plot the line, predict at a new value, comment
on what the numbers mean.
BA Practical · R Guidebook 6 / 18
The five-step script
# ---- Q2 PATTERN: simple linear regression ----
rm(list = ls())
# 1. Build / load the data
df <- [Link](
weekly_hours = c(230.1, 44.5, 17.2, 151.5, 180.8, 8.7),
avg_attrition = c(22.1, 10.4, 12.0, 16.5, 17.9, 7.2)
)
# 2. Visualise: scatter plot to see if a line is even sensible
plot(df$weekly_hours, df$avg_attrition,
pch = 19, col = "blue",
main = "Weekly Hours vs Attrition",
xlab = "Weekly hours",
ylab = "Average attrition (%)")
# 3. Fit the model. Read aloud: "attrition modelled on hours"
model <- lm(avg_attrition ~ weekly_hours, data = df)
summary(model) # the SINGLE most important command
# 4. Add the regression line on top of the scatter plot
abline(model, col = "red", lwd = 2)
# 5. Predict at a new value
new_x <- [Link](weekly_hours = 50)
y_hat <- predict(model, newdata = new_x)
cat("Predicted attrition at 50 hrs:", y_hat, "\n")
How to read summary(model) ← the marks live here
OUTPUT
Call:
lm(formula = avg_attrition ~ weekly_hours, data = df)
Residuals:
Min 1Q Median 3Q Max
-3.512 -1.034 0.187 1.230 2.917
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 7.83210 0.91234 8.585 4.5e-08 ***
weekly_hours 0.06823 0.01012 6.742 1.8e-07 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 1.93 on 23 degrees of freedom
Multiple R-squared: 0.6024, Adjusted R-squared: 0.5892
F-statistic: 45.46 on 1 and 23 DF, p-value: 1.7e-07
BA Practical · R Guidebook 7 / 18
What you see What it means
(Intercept) 7.832 Predicted Y when X = 0. Often not meaningful in real terms.
weekly_hours 0.068 Slope. For each unit increase in X, Y increases by 0.068.
Pr(>|t|) 1.8e-07 *** p-value for "is the slope = 0?". p < 0.05 → significant. Three stars = p < 0.001 (very
strong).
Multiple R-squared 0.6024 Model explains 60.24% of the variation in Y.
F-statistic … p = 1.7e-07 Overall test of "does any predictor matter?". p < 0.05 → yes.
Confidence vs Prediction intervals
# Confidence interval = uncertainty about the MEAN response
predict(model, newdata = new_x,
interval = "confidence", level = 0.95)
# Prediction interval = uncertainty about an INDIVIDUAL observation
# (always wider — adds individual residual noise)
predict(model, newdata = new_x,
interval = "prediction", level = 0.95)
Interval What it answers Width
Confidence What's the average Y at this X? Narrower
Prediction What's an individual Y at this X? Wider
VIVA FAVOURITE
Q. Why is the prediction interval wider? Because it includes the residual variance of an individual observation
around the mean line, not just the uncertainty of the mean line itself.
Q. How do you interpret a slope coefficient? "For each one-unit increase in the predictor, the response is expected
to change by the value of the slope, holding all other predictors constant."
COMMENTS TO PASTE UNDER THE REGRESSION IN YOUR SCRIPT
# Slope = 0.068 (p < 0.001) -> each extra weekly hour is
# associated with 0.068 percentage-point higher attrition.
# R-squared = 0.60 -> 60% of variation in attrition explained.
# F-test p < 0.001 -> model is overall significant.
# CONCLUSION: weekly_hours is the variable to focus on.
05 · Pattern C Multiple regression + diagnostics
This is Q5 of last year's paper. Same as Pattern B but with several predictors, plus the two big diagnostic checks.
BA Practical · R Guidebook 8 / 18
Step 1–2 — fit the model
# ---- Q5 PATTERN: multiple regression + diagnostics ----
rm(list = ls())
# 1. Load the cleaned CSV (after Excel cleaning)
houses <- [Link]("housing_clean.csv")
str(houses); summary(houses)
# 2. Fit MLR — the dot . means "all other columns as predictors"
model <- lm(median_house_value ~ ., data = houses)
summary(model)
Step 3–5 — the two diagnostics that earn marks
# 3. HETEROSCEDASTICITY check — visual + formal
plot(model$[Link], model$residuals,
pch = 19,
main = "Residuals vs Fitted",
xlab = "Fitted", ylab = "Residuals")
abline(h = 0, col = "red")
library(lmtest)
bptest(model) # Breusch-Pagan test
# If p < 0.05 → heteroscedasticity present. Fix: log-transform y.
# 4. MULTICOLLINEARITY check
library(car)
vif(model)
# Rule of thumb: VIF > 10 = severe multicollinearity. Fix: drop
# one of the correlated predictors.
# 5. Four standard diagnostic plots in one go
par(mfrow = c(2, 2))
plot(model) # produces 4 diagnostic plots
par(mfrow = c(1, 1)) # reset layout
Problem What it is Detect with Fix with
Heteroscedasticity Variance of residuals is NOT constant Funnel-shape in residuals vs log(y) transform, or
— they fan out as the predictor grows. fitted plot. Or formal: weighted least squares,
Makes p-values unreliable. bptest() , p < 0.05. or robust SEs.
Multicollinearity Predictors highly correlated with each vif(model) . Any value > 10 Drop one of the
other. Coefficients become unstable, = severe. correlated predictors. Or
signs may flip. combine them.
What if a check fails?
# If heteroscedasticity is found, log-transform Y
model_log <- lm(log(median_house_value) ~ ., data = houses)
summary(model_log)
# If a predictor has VIF > 10, drop it and refit
model_fixed <- lm(median_house_value ~ . - total_bedrooms,
data = houses)
BA Practical · R Guidebook 9 / 18
VIVA FAVOURITES
Q. What's a VIF? Variance Inflation Factor — measures how much the variance of a coefficient is inflated due to its
correlation with other predictors. VIF > 10 = severe.
Q. Consequence of heteroscedasticity? OLS estimates stay unbiased, but standard errors are wrong, so t-tests and
CIs become unreliable.
Q. Five MLR assumptions? Linearity, independence, homoscedasticity, normality of residuals, no multicollinearity.
COMMENTS TO PASTE UNDER YOUR DIAGNOSTICS
# Residuals vs Fitted shows a slight funnel -> mild heteroscedasticity.
# bptest p = 0.01 -> reject homoscedasticity. Fix: use log(price).
#
# VIFs: total_rooms = 14.2, total_bedrooms = 12.8 (both > 10) ->
# severe multicollinearity. Dropping total_bedrooms reduces all
# VIFs below 5.
#
# After fixes, R-squared = 0.71, F-test p < 2e-16 -> useful for prediction.
06 · Pattern D Wordcloud + sentiment
This is Q6 of last year's paper. The SOL is thin on this — the script below covers everything the question asks for.
Memorise the order.
The pipeline (memorise this order — it's always the same)
1 Load text into a single string.
2 Build a corpus with Corpus(VectorSource(text)).
3 Clean it: lower → remove numbers → punctuation → stopwords → whitespace.
4 Term-Document Matrix → sort frequencies.
5 Wordcloud with [Link] = 2.
6 Sentiment: get_nrc_sentiment() → colSums() → barplot().
7 Comment what the cloud and the bar chart say about the text.
BA Practical · R Guidebook 10 / 18
Setup
# ---- Q6 PATTERN: wordcloud + sentiment ----
rm(list = ls())
# 1. Install once, library every time
# [Link](c("tm", "wordcloud", "RColorBrewer", "syuzhet"))
library(tm)
library(wordcloud)
library(RColorBrewer)
library(syuzhet)
# 2. Get your text into a single string
speech <- "Hon'ble Members, ...full speech text from question..."
The 5 cleanups (always in this order)
# 3. Build a corpus and clean it (5 cleanups, ALWAYS in this order)
corpus <- Corpus(VectorSource(speech))
corpus <- tm_map(corpus, content_transformer(tolower)) # lowercase
corpus <- tm_map(corpus, removeNumbers) # remove digits
corpus <- tm_map(corpus, removePunctuation) # punctuation
corpus <- tm_map(corpus, removeWords, stopwords("en")) # the, is, of...
corpus <- tm_map(corpus, stripWhitespace) # collapse spaces
WHY TH IS ORDER MAT TERS
Lowercasing first means "The" and "the" become the same word. Removing numbers before punctuation means
"2024" gets stripped before commas confuse the parser. Stopwords go AFTER the basic cleaning so the matcher can
find "is", "the", etc. Stripping whitespace is the last step to collapse the spaces all the previous removals left behind.
The wordcloud
# 4. Term-Document Matrix → word frequency table
tdm <- TermDocumentMatrix(corpus)
m <- [Link](tdm)
v <- sort(rowSums(m), decreasing = TRUE)
freq_df <- [Link](word = names(v), freq = v)
# 5. Wordcloud (question asks min frequency > 2)
[Link](1234)
wordcloud(words = freq_df$word, freq = freq_df$freq,
[Link] = 2, [Link] = 200,
[Link] = FALSE,
colors = [Link](8, "Dark2"))
BA Practical · R Guidebook 11 / 18
The sentiment bar plot
# 6. Sentiment bar plot using NRC lexicon
sentiment <- get_nrc_sentiment(speech)
totals <- colSums(sentiment)
barplot(totals,
las = 2, # vertical x-axis labels
col = rainbow(10),
main = "Sentiment scores for the speech",
ylab = "Count")
THE 8 EMOTIONS + 2 POL ARITIES
get_nrc_sentiment() returns 10 columns: anger, anticipation, disgust, fear, joy, sadness, surprise, trust (the
8 emotions), plus negative, positive (the 2 polarities). They're all just word counts.
Interpretation comments (paste into your script)
# Interpretation comments (PASTE these into your script — Q6 explicitly
# asks for interpretation):
# WORDCLOUD: the largest words are "government", "country", "crore",
# "scheme" — confirming the speech focuses on policy, public welfare,
# and economic schemes.
# SENTIMENT: positive scores meaningfully exceed negative; "trust" and
# "anticipation" dominate. This is consistent with an aspirational
# political address aimed at projecting confidence in future plans.
LIKELY VIVA
Q. Why do you remove stopwords? Words like "the", "is", "of", "and" appear very frequently but carry no topical
meaning. Removing them lets the wordcloud highlight content words.
Q. Two approaches to sentiment analysis? Lexicon-based (match words against a pre-built dictionary like NRC —
what we use). Machine-learning-based (train a classifier on labelled text — more accurate but needs training data).
07 · Quick reference — data structures
One page per data structure. Skim, don't read.
Vector — 1D, all same type
v <- c(10, 20, 30, 40, 50) # build with c()
length(v) # 5
v[2] # 20 (1-indexed!)
v[2:4] # 20 30 40
v[-2] # all except 2nd
v[v > 25] # filter: 30 40 50
sum(v); mean(v); sd(v) # vectorised stats
BA Practical · R Guidebook 12 / 18
Matrix — 2D, all same type
m <- matrix(1:12, nrow = 3, ncol = 4)
matrix(1:12, 3, 4, byrow = TRUE) # fill row-by-row instead
m[2, 3] # one cell
m[, 1] # whole column 1 (vector)
m[2, ] # whole row 2 (vector)
m[-2, ] # remove row 2
m1 + m2 # element-wise add
m1 * m2 # element-wise multiply
m1 %*% m2 # MATRIX multiplication
t(m) # transpose
rowSums(m); colSums(m)
TRAP
m1 * m2 is element-wise. m1 %*% m2 is matrix multiplication. They give different answers.
List — heterogeneous container
L <- list(num = 42, txt = "hi", v = c(1,2,3))
L[[1]] # the underlying value (42)
L[["num"]] # same, by name
L$num # same, shortcut
L[1] # a SUB-LIST of length 1 — different!
L$new <- "added" # add element
L$num <- NULL # delete element
VIVA FAVOURITE
Q. Difference between [ ] and [[ ]] ? Single brackets give back a sub-list (still a list). Double brackets and $
give back the actual element. You almost always want [[ ]] or $ .
Factor — categorical with fixed levels
x <- c("M", "F", "M", "F", "M")
fac <- factor(x)
levels(fac) # "F" "M"
# CRITICAL: levels are LOCKED at creation
fac[1] <- "X" # → NA + warning
# because "X" isn't a level
# To allow new values, declare them up front:
fac <- factor(x, levels = c("M", "F", "Other"))
BA Practical · R Guidebook 13 / 18
Data frame — the workhorse for the exam
# Build
df <- [Link](name = c("A","B","C"),
age = c(25, 30, 35))
# Inspect
str(df); summary(df); head(df); nrow(df)
# Access
df$name # column (vector)
df[, "age"] # column by name
df[1, ] # row 1 (data frame)
df[df$age > 28, ] # FILTER — rows where age > 28
# Missing values
[Link](df$age) # TRUE/FALSE per row
[Link](df) # drop rows with any NA
df$age[[Link](df$age)] <- mean(df$age, [Link]=TRUE) # impute
# Combine — like SQL JOIN
merge(df1, df2, by = "id") # INNER
merge(df1, df2, by = "id", all = TRUE) # FULL OUTER
merge(df1, df2, by = "id", all.x = TRUE) # LEFT
merge(df1, df2, by = "id", all.y = TRUE) # RIGHT
The apply family — replace loops
# apply() — across rows or columns of a matrix
apply(m, 1, sum) # 1 = ROWS
apply(m, 2, mean) # 2 = COLUMNS
# lapply() — over a list, returns a list
lapply(L, function(x) x * 2)
# sapply() — like lapply but simplifies to vector/matrix
sapply(L, mean)
# tapply() — group-by then apply
tapply(salaries, dept, mean) # mean salary per dept
08 · Visualisations gallery
For Q1 (compulsory dashboard) and any chart asked. Two ways to plot — base R is simpler, ggplot2 is prettier.
BA Practical · R Guidebook 14 / 18
Base R plotting (no package needed)
# Histogram (one continuous variable)
hist(mpg$hwy,
breaks = 15, col = "steelblue",
main = "Highway Mileage", xlab = "MPG")
# Bar chart (categorical counts)
counts <- table(mpg$class)
barplot(counts,
col = "coral",
main = "Cars by Class", ylab = "Count")
# Box plot (one continuous, one categorical)
boxplot(hwy ~ class, data = mpg,
col = "lightgreen",
main = "HWY by class")
# Scatter plot (two continuous)
plot(mpg$displ, mpg$hwy,
pch = 19, col = "blue",
main = "Engine Size vs MPG",
xlab = "Engine Size", ylab = "MPG")
# Line graph (typically over time)
plot(year, sales, type = "l",
col = "blue", lwd = 2,
main = "Sales Trend")
ggplot2 (cleaner, but needs library(ggplot2))
library(ggplot2)
# Histogram
ggplot(mpg, aes(x = hwy)) +
geom_histogram(binwidth = 2,
fill = "steelblue", color = "black") +
labs(title = "Highway Mileage",
x = "MPG", y = "Frequency")
# Bar chart
ggplot(mpg, aes(x = class)) +
geom_bar(fill = "coral", color = "black") +
labs(title = "Cars by Class")
# Box plot
ggplot(mpg, aes(x = class, y = hwy)) +
geom_boxplot(fill = "lightgreen") +
labs(title = "HWY by Class")
# Scatter with colour
ggplot(mpg, aes(x = displ, y = hwy, color = class)) +
geom_point(size = 3) +
labs(title = "Engine Size vs MPG")
# Add a regression line — useful for Q5
ggplot(mpg, aes(x = displ, y = hwy)) +
geom_point() +
geom_smooth(method = "lm", se = TRUE)
BA Practical · R Guidebook 15 / 18
Chart When to use What it shows
Histogram One continuous variable Distribution shape: skew, spread, peaks
Bar chart One categorical variable Counts in each category
Box plot Continuous Y, categorical X Median, Q1, Q3, outliers — comparison across groups
Scatter Two continuous variables Relationship, trend, clusters
Line graph X is time / ordered Trend over time
PRO TIP FOR Q1
Q1 is "create a dashboard / visualisation". You can do it in Excel, Power BI or R. If you choose R, just stack 4 plots in
a 2×2 grid: par(mfrow = c(2,2)) , then 4 plot calls, then par(mfrow = c(1,1)) .
09 · Top 15 viva questions
These cover ~80% of what gets asked. Read every answer aloud once.
1. Difference between a package and a library? 2. Why <- and not =?
Package = bundle of functions and data (e.g. readxl). Library Convention. <- is for assignment; = is for binding arguments
= the directory where installed packages live. Install once, inside function calls. Using <- for assignment makes intent
library every time. clearer.
3. Vector vs list? 4. [ ] vs [[ ]] on a list?
Vector = homogeneous (all elements same type). List = Single bracket returns a sub-list. Double bracket (and $) returns
heterogeneous (any types, any sizes). the actual element. Almost always you want [[ ]].
5. What's a factor and when? 6. fac[2] <- 15 gives NA. Why?
Categorical data with fixed levels. Use it for Gender, Factor levels are locked at creation. 15 isn't in the levels, so the
Department, Region — anything with predefined categories. assignment becomes NA. Either pre-declare with levels =
Models need it to know which columns to dummy-code. c(..., 15), or convert to character first.
7. Matrix vs data frame? 8. What is [Link] = TRUE?
Matrix = 2D, all same type. Data frame = 2D, columns can have Tells mean(), sum(), sd() etc. to skip NAs. Without it, even
different types. Use data frame for real-world tabular data. one NA in the input poisons the result.
9. Recycling rule? 10. How do you interpret a slope coefficient?
When two vectors of different lengths are combined element- For each one-unit increase in the predictor, the response is
wise, R repeats the shorter one until lengths match. Warning if expected to change by the slope, holding all other predictors
the longer length isn't a multiple of the shorter. constant.
11. R² meaning? 12. R² vs Adjusted R²?
Proportion of variance in Y explained by the model. R² = 0.81 Adjusted R² penalises adding useless predictors. Plain R² only
→ 81% of variation explained. Bounded between 0 and 1. goes up as you add predictors; adjusted R² can decrease,
exposing dead-weight predictors.
13. What is heteroscedasticity? 14. What is multicollinearity? How to detect?
Non-constant variance of residuals — the spread changes with Strong linear relationship between predictors. Detected with
the predictor's value. Detected by funnel-shape in residuals-vs- VIF — values > 10 = severe. Fix: drop one of the correlated
fitted, or by bptest() p < 0.05. Fix: log-transform Y. predictors.
BA Practical · R Guidebook 16 / 18
15. Confidence vs prediction interval?
Confidence interval = uncertainty about the MEAN response.
Prediction interval = uncertainty about an INDIVIDUAL
response. Prediction is wider — it includes individual residual
noise.
10 · Common errors & fixes
Memorise the top 5. The rest you can skim.
Error message What it means Fix
could not find function "X" Package not loaded library(<package>) first
there is no package called 'X' Not installed [Link]("X")
object 'X' not found Variable doesn't exist Check spelling, case, or run earlier lines
non-numeric argument to binary Tried + on a string [Link](x) or fix the column type
operator
cannot open file: No such file Wrong path Check getwd() , use [Link]()
subscript out of bounds Index beyond length Check length(v) ; remember R is 1-
indexed
invalid factor level, NA generated Assigning value not in levels Add the level via levels= or use
character
Result is NA from mean() NAs in input Add [Link] = TRUE
longer object length is not a multiple Recycling rule warning Usually a real bug — check vector
of shorter lengths
argument is of length zero (in if ) Comparing NA in if- Wrap in isTRUE() or [Link]()
condition
Plot doesn't appear Inside a function, forgot Wrap ggplots in print() when in
print() functions
Error in [Link]: NA/NaN/Inf NAs in modelling data [Link](df) before lm()
IF PANIC STRIKES
Walk through this checklist before raising your hand:
1. Did I run library() for every package I'm using?
2. Did I run the earlier lines that create the variable I'm referencing?
3. Are file paths using forward slashes / not backslashes?
4. Have I added [Link] = TRUE to mean() / sum() / sd() ?
5. Is the column class right? str(df) tells you.
BA Practical · R Guidebook 17 / 18
11 · Final cheatsheet — read 30 minutes before the
exam
Setup Diagnostics
rm(list = ls()) # heteroscedasticity
setwd("path/to/folder") plot(m$[Link], m$residuals);
library(readxl); library(ggplot2) abline(h=0,col="red")
df <- [Link]("[Link]") library(lmtest); bptest(m) # p<0.05 = bad
str(df); summary(df)
# multicollinearity
library(car); vif(m) # >10 = bad
Descriptive stats
# 4 diagnostic plots
mean(x, [Link] = TRUE) par(mfrow=c(2,2)); plot(m); par(mfrow=c(1,1))
median(x); sd(x); var(x); IQR(x)
quantile(x); range(x); summary(x)
cov(x, y); cor(x, y); cor(x,y)^2 Wordcloud + sentiment
get_mode <- function(v) { library(tm); library(wordcloud); library(syuz
u <- unique(v) het)
u[[Link](tabulate(match(v,u)))]
} corp <- Corpus(VectorSource(text))
corp <- tm_map(corp, content_transformer(tolo
wer))
Visualisations corp <- tm_map(corp, removeNumbers)
corp <- tm_map(corp, removePunctuation)
hist(x, col="steelblue", main="...", xlab=".. corp <- tm_map(corp, removeWords, stopwords("
.") en"))
boxplot(y ~ class, col="lightgreen") corp <- tm_map(corp, stripWhitespace)
plot(x, y, pch=19, col="blue")
abline(model, col="red", lwd=2) tdm <- TermDocumentMatrix(corp)
v <- sort(rowSums([Link](tdm)), decreasi
Regression ng=TRUE)
wordcloud(names(v), v, [Link]=2,
colors=[Link](8,"Dark2"))
m <- lm(y ~ x, data = df); summary(m)
m <- lm(y ~ x1 + x2, data = df); summary(m)
s <- get_nrc_sentiment(text)
m <- lm(y ~ ., data = df); summary(m)
barplot(colSums(s), las=2, col=rainbow(10))
predict(m, newdata = [Link](x = 50))
predict(m, ..., interval="confidence")
predict(m, ..., interval="prediction")
T WO RULES THAT EARN MARKS
1. Always run summary(model) after lm() — many marks live in interpreting it.
2. Always write your interpretation as # comments — last year's paper paid explicitly for "interpret as comments
in R-Script".
FINAL RITUAL
Folder = your full exam roll number. Sub-folders Q.1 , Q.2 , …
File names: Qn_subpart_last4digits.R . Save with Ctrl+S after every change. Good luck.
BA Practical · R Guidebook 18 / 18