my_age <- 34
my_age## [1] 34
Objects, vectors, data frames, factors, missing values
George Michaelides
Norwich Business School, University of East Anglia
July 7, 2026
By the end of this tutorial you will be able to:
na.rm = TRUEThis tutorial uses only base R — no additional packages are needed. We set the output comment prefix to ## so that the rendered output matches what you will see in your RStudio console.
In R, you store values in named objects using the assignment operator <-. Think of an object as a labelled box that holds a value you can refer to later.
The arrow points from the value on the right to the name on the left, so “34 goes into my_age”. You can also use = for assignment, but <- is the convention we follow throughout these tutorials because it makes the direction of assignment explicit.
Object names should be descriptive and use snake_case — lowercase letters with underscores separating words. Avoid dots in names; they are an older R convention and can be confused with method calls in other languages.
To see everything in your current environment, use ls(). To remove an object, use rm().
## [1] "both_assigned" "mean_score" "my_age" "pandoc_dir"
## [5] "participant_id" "quarto_bin_path"
R is case-sensitive. My_Age and my_age are two different objects. If you get an object 'x' not found error, check your capitalisation first.
A vector is the fundamental data structure in R — a sequence of values that all share the same type. You create vectors with c(), which stands for “combine” or “concatenate”.
Every value in a vector must be the same type. R has four main types you will meet:
You can inspect the type and length of any object with class(), typeof(), and length():
For a compact overview of an object’s structure, use str():
Create a character vector called item_names containing the names of three survey items (e.g. "con1", "con3", "con6"). Then check its class and length.
If you mix types in a vector, R silently coerces everything to the most general type:
The number 1 and 3 have been converted to text. This is rarely what you want, so keep your vectors homogeneous.
You pull out individual elements of a vector with square brackets. Position numbers start at 1 in R (not 0, as in Python or C).
## [1] 3
## [1] 3 2
## [1] 4 2 5 4
You can also use a logical vector to select elements that meet a condition:
This is called logical indexing and you will use it constantly. The comparison scores > 3 produces a logical vector of the same length as scores:
R returns TRUE for each element that satisfies the condition, and the square brackets keep only those positions.
Use logical indexing to extract only the scores equal to 5 from scores.
A data frame is R’s version of a spreadsheet — a collection of columns (vectors) where each column can have a different type but every column has the same length. This is the structure you will work with most often when analysing survey data.
You can build a data frame from vectors with data.frame():
## participant age_group pa_score
## 1 P001 2 4.2
## 2 P002 3 3.8
## 3 P003 4 2.1
## 4 P004 3 3.5
Inspect its dimensions and column names:
## [1] 4
## [1] 3
## [1] 4 3
## [1] "participant" "age_group" "pa_score"
For a quick look at the first or last few rows, use head() and tail():
## participant age_group pa_score
## 1 P001 2 4.2
## 2 P002 3 3.8
## participant age_group pa_score
## 4 P004 3 3.5
And for summary statistics of every column at once, summary():
## participant age_group pa_score
## Length :4 Min. :2.00 Min. :2.10
## N.unique :4 1st Qu.:2.75 1st Qu.:3.15
## N.blank :0 Median :3.00 Median :3.65
## Min.nchar:4 Mean :3.00 Mean :3.40
## Max.nchar:4 3rd Qu.:3.25 3rd Qu.:3.90
## Max. :4.00 Max. :4.20
summary() is your fastest first look at any dataset. It shows the minimum, first quartile, median, mean, third quartile, and maximum for numeric columns, and counts for factors.
Data frames use row, column indexing with square brackets:
## [1] 4.2 3.8 2.1 3.5
## participant pa_score
## 1 P001 4.2
## 2 P002 3.8
## 3 P003 2.1
## 4 P004 3.5
## participant age_group pa_score
## 1 P001 2 4.2
## participant age_group
## 1 P001 2
## 2 P002 3
## 3 P003 4
The $ operator is a convenient shortcut for extracting a single column by name:
Extract the pa_score values for participants whose age_group is 3 or higher, using logical indexing on df$age_group.
R provides built-in functions for the descriptive statistics you will need throughout these tutorials:
## [1] 3.525
## [1] 3.45
## [1] 0.6158618
## [1] 0.3792857
## 25% 50% 75%
## 3.05 3.45 3.95
## [1] 2.8 4.5
NAReal survey data almost always has missing values. In R, missing values are represented by NA. The problem is that many functions return NA if any NA is present:
Pass na.rm = TRUE to tell R to ignore missing values before computing:
Forgetting na.rm = TRUE is the single most common error in early R use. If you see NA as your result and did not expect it, add na.rm = TRUE and try again.
A factor is R’s structure for categorical variables — a vector of integer codes, each mapped to a text label. Factors matter because many statistical functions (ANOVA, regression with grouping variables) treat factors differently from numeric vectors.
## [1] Male Female Female Male Female
## Levels: Male Female
## [1] "factor"
## [1] "Male" "Female"
For Likert-scale items (e.g. 1 = Never, 5 = Very often), you will usually keep them as numeric values because you want means and standard deviations. But for grouping variables like gender, age band, or experimental condition, convert them to factors so R knows to treat them as categories.
You will see in Tutorial 03 that when you import an SPSS .sav file with haven::read_sav(), categorical variables come in as haven_labelled objects. You convert them to factors with haven::as_factor().
R distinguishes several kinds of “nothing”:
NA — “Not Available”. A missing value. The most common one in survey data.NaN — “Not a Number”. The result of an undefined mathematical operation, e.g. 0 / 0.NULL — “empty”. The object itself does not exist (different from NA, which means the value is missing).You test for missing values with is.na(), which returns a logical vector:
## [1] FALSE FALSE TRUE FALSE FALSE TRUE FALSE
## [1] 2
To keep only the complete cases (rows with no NA anywhere), use complete.cases():
Create a vector items <- c(3, NA, 4, 5, NA, 2, 4). Calculate (a) the number of missing values, (b) the mean of the non-missing values, and (c) a new vector with the NAs removed.
A vector holds values of the same type. A list can hold values of different types — including other vectors, data frames, or even other lists. You index lists with double square brackets [[ ]] or the $ operator. You will encounter lists when fitting lavaan models (a fitted model object is a list containing coefficients, residuals, and fit indices). For now, know the distinction: c() creates a vector; list() creates a list.
Error: object 'score' not found — you have either not run the line that creates the object, or you have misspelled the name (remember R is case-sensitive).
Error in foo(): could not find function "foo" — the function does not exist, or the package that provides it has not been loaded with library(). In Tutorial 02 we cover package management in detail.
NA in resultsYour result is NA when you expected a number. Add na.rm = TRUE to the function call (see above).
Create a numeric vector reaction_times <- c(245, 312, 198, 401, 287, 356). Calculate the mean, standard deviation, and median. Round the mean to two decimal places using round().
Using the same reaction_times vector, use logical indexing to extract only the reaction times below 300 ms. How many are there?
Create a data frame participants with three columns: id (integers 1 to 5), condition (factor with levels “control” and “treatment”), and score (numeric values of your choice). Use summary() to inspect it.
Add a missing value to your score column (set the third participant’s score to NA). Recalculate the mean score using na.rm = TRUE. How many complete cases are there?
Exercise 1
## [1] 299.8333
## [1] 73.59189
## [1] 299.5
## [1] 299.83
Exercise 2
Exercise 3
## id condition score
## Min. :1 control :3 Min. :2.8
## 1st Qu.:2 treatment:2 1st Qu.:3.2
## Median :3 Median :3.5
## Mean :3 Mean :3.5
## 3rd Qu.:4 3rd Qu.:3.9
## Max. :5 Max. :4.1
Exercise 4
You now have the building blocks of R: objects, vectors, data frames, factors, and missing values. These are the raw materials you will use in every subsequent tutorial. In the next tutorial we set up RStudio — the environment where you will write and run all of your R code — and install the packages (lavaan, haven, ggplot2, and friends) that we will need from Tutorial 03 onward.