R's Data Model
Everything in R is a vector. Unlike Python, where a scalar 42 is an int object, in R 42 is a numeric vector of length 1. This single idea explains most of R's surprising behaviors.
x <- c(1, 2, 3, 4, 5) # c() "combines" scalars into a vector
class(x) # "numeric"
length(x) # 5
x[2] # 2 (R is 1-indexed!)
x[c(1, 3)] # 1 3
x > 3 # FALSE FALSE FALSE TRUE TRUE (logical vector)
x[x > 3] # 4 5 (logical subsetting)The <- assignment arrow is idiomatic R; = works too but is typically reserved for function arguments.
Atomic Vector Types
R has six atomic types. The ones you will use daily:
- logical —
TRUE/FALSE(Python:bool) - integer —
1L,2L(theLsuffix marks integer literals) - double —
3.14(Python:float; this is the default numeric type) - character —
"hello"(Python:str) - factor — categorical variable with fixed levels (Python:
pd.Categorical)
Factors deserve special mention because they appear constantly in statistical output and plotting:
status <- factor(c("low", "high", "med", "high", "low"),
levels = c("low", "med", "high"))
table(status) # counts per level, in level order
nlevels(status) # 3forcats (part of the Tidyverse) provides helpers like fct<em>reorder, fct</em>lump, and fct<em>relevel for manipulating factors — essential before feeding categoricals to ggplot2.
Lists
A list is R's heterogeneous container — the Python dict / list hybrid:
person <- list(name = "Ada", age = 36, scores = c(95, 87, 92))
person$name # "Ada" ($ is like Python .attribute)
person[["age"]] # 36 (double brackets extract the value)
person["age"] # a list of length 1 (single brackets keep the list wrapper)Data Frames and Tibbles
A data frame is a named list of equal-length vectors — directly analogous to a pandas DataFrame. A tibble (tbl</em>df) is the Tidyverse's improved version:
- Never converts strings to factors automatically (unlike base
data.frame). - Prints only the first 10 rows and truncates wide columns.
- Gives informative errors on non-existent column access instead of silently returning
NULL.
library(tibble)
df <- tibble(
name = c("Alice", "Bob", "Carol"),
score = c(88, 95, 72),
passed = score >= 75 # you can reference earlier columns!
)
df
# A tibble: 3 × 3
# name score passed
# <chr> <dbl> <lgl>
# 1 Alice 88 TRUE
# 2 Bob 95 TRUE
# 3 Carol 72 FALSENotice the column type tags (<chr>, <dbl>, <lgl>) printed beneath headers — a convenience absent from base data frames.
Python ↔ R Quick Reference
pd.DataFrame({...})→tibble(...)df['col']→df$colordf[['col']]df.shape→dim(df)ornrow(df)/ncol(df)df.dtypes→str(df)orglimpse(df)(fromdplyr)df.head(10)→head(df, 10)or justdf(tibble truncates automatically)df.describe()→summary(df)
Reading Data
readr (Tidyverse) reads rectangular text files faster than base R and returns tibbles:
library(readr)
students <- read_csv("students.csv") # comma-separated
exams <- read_tsv("exams.tsv") # tab-separated
config <- read_csv("data.csv",
col_types = cols(
id = col_integer(),
score = col_double(),
date = col_date(format = "%Y-%m-%d")
))read<em>csv (with underscore) is readr's function; read.csv (with dot) is the slower base-R version. Prefer readr.
Missing Values
R uses NA (not NaN) as the universal missing-value sentinel. NA is typed: NA</em>real<em>, NA</em>integer<em>, NA</em>character_. Key behaviors:
is.na(NA) # TRUE
sum(c(1, NA, 3)) # NA (NA is contagious)
sum(c(1, NA, 3), na.rm = TRUE) # 4The na.rm = TRUE pattern recurs across virtually all R summary functions.