Data Structures
R provides several data structures for organizing your data. Each one serves a different purpose. In bioinformatics, you will use all of them regularly. This page walks through the most important ones with practical examples.
Vectors
Section titled “Vectors”A vector is the most basic data structure in R. It holds a sequence of values that are all the same type. You can think of it as a single column of data.
Use the c() function to create a vector. Here we store the lengths of five genes in base pairs.
gene_lengths <- c(1542, 987, 2341, 456, 1823)gene_lengths[1] 1542 987 2341 456 1823The length() function tells you how many elements are in the vector.
length(gene_lengths)[1] 5Vectors can hold text values too. Character vectors are useful for storing gene names, sample IDs, or any other labels.
gene_names <- c("TP53", "BRCA1", "EGFR", "KRAS", "MYC")gene_names[1] "TP53" "BRCA1" "EGFR" "KRAS" "MYC"Logical vectors hold TRUE or FALSE values. These are handy for tracking quality control status.
passed_qc <- c(TRUE, TRUE, FALSE, TRUE, FALSE)passed_qc[1] TRUE TRUE FALSE TRUE FALSEIndexing vectors
Section titled “Indexing vectors”You can extract specific elements from a vector using square brackets. R uses 1-based indexing, so the first element is at position 1.
gene_names[1][1] "TP53"Pass a vector of positions to grab multiple elements at once.
gene_names[c(1, 3, 5)][1] "TP53" "EGFR" "MYC"The colon operator creates a range of positions. This is a quick way to select consecutive elements.
gene_names[2:4][1] "BRCA1" "EGFR" "KRAS"You can filter a vector using a logical condition. This returns only the gene lengths greater than 1000.
gene_lengths[gene_lengths > 1000][1] 1542 2341 1823A logical vector can also serve as a filter. Here we use the passed_qc vector to select only the genes that passed quality control.
gene_names[passed_qc][1] "TP53" "BRCA1" "KRAS"Named vectors
Section titled “Named vectors”You can assign names to each element of a vector. This lets you look up values by name instead of by position. Named vectors are common for storing gene expression values.
expression <- c(TP53 = 23.5, BRCA1 = 45.2, EGFR = 12.8, KRAS = 67.3, MYC = 34.1)expression TP53 BRCA1 EGFR KRAS MYC 23.5 45.2 12.8 67.3 34.1Access a single value by its name using quotes inside square brackets.
expression["BRCA1"]BRCA1 45.2You can also select multiple named elements at once.
expression[c("TP53", "MYC")]TP53 MYC23.5 34.1The names() function returns all the names as a character vector.
names(expression)[1] "TP53" "BRCA1" "EGFR" "KRAS" "MYC"Vector operations
Section titled “Vector operations”R applies mathematical functions to every element of a vector at once. This is called vectorization. It makes R fast and concise. For example, you can log-transform all expression values in one step.
log2_expr <- log2(expression)log2_expr TP53 BRCA1 EGFR KRAS MYC4.554589 5.498251 3.678072 6.072535 5.091700Summary functions collapse a vector into a single value.
sum(gene_lengths)[1] 7149mean(gene_lengths)[1] 1429.8The sort() function reorders the values. Set decreasing = TRUE to sort from highest to lowest.
sort(expression, decreasing = TRUE) KRAS BRCA1 MYC TP53 EGFR 67.3 45.2 34.1 23.5 12.8Factors
Section titled “Factors”Factors represent categorical data. They are essential when your data has a fixed set of possible values, such as treatment groups or batch labels. R stores the unique categories as “levels.”
condition <- c("treated", "control", "treated", "control", "treated")condition_factor <- factor(condition)condition_factor[1] treated control treated control treatedLevels: control treatedThe levels() function shows the unique categories.
levels(condition_factor)[1] "control" "treated"The table() function counts how many times each level appears. This is useful for checking sample balance across experimental groups.
table(condition_factor)condition_factorcontrol treated 2 3Sometimes categories have a natural order. Ordered factors let you specify this ranking. Here we create a severity factor with a defined ranking from mild to severe.
severity <- factor(c("mild", "severe", "moderate", "mild", "severe"), levels = c("mild", "moderate", "severe"), ordered = TRUE)severity[1] mild severe moderate mild severeLevels: mild < moderate < severeLists can hold elements of different types and different lengths. This makes them flexible containers. Many R functions return their results as lists. A statistical test might return a gene name, a p-value, a fold change, and a vector of sample IDs all in one object.
test_result <- list( gene = "TP53", pvalue = 0.0023, log2fc = 2.1, significant = TRUE, samples = c("S1", "S2", "S3"))test_result$gene[1] "TP53"
$pvalue[1] 0.0023
$log2fc[1] 2.1
$significant[1] TRUE
$samples[1] "S1" "S2" "S3"Use the $ operator to access a named element of a list.
test_result$gene[1] "TP53"test_result$pvalue[1] 0.0023Double square brackets also work for extracting list elements by name.
test_result[["samples"]][1] "S1" "S2" "S3"Matrices
Section titled “Matrices”A matrix is a two-dimensional structure where every element has the same type. In bioinformatics, matrices are commonly used to store gene expression count data. Rows represent genes. Columns represent samples.
count_matrix <- matrix( c(100, 250, 50, 120, 300, 75, 90, 280, 60), nrow = 3, byrow = TRUE, dimnames = list( c("TP53", "BRCA1", "EGFR"), c("Sample1", "Sample2", "Sample3") ))count_matrix Sample1 Sample2 Sample3TP53 100 250 50BRCA1 120 300 75EGFR 90 280 60Select a single row by name. Leave the column index empty to get all columns.
count_matrix["TP53", ]Sample1 Sample2 Sample3 100 250 50Select a single column by name. Leave the row index empty to get all rows.
count_matrix[, "Sample2"] TP53 BRCA1 EGFR 250 300 280The dim() function returns the number of rows and columns.
dim(count_matrix)[1] 3 3Data frames
Section titled “Data frames”Data frames are the most common structure for tabular data in R. Unlike matrices, each column can hold a different type. One column might be character strings, another might be numbers, and another might be logical values. This makes data frames ideal for storing sample metadata.
sample_metadata <- data.frame( sample_id = c("S1", "S2", "S3", "S4"), condition = c("control", "control", "treated", "treated"), read_count = c(25000000, 28000000, 31000000, 27000000), passed_qc = c(TRUE, TRUE, TRUE, FALSE))sample_metadata sample_id condition read_count passed_qc1 S1 control 2.5e+07 TRUE2 S2 control 2.8e+07 TRUE3 S3 treated 3.1e+07 TRUE4 S4 treated 2.7e+07 FALSEThe str() function shows the structure of a data frame. It tells you the type and first few values of each column. This is one of the most useful functions for inspecting your data.
str(sample_metadata)'data.frame': 4 obs. of 4 variables: $ sample_id : chr "S1" "S2" "S3" "S4" $ condition : chr "control" "control" "treated" "treated" $ read_count: num 2.5e+07 2.8e+07 3.1e+07 2.7e+07 $ passed_qc : logi TRUE TRUE TRUE FALSEUse $ to extract a single column as a vector.
sample_metadata$condition[1] "control" "control" "treated" "treated"Filter rows using a logical condition. Here we keep only the samples that passed quality control.
sample_metadata[sample_metadata$passed_qc, ] sample_id condition read_count passed_qc1 S1 control 2.5e+07 TRUE2 S2 control 2.8e+07 TRUE3 S3 treated 3.1e+07 TRUESeveral functions help you inspect the size and structure of a data frame.
nrow(sample_metadata)[1] 4ncol(sample_metadata)[1] 4colnames(sample_metadata)[1] "sample_id" "condition" "read_count" "passed_qc"Quick reference
Section titled “Quick reference”| Structure | Dimensions | Types allowed | Typical use in bioinformatics |
|---|---|---|---|
| Vector | 1D | One type only | Gene names, expression values |
| Factor | 1D | Categorical | Treatment groups, batch labels |
| List | 1D | Mixed types | Statistical test results |
| Matrix | 2D | One type only | Expression count matrices |
| Data frame | 2D | Mixed per column | Sample metadata, annotation tables |
Next steps
Section titled “Next steps”Now that you know how R organizes data, you are ready to learn how to control the flow of your programs. Continue to Control Flow.