3  R Syntax & Core Data Structures

Authors
Affiliation

Texas A&M Institute for Genome Sciences & Society (TIGSS) - Bioinformatics Core, Texas A&M University Vashist College of Medicine

Christopher Bottoms

Published

October 7, 2026

Download this Notebook (R-Introductions.qmd)

3.1 R Basics

Congratulations, you have made it through the through the fundamentals of setting up your own personal R environment, and we have hardly done any coding. If you have managed to not fall asleep so far, get ready for the excitement of writing code! Join the dark side of the lab, and feel it’s power (read in a legally distinct and not trademarkable character’s voice)!

3.1.1 Hello, world!

In all seriousness though, coding can be as simple or complex as you make it. For instance, we are going to begin by writing the first program everyone learns in any programming language, printing the Hello, world! statement. We will use the print() function from base R. We will go over each part of it shortly. Begin, typing the following in your R script:

print("Hello, world!")
[1] "Hello, world!"

As you can see the statement “Hello, world!” was output (or printed, hence the function name) below in our R console. We called the print() function which takes as input a string (text), number, or even a variable as input. This print() function is used a lot across many programming languages. You have commonly encountered it in your day-to-day jobs. When coding it is often used to print a status update, error message, warning, etc. to the user as feedback so we aren’t just starting at a static screen wondering if something has happened or we have messed up.

Now we are going to add a layer to our print() code from earlier. What if we wanted to store a message to ourselves, say remember to drink our coffee while it is still hot, and view it later. We achieve this by creating a variable and casting (storing) a value in it using the <- operator.

1note <- "Remember, to drink your coffee, while it is hot!"
print("Here we are going to print our note below:")
2print(note)
1
this line stores our message about our coffee into the variable “note”.
2
then we print it to stdout.
[1] "Here we are going to print our note below:"
[1] "Remember, to drink your coffee, while it is hot!"

Congrats! You have learned two basics in the R programming language, how to call a function print, and how to store a variable with the <- operator. Now we are going to move on to store data in R.

3.1.2 Vector, the simplest way to store multiple data

Above we stored our first data, a single string into the variable note. But what if we wanted to store a list of numbers? In R, a series of data is called a vector (in R we also have list objects, which behave differently but can look similar). We begin a vector using the c() and separating elements in the vector with a comma. Below, let’s create a vector of numbers 1-5.

num_vec <- c(1,2,3,4,5)
print(num_vec)
[1] 1 2 3 4 5

As you can see, we stored a list of the numbers 1-5 in the variable called num_vec. Unlike before, our variable now stored as numbers, specifically as integers. You’ll notice how we dropped the "" marks as we intended to store numbers instead of strings.

However we can mix data types in vectors, lets see what happens when we try to mix numbers and strings:

mix_vec <- c(1, "the", 2, "cow", 3, "moos")
print(mix_vec)
[1] "1"    "the"  "2"    "cow"  "3"    "moos"

3.1.3 Comments

And to help us understand our code later, we add “comments” which start with a hashtag #. When our script runs, comments don’t actually do anything. However, they help document our code so that later on we know what we are reviewing our code, we can see what were thinking when we wrote it.

# Comments can be an entire line, or come at the end of code

# Create a mixed type vector (for a demo)
mix_vec <- c(1, "the", 2, "cow", 3, "moos") # commas separate values

print(mix_vec)
[1] "1"    "the"  "2"    "cow"  "3"    "moos"

3.1.4 DataFrames: R’s Excel Table Data Object

Vectors are very useful and a core object type in R. Many other useful objects are built on top of vectors. However, the most common type of data we deal with in R will be DataFrames/Tibbles. DataFrames (or the modern equivalent Tibbles, used in tidyverse) are the R equivalent of tables in Excel.

Tibbles are not included in base R, like Dataframes are, so here we need to attach the tidyverse package we installed earlier.Copy and run the following code to generate a dataframe:

library(tidyverse)
1
Here we use the base R function data.frame() function to create a dataframe object
2
Here we use the tidyverse tibble() function to create a tibble object.
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.2.1     ✔ readr     2.2.0
✔ forcats   1.0.1     ✔ stringr   1.6.0
✔ ggplot2   4.0.3     ✔ tibble    3.3.1
✔ lubridate 1.9.5     ✔ tidyr     1.3.2
✔ purrr     1.2.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
1df <- data.frame(name=c("A", "B"), value = c(5, 10))
print(df)

2tib <- tibble(name=c("C", "D"), value = c(15, 20))
print(tib)
  name value
1    A     5
2    B    10
# A tibble: 2 × 2
  name  value
  <chr> <dbl>
1 C        15
2 D        20

Now we have two separate frames of data. Next we will show you how we can combine them. This is useful if we have multiple datasets we have loaded individually and now need to combine. Here we will use the dplyr function bind_rows().

combined_df <- bind_rows(df, tib)

3.1.4.1 Accessing Data inside Dataframes/Tibbles

Now let’s take a look on how to access rows, columns, and specific values using base R’s indexing methods. First let’s see how we can view the row and column names of a dataframe:

1print(rownames(combined_df))
2print(colnames(combined_df))
1
rownames() returns a vector of the row index names, or numbers if no names are present
2
colnames() returns a vector of the column names, or numbers if no names are present.
[1] "1" "2" "3" "4"
[1] "name"  "value"

Above we can see that, the row indexes are numbers, and our column names are “name” and “value”. We can use the rownames() and colnames() functions to replace the old labels with new ones if we wanted to as well. Next lets see how we print specific rows:

print(combined_df[1,])
  name value
1    A     5
print(combined_df[3,])
  name value
3    C    15

Here the numbers represent coordinates where the x-value corresponds to row number, and the y-value corresponds to a column number within the dataframe. By leaving the second value empty (after the comma) we print a single row. The same would be true if we switched the numbers around, and we would print a column instead. To access specific data, we use both coordinate values:

print(combined_df[1,2])
[1] 5

3.1.4.2 Importing Tabular/CSV Data

Now that we understand the structure of dataframes/tibbles in R, let’s learn how to import our CSV/TSV/Excel-formatted data files as dataframes into R. Here we are going to leverage read.csv() from base R. Later we will work on data we pull from iLab, for now please downlaod a sample data set from here. The read.csv() has a few parameters. We can conveniently see a quick help guide on any function of interest by using the ? before any function call try it first below:

?read.csv()
1csv_data <- read.csv("data/fy26_costs.csv")
print(csv_data)

2csv_data$Costs
1
Here we only need to pass the path to our file, in some cases, you may need to specify which columns you would like to keep, and/or skip loading into memory.
2
Here is another way we can print entire columns of a dataframe, by accessing the specific column name of the dataframe with $.
       Month Costs  Sales
1   Janurary 20000 100521
2   February 18500 167744
3      March 10203 185467
4      April 16789 125223
5        May  4532  14000
6       June  9000 132450
7       July 42000  67500
8     August 16000   4150
9  September 20245  13252
10   October 25225 140500
11  November  7845  65000
12 Decemeber 14897  42000
 [1] 20000 18500 10203 16789  4532  9000 42000 16000 20245 25225  7845 14897

Here we have an example of a simple monthly breakdown of costs and sales. What if we wanted to quickly calculate gross revenue per month? We can, quickly in fact, and it is as intuitive as using a calculator:

1csv_data$Revenue <- csv_data$Sales - csv_data$Costs
print(csv_data)
1
Here we create a new column with $ and name it Revenue. We then subtract the vector csv_data$Costs from csv_data$Sales.
       Month Costs  Sales Revenue
1   Janurary 20000 100521   80521
2   February 18500 167744  149244
3      March 10203 185467  175264
4      April 16789 125223  108434
5        May  4532  14000    9468
6       June  9000 132450  123450
7       July 42000  67500   25500
8     August 16000   4150  -11850
9  September 20245  13252   -6993
10   October 25225 140500  115275
11  November  7845  65000   57155
12 Decemeber 14897  42000   27103

We have quickly calculated our total revenue for FY26. But this is something that can be readily done in excel with a similar equation and then a click and drag down. However what if we wanted some quick statistics for each of our columns:

summary(csv_data)
       Month        Costs           Sales           Revenue      
 Length   :12   Min.   : 4532   Min.   :  4150   Min.   :-11850  
 N.unique :12   1st Qu.: 9902   1st Qu.: 35000   1st Qu.: 21492  
 N.blank  : 0   Median :16394   Median : 84010   Median : 68838  
 Min.nchar: 3   Mean   :17103   Mean   : 88151   Mean   : 71048  
 Max.nchar: 9   3rd Qu.:20061   3rd Qu.:134462   3rd Qu.:117319  
                Max.   :42000   Max.   :185467   Max.   :175264  

The summary() function take a dataframe or vector as input and quickly output basic statistical analysis of the distribution of the data. You’ll notice that our Month column is a column filled with strings. Instead we get an overview of the length, number of unique identities, number of blank entries, and the minimum and maximum number of characters in a row. All other columns contain numerical values and so we get quick distribution summaries of our data.

As a final example of what we can do with R combining everything we have gone over in this notebook. Lets print a message telling us the sum total of our costs, sales, and revenue numbers for FY26:

print(paste0("Total Costs: $", sum(csv_data$Costs)))
[1] "Total Costs: $205236"
print(paste0("Total Sales: $", sum(csv_data$Sales)))
[1] "Total Sales: $1057807"
print(paste0("Total Revenue: $", sum(csv_data$Revenue)))
[1] "Total Revenue: $852571"

3.1.5 Pipes

Functions in R and many other programming languages are evaluated “inside first”.

This can get to look really messy sometimes, like this:

print(sqrt(sum(sqrt(100),26)))

What is even going on here?

It’s basically the following:

print(sqrt(sum(sqrt(100),26))) # sqrt(100)  gets replaced by 10
print(sqrt(sum(10,26)))        # sum(10,26) gets replaced by 36
print(sqrt(36))                # sqrt(36)   gets replaced by 6
print(6)                       # Prints the answer: 6

So, the first function that we see, reading left-to-right, is actually the last one that runs.

This is where pipes can be helpful. They take their input from the left and put it in as the first argument in the function on their right. Here’s the same code written with the native pipe |>:

sqrt(100) |> sum(26) |> sqrt() |> print()

Or, with each step on its own line:

sqrt(100) |>
 sum(26)  |>
 sqrt()   |>
 print()

Now, reading left-to-right or top-to-bottom, we see things happen in the same order that they are written.

  • take the square root of 100
  • add that result to 26
  • take the square root of that answer
  • and then print it.

Same thing, but with in-line comments:

sqrt(100) |>            # take the square root of 100           # 10
 sum(26)  |>            # add that result to 26                 # 36
 sqrt()   |>            # take the square root of that answer   #  6
 print()                # print the final result

3.1.6 Next: Real world example

Now that we have gotten some exposure on loading, manipulating, and messing around with data in R. Let’s move on to a real-world example in the next section!