print("Hello, world!")[1] "Hello, world!"
Download this Notebook (R-Introductions.qmd)
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)!
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.
[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.
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"
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)── 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
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)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:
[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
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:
$.
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)$ 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"
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.
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
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!
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.