Prepared by: Yuxiao Zhang
Maintained by: PUPIL Lab
Last updated: July 2026

Data note. The datasets included with this resource are small demonstration datasets provided for instructional practice. They are intended for learning R syntax, data import, data exploration, and related examples in this handout.

1. Getting Started with R

1.1 What is R?

R is a powerful programming language designed for statistical analysis and graphical representation of data. It is used in various scientific disciplines, including psychology, education, and social sciences.

1.2 Why use R in educational research?

  • Free and open-source: R is completely free and open-source, meaning there are no financial barriers to using it, unlike many commercial statistical software packages such as SPSS, SAS, or Stata.
  • Versatility: R supports a wide range of statistical analyses, from introductory to advanced levels. It can also create customized, and even interactive, visualizations.
  • Educational research packages: Several R packages are particularly useful for educational research, including lavaan for structural equation modeling, lme4 for multilevel modeling or mixed-effects models, meta and robumeta for meta-analysis, and ggplot2 for advanced graphical representations.
  • Reproducibility: R ensures reproducibility and transparency in research through scriptable analysis and the ability to share code, which is crucial for peer review and collaborative projects.

Takeaway: R is not just a tool for statistical analysis, but a gateway to making your research more reproducible, robust, and advanced.

2. Getting Started with RStudio

2.1 RStudio interface

RStudio usually has four panes:

  • Script Editor: Where you write and save your code, like a text editor where you write instructions for R to follow.
  • Console: Where you can type commands and see immediate results, like a calculator where you can input a calculation and see the answer right away.
  • Environment/History Pane: This section keeps track of everything you have created or used in your current R session, such as variables or datasets, like a workspace that shows you everything you have on your desk right now.
  • Plots/Files/Packages/Help Pane: This area has multiple tabs. You can see your graphs, manage your files, install and view packages that help you with your analysis, and get help when you need it.
Screenshot of the RStudio interface showing four main panes: a script editor in the upper left, console in the lower left, environment and history in the upper right, and plots, files, packages, and help in the lower right.

Figure 1. RStudio interface with script editor, console, environment/history, and plots/files/packages/help panes.

The screenshot shows the four main panes learners will use most often: script editor, console, environment/history, and plots/files/packages/help.

2.2 Basic calculations

You can use R like a calculator to run simple calculations.

2 + 3
## [1] 5
3 * 5
## [1] 15
8 / 2
## [1] 4

2.3 Assigning variables

Variables are like named storage boxes where you can keep data. You can assign a value to a variable using the assignment arrow <-.

x <- 5 # Assigning the value 5 to the variable x
x
## [1] 5

Once you have assigned a value, you can use the variable in other operations.

y <- x + 3
y
## [1] 8

2.4 Vectors and indexing

Vectors are like lists that can hold multiple values. You can create a vector to store several numbers at once with the c() function, which concatenates multiple values into a vector.

numbers <- c(1, 3, 5, 7, 9)

sum(numbers)  # Total of all elements in the vector
## [1] 25
mean(numbers) # Average of all elements in the vector
## [1] 5
new_nums <- numbers - mean(numbers)
new_nums
## [1] -4 -2  0  2  4

You can access specific elements in the vector using square brackets [].

numbers[2]      # The second element in the vector
## [1] 3
numbers[1:3]    # The first three elements in the vector
## [1] 1 3 5
numbers[-4]     # All elements except the fourth one
## [1] 1 3 5 9
new_nums2 <- numbers[-c(3, 5)]
new_nums2
## [1] 1 3 7

You can also use conditions to select only the elements that meet certain criteria. Suppose you want all the numbers in numbers that are greater than 5:

numbers > 5          # R checks each element: is it greater than 5?
## [1] FALSE FALSE FALSE  TRUE  TRUE
numbers[numbers > 5] # Elements greater than 5
## [1] 7 9

Outcome: You should now feel comfortable navigating the RStudio environment, running basic commands, assigning variables, and using vectors.

3. Importing and Exploring Data

3.1 Working directory

  • What is a working directory? The working directory tells R where to look for files you want to load, and where to save any files you create.
  • How do you set the working directory? You can set the working directory using Session > Set Working Directory > Choose Directory in RStudio. The keyboard shortcut is Ctrl + Shift + H on Windows.

For this handout, all code assumes that you render or run files from inside the Part_1_Introduction_and_Data_Exploration folder. The data files are stored in the data/ folder.

# Run getwd() interactively if you want to check the full local folder path.
basename(getwd()) # Confirms the project folder name without printing a local path
## [1] "Part_1_Introduction_and_Data_Exploration"
list.files("data")
## [1] "PISA_200.sav"  "PISA_200.xlsx"

3.2 Packages and libraries

Packages in R are like add-ons or extensions. They contain additional functions that make it easier to work with specific tasks, such as reading Excel or SPSS data files.

To use these packages, you need to install them once and then load them into your R session each time you want to use the package.

# install.packages("readxl")
library(readxl) # For reading Excel (.xlsx) files

# install.packages("haven")
library(haven)  # For reading SPSS (.sav) files

Once a package is loaded, you can use the functions it provides. For example, readxl lets you read Excel files, and haven lets you read SPSS files.

3.3 Reading Excel and SPSS files

The workshop materials include both Excel and SPSS versions of a 200-case PISA example dataset.

# Excel file (.xlsx)
pisa_200_excel <- read_excel("data/PISA_200.xlsx")

# SPSS file (.sav)
data_demo <- read_sav("data/PISA_200.sav")

3.4 First look at the data

Use head() to view the first few rows of a dataset and dim() to check how many rows and columns are in the dataset.

head(data_demo)
## # A tibble: 6 × 7
##     Stu_ID Gender     Sch_Type   Math_Overall Math_Reasoning Math_Relation
##      <dbl> <dbl+lbl>  <dbl+lbl>         <dbl>          <dbl>         <dbl>
## 1 84002502 1 [Female] 3 [Public]         342.           268.          199.
## 2 84004336 1 [Female] 3 [Public]         581.           649.          560.
## 3 84001560 2 [Male]   3 [Public]         542.           532.          535.
## 4 84002616 1 [Female] 3 [Public]         462.           405.          348.
## 5 84003837 1 [Female] 3 [Public]         468.           453.          470.
## 6 84005224 1 [Female] 3 [Public]         424.           401.          415.
## # ℹ 1 more variable: Math_Quantity <dbl>
dim(data_demo)
## [1] 200   7

The formatted table below shows the first six rows of data_demo.

Table 1. First six rows of the PISA_200 SPSS dataset.
Stu_ID Gender Sch_Type Math_Overall Math_Reasoning Math_Relation Math_Quantity
84002502 1 3 342.24 268.25 199.36 203.45
84004336 1 3 581.11 648.51 559.53 586.32
84001560 2 3 541.69 531.56 534.86 492.39
84002616 1 3 461.72 405.34 348.19 376.92
84003837 1 3 468.31 453.36 469.87 515.59
84005224 1 3 423.53 400.63 415.30 459.28

The table shows the main identifiers and math score variables used in the examples.

3.5 Exploring continuous variables

Use the $ symbol to select a particular column or variable from your dataset.

summary(data_demo$Math_Overall)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   262.6   407.5   457.4   464.7   524.7   686.9

The summary output shows the minimum, first quartile, median, mean, third quartile, and maximum for math overall scores.

hist(
  data_demo$Math_Overall,
  main = "Histogram of Math Overall Scores",
  xlab = "Math Overall Scores",
  ylab = "Frequency",
  col = "gray70",
  border = "gray20"
)
Histogram showing the distribution of Math Overall scores. Most scores fall between about 350 and 550, with fewer students at the lowest and highest score ranges.

Figure 2. Histogram of Math Overall scores in the PISA_200 dataset.

The histogram shows that most students in this example dataset have math overall scores in the middle of the observed range, with fewer students at very low or very high score values.

3.6 Exploring categorical variables

For categorical variables, use table() to create frequency distributions.

table(data_demo$Gender)
## 
##   1   2 
##  98 102
table(as_factor(data_demo$Gender))    # Convert to descriptive labels
## 
## Female   Male 
##     98    102
table(as_factor(data_demo$Sch_Type))
## 
##          Private independent Private Government-dependent 
##                            8                            1 
##                       Public 
##                          188

The following bar chart displays the gender counts. A bar chart makes category counts easier to compare visually and in text.

gender_counts <- table(as_factor(data_demo$Gender))
barplot(
  gender_counts,
  main = "Distribution of Gender",
  xlab = "Gender",
  ylab = "Frequency",
  col = "gray65",
  border = "gray20"
)
Bar chart with two bars showing that female and male student counts are nearly balanced in the PISA_200 dataset.

Figure 3. Bar chart of gender distribution in the PISA_200 dataset.

The gender distribution is approximately balanced, with similar counts for female and male students.

3.7 Using the pipe and selecting rows and columns

Use the pipeline operator |> to chain multiple operations together.

data_demo$Gender |> as_factor() |> table()
## 
## Female   Male 
##     98    102

Use data[row, col] to select specific rows and columns.

data_demo[1:10, 1:4]
## # A tibble: 10 × 4
##      Stu_ID Gender     Sch_Type   Math_Overall
##       <dbl> <dbl+lbl>  <dbl+lbl>         <dbl>
##  1 84002502 1 [Female] 3 [Public]         342.
##  2 84004336 1 [Female] 3 [Public]         581.
##  3 84001560 2 [Male]   3 [Public]         542.
##  4 84002616 1 [Female] 3 [Public]         462.
##  5 84003837 1 [Female] 3 [Public]         468.
##  6 84005224 1 [Female] 3 [Public]         424.
##  7 84000675 1 [Female] 3 [Public]         423.
##  8 84002233 2 [Male]   3 [Public]         399.
##  9 84002688 1 [Female] 3 [Public]         571.
## 10 84001135 2 [Male]   3 [Public]         403.
female_score <- data_demo[data_demo$Gender == 1, c("Stu_ID", "Math_Overall")]
head(female_score)
## # A tibble: 6 × 2
##     Stu_ID Math_Overall
##      <dbl>        <dbl>
## 1 84002502         342.
## 2 84004336         581.
## 3 84002616         462.
## 4 84003837         468.
## 5 84005224         424.
## 6 84000675         423.

If you want to save a new dataset, use a relative path so the output goes to the current project folder. The commands below are shown as examples and are not evaluated when the handout renders.

write.csv(female_score, "female_score.csv", row.names = FALSE)
write_sav(female_score, "female_score.sav")

3.8 Exercise: create and summarize a subset dataset

We want to create a new dataset that will help us identify students who may need extra support in math. Please do the following:

  1. Create a new dataset that contains only the students whose Math_Overall score is below 400.
  2. From this new dataset, include only the following columns: Stu_ID, Gender, and Math_Overall.
  3. Once you have this subset, answer the following questions:
    • How many students are in this subset?
    • What is the average math score for this group?
    • What is the gender distribution in this group?
low_math <- data_demo[data_demo$Math_Overall < 400,
                      c("Stu_ID", "Gender", "Math_Overall")]

dim(low_math)[1]
## [1] 47
mean(low_math$Math_Overall)
## [1] 350.8397
low_math$Gender |> as_factor() |> table()
## 
## Female   Male 
##     26     21

In this example dataset, the low-math subset contains 47 students, the average math score is about 350.84, and the gender distribution is 26 female students and 21 male students.

3.9 Missing data warning

Use is.na() to detect and filter missing values. Missing values can create unexpected rows in logical subsetting if we do not remove or account for NA values.

school_type_is_private <- data_demo$Sch_Type == 1

# This result includes rows with missing school type values.
data_demo[school_type_is_private, ]
## # A tibble: 11 × 7
##      Stu_ID Gender      Sch_Type       Math_Overall Math_Reasoning Math_Relation
##       <dbl> <dbl+lbl>   <dbl+lbl>             <dbl>          <dbl>         <dbl>
##  1       NA NA          NA                      NA             NA            NA 
##  2 84000262  2 [Male]    1 [Private i…         484.           544.          434.
##  3 84000571  2 [Male]    1 [Private i…         407.           423.          376.
##  4 84002019  2 [Male]    1 [Private i…         548.           594.          582.
##  5 84003371  2 [Male]    1 [Private i…         477.           457.          535.
##  6 84005456  2 [Male]    1 [Private i…         482.           487.          489.
##  7 84007901  1 [Female]  1 [Private i…         645.           564.          605.
##  8       NA NA          NA                      NA             NA            NA 
##  9       NA NA          NA                      NA             NA            NA 
## 10 84003732  1 [Female]  1 [Private i…         427.           481.          461.
## 11 84000399  1 [Female]  1 [Private i…         502.           407.          465.
## # ℹ 1 more variable: Math_Quantity <dbl>
# This version removes missing school type values from the condition.
data_demo[school_type_is_private & !is.na(school_type_is_private), ]
## # A tibble: 8 × 7
##     Stu_ID Gender     Sch_Type         Math_Overall Math_Reasoning Math_Relation
##      <dbl> <dbl+lbl>  <dbl+lbl>               <dbl>          <dbl>         <dbl>
## 1 84000262 2 [Male]   1 [Private inde…         484.           544.          434.
## 2 84000571 2 [Male]   1 [Private inde…         407.           423.          376.
## 3 84002019 2 [Male]   1 [Private inde…         548.           594.          582.
## 4 84003371 2 [Male]   1 [Private inde…         477.           457.          535.
## 5 84005456 2 [Male]   1 [Private inde…         482.           487.          489.
## 6 84007901 1 [Female] 1 [Private inde…         645.           564.          605.
## 7 84003732 1 [Female] 1 [Private inde…         427.           481.          461.
## 8 84000399 1 [Female] 1 [Private inde…         502.           407.          465.
## # ℹ 1 more variable: Math_Quantity <dbl>

Only for illustrative purposes, we remove rows with missing school type values. In practice, other approaches should be considered depending on the research question and missing-data mechanism.

data_demo <- data_demo[!is.na(data_demo$Sch_Type), ]
dim(data_demo)
## [1] 197   7

Outcome: You should now understand how to set your working directory, install and load packages, and read common types of data files into R. You should also feel comfortable viewing and exploring your data using basic R functions.

4. Further Learning

Use these resources to continue learning:

Next Step

After completing Part I, continue to Part II: Data Manipulation, Visualization, and Basic Analysis in R.