Data Types and Data Structures
Understand what values are, how data is organized, and how to extract the parts you need
Data types
Data types describe what individual values are and what operations make sense for them. The sections below cover the types you will encounter most often when working with data in R.
Important data types in R
Most beginner errors in R come from a mismatch between what a value means and what type R thinks it is.
| Type | What it stores | Example | Typical use |
|---|---|---|---|
numeric |
Decimal values (double precision) | 74.3, 3.14 |
Continuous measurements and arithmetic |
integer |
Whole-number values | 1L, 2L, 3L |
Counts, whole numbers |
character |
Text strings | "A", "sample_01" |
Names, IDs, labels |
logical |
Truth values | TRUE, FALSE |
Filtering and condition checks |
factor |
Categorical levels | factor(c("Low", "High")) |
Grouping in summaries, models, and plots |
Date |
Calendar date | as.Date("2026-08-14") |
Day-level time tracking |
POSIXct |
Date and time | as.POSIXct("2026-08-14 09:30") |
Timestamps and time-based events |
Quick inspection example:
R treats 1 and 1L differently: 1 is numeric and 1L is integer. In many beginner workflows, plain 1 is fine. Use L when you explicitly want integer type.
Numeric and integer
Use numeric for measurements and calculations where decimal precision matters. Use integer for whole-number counts and explicit indexing values.
Type conversion is possible, but be careful about what changes:
Character
Use character for text by wrapping in quotes (““). Even if text looks like a number, it is still text until converted.
If conversion fails, invalid entries become NA:
Logical
Logical values are either TRUE or FALSE. Comparisons create logical values, which R can then use to make decisions or select data.
For example, > asks whether each value is greater than 500. R checks every value and returns one logical result for each comparison:
The result can be stored in a named object. Here, is_heavy is a logical vector with the same length as weight_mg:
Logical vectors are useful for selecting matching values. R keeps the values where the logical index is TRUE and drops the values where it is FALSE:
You can also combine or reverse logical conditions. Use & when both conditions must be true, | when either condition may be true, and ! to reverse TRUE to FALSE or FALSE to TRUE:
Factor (categorical data)
Use factors when values represent specific categories. Converting to factors can change how R interprets the values, especially for numerical variables that should be treated as discrete groups.
Consider a first-order degradation study. The measured concentration decreases over time at three temperatures:
Here, temperature is genuinely numeric. A temperature of 40 °C is higher than 30 °C, and the difference between temperatures has meaning. A continuous colour scale communicates that ordering:
However, you may want to treat the tested temperatures as separate experimental groups. In that case, convert the column to a factor. The temperature values are no longer used to determine a continuous colour gradient; they define three discrete groups:
Now the colour scale is discrete and the data is grouped by temperature:
The factor version is useful when the temperatures are experimental groups that you want to compare. The numeric version is useful when you want the plot to emphasize that temperature is ordered and that the spacing between 20, 30, and 40 °C matters. Factor conversion changes the interpretation of the variable, not only how it prints.
Date and POSIXct
Use Date for day-level data and POSIXct when time-of-day matters.
Dates can be compared and sorted. Subtracting one Date from another gives the time between them in days:
You can add or subtract whole numbers of days from a Date:
POSIXct values also include a time of day, so subtracting them gives a duration in seconds:
The subtraction above does not round the duration. Request the unit explicitly when you need a numeric result:
Use Date when the time of day is not relevant. Use POSIXct when the order or duration of events matters within a day.
Missing and special values: NA, NaN, NULL, Inf
These values are common in real data and affect calculations differently.
| Value | Meaning | Typical source |
|---|---|---|
NA |
Missing value | Not recorded or unavailable data |
NaN |
Undefined numeric result | Invalid math like 0/0 |
Inf / -Inf |
Infinite value | Division by zero |
NULL |
No value/object exists | Empty list element, optional input omitted |
Data structures
Data types describe what values are. Data structures describe how values are arranged and stored together. The next sections introduce the main structures you will work with in R.
Vector, list, data frame, tibble
The structure determines how you access and work with the values inside it.
Vector
A vector is a sequence of values of the same type. The values have positions, so vectors are useful for calculations and simple indexing.
You can also create a named vector directly. Names are labels that make the contents easier to read and identify:
List
A list can contain different types or objects (even other vectors or lists). Each component can have its own length and structure.
You can also create a named list directly. This is optional, but names make a list easier to understand:
Names are labels, not replacements for the values themselves. They are especially useful when positions may change or when the meaning of a value is more important than its position.
Data frame and tibble
A data frame or tibble stores vectors of equal length as columns. Each row represents one observation, and each column can have a different type.
Both objects are tables with two rows and two columns. A tibble is a modern version of a data frame with cleaner printing and stricter behavior. glimpse() shows the columns, their types, and example values without printing the whole table:
The structures are related but not interchangeable: a column in a tibble is a vector, while a tibble itself is a collection of columns. The next sections show how indexing extracts values from each structure.
Indexing vectors and lists
Indexing means selecting part of an object. For vectors, use square brackets [] with a position, a name, or a logical condition. R indexing starts at 1, so the first element is at position 1. A single index can return one value, while a range or condition can return several values.
For lists, the brackets determine whether you keep the result as a list or extract the element inside it. [] selects one or more list components and returns a list. [[]] extracts the contents of one component and returns that component itself.
The same rule works with a named list. In that case, you can use either the position or the component name:
Indexing data frames and tibbles ([], [[ ]], $)
Data frames and tibbles have two dimensions: rows and columns. The general form is [rows, columns]. The comma separates the row index from the column index:
- Before the comma, specify which rows to keep.
- After the comma, specify which columns to keep.
- Leaving one side empty means “keep all” rows or columns.
The result of [rows, columns] is still a tibble/data frame, even when it contains one row or one column. To select several columns, provide their names as a character vector or use a range of numeric positions:
To extract one column as a vector, use $ or [[ ]]:
For common data-analysis tasks, tidyverse functions make the intention more explicit:
See Data Handling for a fuller workflow with filter(), select(), and mutate().