Introduction to R
Ross McKenzie
2025-08-06
Note: For illustration purposes we will be using the data set retail_clean.csv, which is
available to download from MyDundee.
What is R/RStudio?
R is an open-source programming environment and language, and RStudio is basically an
integrated environment to make R just a bit easier to handle. RStudio has various different
packages that allow us to perform different tasks - we will mainly use those that help us
clean, manipulate, analyse and visualise data.
How do I get RStudio?
Using university computers/servers/remote access: You can download/open RStudio
using AppsAnywhere. Please note, that you also need to download the latest version of R
with it (also from AppsAnywhere).
Using your own laptop If you are working on your own laptop, just download the latest
versions of R and RStudio from [Link] and
[Link] respectively. Once you have downloaded
RStudio, just open by clicking on the RStudio icon in your Windows/Mac menu.
I opened RStudio and it’s all confusing. What do I do?
There are several different ways you can write code/perform tasks within RStudio. We will
use the RMarkdown option. RMarkdown is very useful because it allows you to write
comments next to your code and output, and at the end of each session you can create a
nice and convenient PDF or Word document that summarises all the stuff you worked on.
To open a new markdown file, click on File –> New File –> RMarkdown. A new file will
open. You can just go with the default options but do make sure to name your file. This
contains a short guide on how to use RMarkdown, but feel free to delete anything under
“knitr::opts_chunk$set(echo = TRUE)” and then the . (It is probably best
if you do not delete the part)
Now before we get into any of the more complicated stuff, we need to understand how
RMarkdown works. There are two main types of environments:
1) Text – As long as you are typing within the area with a white background, what you
are typing is just plain text. You can style your text quite nicely in RMarkdown
(useful for assignments), so here is a cheatsheet to help you do that:
[Link]
2) Code – You will mainly use RMarkdown to do some coding, which is a fancy term for
manipulating your data to perform some tasks, such as statistical analysis and
creating data graphics. To perform these tasks, you need to use the ‘coding’
environment within RMarkdown.
To get to this environment, you simply need to click on Insert –> R .
You will find ‘Insert’ near the top right corner (there is a green C with a plus icon next to it).
Once you clicked on R, it will insert a so-called code chunk. This is highlighted by the grey
background environment. Anything you type here is going to be part of your code. Let’s
give it a try.
Setting the working directory
Now this is very important. Setting the working directory means telling RStudio which
folder on your computer it should take stuff (data, files, etc.) from. In our case, we will put
our data in a folder on our computers, this is really up to you which folder you use for this.
We would like to set this folder as our working directory.
So, we click on Insert –> R , a grey coding chunk should appear in Markdown.
setwd("C:/Users/rmckenzie001/Dropbox/Teaching/DataMining/Lectures/
Lecture 1/Lab 1")
To set your working directory, just type setwd(“folder where to you have your data”). Few
things to be mindful of.
1) The folder path is unique for each of you so type (or copy and paste) the folder path
where your assignment data is stored (not mine).
2) R only accepts forward slashes to you might need to change backslashes to forward
slashes in the code.
3) As a shortcut you can press Ctrl-Shift-H. The window should pop-up where you can
navigate to the folder that stores your data. Press open and in the bottom left
window the code will appear and you can copy-paste it.
Once this is all done, just click on the little play button (green triangle) in the top right
corner of your code chunk. If it all turns out green, and there is no error message, then we
were successful in setting the working directory.
Importing the data
Now let’s import the data we will use for this demonstration. As noted earlier, this is the
retail_store_sales.csv file. First we will install and load the package (readxl) we will use for
this. At least we practice how to install and load packages! Insert a code chunk, and type the
following code:
Note: type the [Link] code without the # in front. I only have that there
because I have already installed the package before.
#[Link]("readr")
library("readr")
Note that the ’library(package name) code is enough if the package has already been
installed. Now get the data. Insert another code chunk, type, and click on the wee play
button in the corner of the chunk:
data <- [Link]("retail_clean.csv")
If it was successful, you should see your data in the top right corner, under ‘global
environment’, click there on the data and it should appear in a separate window. You
should be able to see that we have two variables ‘x’ and ‘y’ and various different values for
each.
Note, that your code essentially named the ‘object’ we assigned your data to ‘data’, but you
could have named it anything, that’t the first part of the code, before <-. For example, if you
want to call your data ‘mydata’ then just type:
mydata <- [Link]("retail_clean.csv")
Great, we got the data imported, now let’s see what else we can do. In the meantime, save
your Markdown document using Ctrl+S (or File –> Save As or Save) in an RmD format. You
will always be able to reopen the whole Markdown file.
Also note how you will have to use different commands for different types of data (for
example xlsx) – you can find a cheatsheet for that here
([Link] under Data Import.
Manipulating data with dplyr
When we learn how to clean our data next week we will be making extensive use of the
dplyr package.
So let’s learn the key operations we can use dplyr for, if you want to know everything dplyr
can do then check out the cheatsheet - [Link]
[Link]
Firstly, we must install and load the package
#[Link]("dplyr")
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
Using pipes
Pipes allow us to pass an argument from the LHS to the RHS. 1. The native R pipe is the
following: |> 2. The magrittr pipe is: %>% You may see both as |> was not introduced until
2021, R v4.1.0. For most purposes they work similar to each other.
To turn on the native pipe:
Tools → Global Options → Code → Editing → Use Native Pipe Operator
Here is an example of code that use pipes and code that does not use pipes
##Pipe
data |>
names()
## [1] "[Link]" "[Link]" "Category"
"Item"
## [5] "[Link]" "Quantity" "[Link]"
"[Link]"
## [9] "Location" "[Link]" "[Link]"
## No Pipe
names(data)
## [1] "[Link]" "[Link]" "Category"
"Item"
## [5] "[Link]" "Quantity" "[Link]"
"[Link]"
## [9] "Location" "[Link]" "[Link]"
You can see the output is the same, we see the names of each attribute in the dataset. You
may at this point be thinking, what is the point in this, the code seems more complicated!
This is true for simple functions but as well will see, when applied to more complicated
functions it results in simple, easy to follow code.
Selecting columns
The select() function allows you to choose and extract columns of interest from your data
frame
For example, we may only be interested in the items available. This could be saved as a new
data frame. which we will call items
items <- data |>
select(Category, Item)
We can now apply functions to this more limited dataset
items |>
head()
## Category Item
## 1 Patisserie Item_10_PAT
## 2 Milk Products Item_17_MILK
## 3 Butchers Item_12_BUT
## 4 Beverages Item_16_BEV
## 5 Food Item_6_FOOD
## 6 Patisserie Item_12_PAT
Additionally, we do not always want to save new data for each section of the dataset we
would like to analyse. Here we can use the pipe operator to 1. Select the variables we are
interested in 2. Apply the function
data |>
select(Category, Item) |>
head()
## Category Item
## 1 Patisserie Item_10_PAT
## 2 Milk Products Item_17_MILK
## 3 Butchers Item_12_BUT
## 4 Beverages Item_16_BEV
## 5 Food Item_6_FOOD
## 6 Patisserie Item_12_PAT
Note, if we tried to save this, then R would not save the whole dataset with only the
columns Category and Item, it would save only the output from head()
head_items <- data |>
select(Category, Item) |>
head()
Filtering rows
The filter() function allows you to choose and extract rows of interest from your data frame
(as opposed to columns). The filter function takes in the data frame to filter, followed by a
comma-separed list of conditions that each returned row must satisfy. Let’s say we are only
interested in Beverages
beverages <- data |>
filter(Category == "Beverages")
beverages |>
head()
## [Link] [Link] Category Item [Link]
Quantity
## 1 TXN_9458126 CUST_06 Beverages Item_16_BEV 27.5
9
## 2 TXN_1809665 CUST_14 Beverages Item_14_BEV 24.5
5
## 3 TXN_9939063 CUST_14 Beverages Item_7_BEV 14.0
9
## 4 TXN_6398436 CUST_15 Beverages Item_25_BEV 41.0
7
## 5 TXN_8312797 CUST_14 Beverages Item_11_BEV 20.0
5
## 6 TXN_4223250 CUST_16 Beverages Item_22_BEV 36.5
7
## [Link] [Link] Location [Link]
[Link]
## 1 247.5 Credit Card Online 07/05/2022
NA
## 2 122.5 Credit Card In-store 11/05/2022
NA
## 3 126.0 Digital Wallet In-store 14/01/2024
NA
## 4 287.0 Credit Card In-store 03/06/2024
TRUE
## 5 100.0 Digital Wallet Online 03/06/2022
NA
## 6 255.5 Cash Online 31/05/2023
TRUE
We can add any number of conditions. What if we want only beverages that cost more than
$30 per unit.
beverages_30 <- data |>
filter(Category == "Beverages",
[Link] > 30) #Note: It is good to have each
condition on a separe line for readability
beverages_30 |>
head()
## [Link] [Link] Category Item [Link]
Quantity
## 1 TXN_6398436 CUST_15 Beverages Item_25_BEV 41.0
7
## 2 TXN_4223250 CUST_16 Beverages Item_22_BEV 36.5
7
## 3 TXN_1142481 CUST_02 Beverages Item_22_BEV 36.5
6
## 4 TXN_8210621 CUST_03 Beverages Item_24_BEV 39.5
6
## 5 TXN_3403695 CUST_25 Beverages Item_23_BEV 38.0
8
## 6 TXN_9940030 CUST_25 Beverages Item_19_BEV 32.0
6
## [Link] [Link] Location [Link]
[Link]
## 1 287.0 Credit Card In-store 03/06/2024
TRUE
## 2 255.5 Cash Online 31/05/2023
TRUE
## 3 219.0 Cash In-store 07/02/2023
TRUE
## 4 237.0 Digital Wallet Online 03/01/2024
FALSE
## 5 304.0 Cash Online 27/09/2024
TRUE
## 6 192.0 Credit Card In-store 19/01/2022
TRUE
Creating new variables
The mutute() function allows you to create additional columns for your dataframe. For
example there may be a 5% VAT on all item sales. Let’s create a column that calculates the
tax for each item and save it to our original dataset and then add the tax to the total spent
variable
data <- data |> ## Save new columns to original dataset
mutate(VAT = ([Link])*0.05, ## Add a column called VAT that
is equal to 5% of the price
total_spent_vat = [Link] + VAT) ## Add a column called
total_spent_vat that is [Link] + VAT
Altering existing variables is an easy adjustment. Instead of renaming the variable e.g. as
total_spent_vat = , instead just use the original variable names [Link] =
We may also want to change the variable type. For example let’s check the variable
Category.
data |>
select(Category) |>
summary()
## Category
## Length:12543
## Class :character
## Mode :character
In this scenario, the “Category” variable is known as a ‘string’ . R does not know the group
of items are part of a fixed set of categories. In Data Mining, it often matters whether a
string variable are connected as part of a group (item categories, customer regions etc.) or
not (customer reviews, email addresses)
data <- data |>
mutate(
Category = [Link](Category)
)
data |>
select(Category) |>
summary()
## Category
## Furniture :1591
## Electric household essentials:1582
## Milk Products :1582
## Food :1577
## Butchers :1568
## Beverages :1567
## (Other) :3076
We can now see everything has been grouped appropriately. See the below table for other
common types of transformations we may come across:
Original type Why convert How
character → Categorical data: modeling, mutate(across(where([Link]
factor memory, validation ), [Link]))
character → Real dates/times for time‐series [Link](x, format = "...")
Date / POSIXct plotting or interval arithmetic lubridate::ymd_hms(x)
character → Numbers read in as text parse_number() (readr) or
numeric / (e.g. "1,234" or "42") [Link]()
integer
numeric → Counts, IDs, indices—enforce [Link]()
integer whole‐number semantics
character → Yes/No or True/False flags [Link]() or x == "Yes"
logical
logical → factor Treat TRUE/FALSE as a two‐level factor(x, levels = c(FALSE, TRUE))
Original type Why convert How
categorical variable
Grouping and summarising
The summarise() function will generate new data that contains a ‘summary’ of a column,
computing a single value from multiple observations in that column. Let’s for example find
the mean price and quantity
data |>
summarise([Link] = mean([Link]),
[Link] = mean(Quantity))
## [Link] [Link]
## 1 23.41198 5.524117
However, what if we are interested in the average for each category of item? Here we can
use the group_by() function
data |>
group_by(Category) |>
summarise([Link] = mean([Link]),
[Link] = mean(Quantity))
## # A tibble: 8 × 3
## Category [Link]
[Link]
## <fct> <dbl>
<dbl>
## 1 Beverages 22.6
5.58
## 2 Butchers 25.5
5.48
## 3 Computers and electric accessories 23.6
5.60
## 4 Electric household essentials 23.8
5.46
## 5 Food 23.0
5.54
## 6 Furniture 24.1
5.55
## 7 Milk Products 21.7
5.50
## 8 Patisserie 22.9
5.48
We could now create a new dataset that only provides the average price and quantity of
each category
data_category <- data |>
group_by(Category) |>
summarise([Link] = mean([Link]),
[Link] = mean(Quantity))
data_category |>
head()
## # A tibble: 6 × 3
## Category [Link]
[Link]
## <fct> <dbl>
<dbl>
## 1 Beverages 22.6
5.58
## 2 Butchers 25.5
5.48
## 3 Computers and electric accessories 23.6
5.60
## 4 Electric household essentials 23.8
5.46
## 5 Food 23.0
5.54
## 6 Furniture 24.1
5.55
Obviously not all variables are numerical so we must use other methods, for example - we
may select the most common Item in each Category. These will be explored in more detail
in the next lab.
What if we want to save the average price and quantity by category as a new column in our
original dataset, without losing each individual customer? For this we use mutate() and
ungroup()
data_with_avg1 <- data |>
group_by(Category) |>
mutate(
[Link] = mean([Link])) |>
ungroup()
head(data_with_avg1)
## # A tibble: 6 × 14
## [Link] [Link] Category Item [Link]
Quantity [Link]
## <chr> <chr> <fct> <chr> <dbl>
<int> <dbl>
## 1 TXN_6867343 CUST_09 Patisser… Item… 18.5
10 185
## 2 TXN_3731986 CUST_22 Milk Pro… Item… 29
9 261
## 3 TXN_9303719 CUST_02 Butchers Item… 21.5
2 43
## 4 TXN_9458126 CUST_06 Beverages Item… 27.5
9 248.
## 5 TXN_4575373 CUST_05 Food Item… 12.5
7 87.5
## 6 TXN_7482416 CUST_09 Patisser… Item… 21.5
10 215
## # ℹ 7 more variables: [Link] <chr>, Location <chr>,
## # [Link] <chr>, [Link] <lgl>, VAT <dbl>,
## # total_spent_vat <dbl>, [Link] <dbl>
Hopefully by now you can see the usefulness of Pipe operators!
Data visualisation using RStudio
Let’s do some simple data visualisation on our simple data set. We might want to see the
average price for each category. Let’s use all of our new dplyr knowledge to make this
possible with one code chunk. We will use the package ggplot2. So let’s install this or just
load it if you already have it. We could also just install the package ‘tidyverse’ which should
contain most packages we will need (including dplyr, readr, tidyr and more).
Note, how, since I already installed these packages I just commented out the code for
[Link]. I did this using the # sign, which tells R not to run that specific line of code.
#[Link]("ggplot2")
#[Link]("tidyverse)
library("ggplot2")
## Warning: package 'ggplot2' was built under R version 4.3.3
library("tidyverse")
## Warning: package 'tidyverse' was built under R version 4.3.3
## ── Attaching core tidyverse packages ────────────────────────
tidyverse 2.0.0 ──
## ✔ forcats 1.0.0 ✔ stringr 1.5.0
## ✔ lubridate 1.9.2 ✔ tibble 3.2.1
## ✔ purrr 1.0.1 ✔ tidyr 1.3.0
## ── Conflicts ──────────────────────────────────────────
tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<[Link] to
force all conflicts to become errors
To create our bar chart, add a new code chunk, type, and run:
data |>
group_by(Category) %>% #Group the data by Category
summarise(
avg_price = mean([Link], [Link] = TRUE)) %>% #Calculate the
mean price for each category
ggplot(aes(x = Category, y = avg_price)) + #Using Category on x-
axis and average price on y-axis
geom_col() #Create a bar chart
Great. We got our bar chart, if you don’t like the way it looks, you can customise ho the
graph looks. Here is an example below. This class is not about data visualisation however
so we will leave the pretty graphs here.
data |>
group_by(Category) %>%
summarise(
avg_price = mean([Link], [Link] = TRUE)) %>%
ggplot(aes(x = Category, y = avg_price)) +
geom_col(fill = "steelblue") +
labs(
title = "Average Price per Unit by Category",
x = "Category",
y = "Average Price per Unit"
) +
theme_minimal() +
theme(
[Link].x = element_text(angle = 45, hjust = 1)
)
Looks neat. Here is a cheatsheet for ggplot2 by the way:
[Link]
Creating your RMarkdown document
OK, now we are at the end of this tutorial. Let’s create a Markdown document in Word. This
should have all the things we generated here (text, code, and output). Click on the little
arrow next to Knit and select Knit to Word. Once your Word document loads you can save
that separately as well but make sure you keep saving the Markdown file.
Tidier output with RMarkdown
Now you may have knit your RMarkdown document and realised that some of the output is
too large, or unnecessary to include in our final word document, so how can we knit our
document together while picking and choosing what appears in our knitted document?
This can be done by altering the code in our code chunks. We will be doing this regularly
throughout the labs, but this is also important for your Data Mining Report, which will have
a page limit of 12, so being selective with output is important, while still showing all the
code used to replicability in your final report.
So let’s have a look at these options
##```{r echo=T, results='hide', error=FALSE, warning=FALSE,
message=FALSE}
##```
• echo = TRUE
– Prints the R code in your output document. Set to FALSE if you want to run
the code but hide it (show only the results) [NOTE: For assessment purposes,
we should always set echo = TRUE].
• results = ‘hide’
– Suppresses any printed output (the value returned by the last expression).
You may want to use this when you are exploring the data, where
summarising many variable may create large outputs that would be better
summarised in one or two sentences.
• error = FALSE
– Stops errors from appearing in your knitted document. If an error occurs,
knitting will fail unless you also set eval = FALSE. You can set error = TRUE to
show errors inline (often useful when you’re demonstrating debugging). This
should be less useful, if your code is failing then you will need to fix it before
submitting your assessment.
• warning = FALSE
– Hides any R warnings generated by that chunk. Set warning = TRUE if you
want to expose them. Not all warnings in R need paying attention to. If it does
then fix it, if not then we do not need the warning printed in the final
document. This will be commonly used when loading in different libraries.
For example, libraries that were last updated in a previous version of RStudio
will warn you of this but for the libraries we use this is unlikely to cause any
issues.
• message = FALSE
– Suppresses messages (e.g. those from package startup or from
readr::read_csv()). Again, useful to know when running your analysis, but
less useful to include in your assessment.
That’s it for now, hope you will enjoy RStudio!
Any questions, please email me at rmckenzie001@[Link]