Memra

Levels, and why their order matters

◈ 8 cards

factor(prov4) prints ON QC ON BC with Levels: BC ON QC — alphabetical unless you say otherwise. levels() lists the categories; table() counts them in level order. factor(c("M","S","L","S"), levels = c("S","M","L")) makes a bar chart sort S-M-L. as.numeric(factor) returns level codes, not labels.

A categorical variable has a type of its own

Module 2 sorted every variable into numeric or categorical, and the categorical ones into nominal (prov) and ordinal (a size band S / M / L). R gives categorical variables their own type, the factor: a vector whose values are drawn from a fixed set of levels.

> prov4 <- c("ON", "QC", "ON", "BC")
> prov_f <- factor(prov4)
> prov_f
[1] ON QC ON BC
Levels: BC ON QC

Two things changed. The values print without quotes — they are no longer text, they are category labels. And a second line appeared: Levels: lists the distinct categories, and their order is alphabetical by default. levels() returns that list, and table() counts in level order:

> levels(prov_f)
[1] "BC" "ON" "QC"
> table(prov_f)
prov_f
BC ON QC 
 1  2  1 

Worked example — an ordinal variable needs its order set

Maple & Birch bands orders as S, M or L. Alphabetical order is L M S, which is wrong on every chart. factor() takes a levels argument, and the order you give is the order R keeps:

> size <- factor(c("M", "S", "L", "S"), levels = c("S", "M", "L"))
> size
[1] M S L S
Levels: S M L
> table(size)
size
S M L 
2 1 1 

Without the levels argument the same data tables as L M S / 1 1 2. The values are identical; only the order — and therefore every bar chart and every table() built from it — differs. Setting levels is the R form of dragging the pivot rows into S-M-L order in Sheets, done once and kept.

The trap: as.numeric() on a factor

Under the labels, a factor stores an integer code per level — 1 for the first level, 2 for the second. as.numeric() returns those codes, not the labels:

> as.numeric(size)
[1] 2 1 3 1

M is level 2, S is level 1, L is level 3. That is harmless for sizes, and disastrous for a factor of numbers. A column of prices that arrived as a factor — factor(c("10", "20", "10")) — converts to 1 2 1, not 10 20 10. The fix goes through text first: as.numeric(as.character(f)) gives 10 20 10.

read.csv() no longer makes factors

Before R 4.0.0, read.csv() turned every text column into a factor, and this trap caught everyone. Since R 4.0.0 the default is stringsAsFactors = FALSE: text columns arrive as chr, and you make a factor only when you want one (ordersprov)). Module 10.4 shows str(orders) with every text column as chr — that line is the verification.

Type the two factor calls, then read the codes

Type prov_f <- factor(prov4) and the explicit-order size. The questions read the Levels: line, the default order, and what as.numeric() returns.

CRISP-DM: fixing a level order is data preparation → format data.

NORMAL ~/memra/learn/afm-112/factors-r-categorical-type utf-8 LF