Code
library(readxl)
options(width = 100)This assignment uses the Online Retail dataset from the UCI Machine Learning Repository, which contains transactions from a UK-based e-commerce store between December 2010 and December 2011. The goal is to load the original data reproducibly, select and rename useful columns, and create fields that make cancellations and transaction values easier to analyze.
For this assignment, I plan to use the Online Retail dataset from the UCI Machine Learning Repository. The dataset contains transaction data from a UK-based online retailer and includes information such as invoice number, product code and description, quantity, invoice date, unit price, customer ID, and country.
I chose this dataset because I work with e-commerce data regularly, so I am already familiar with the type of information it contains. I think it will be useful to work with familiar business data while learning how to load, clean, and transform data in R.
For the assignment, I plan to select a smaller set of useful columns, rename some of them to make them easier to understand, and create a field that identifies whether an order was cancelled based on the invoice number.
One data challenge I expect is handling cancelled orders. Any InvoiceNo that starts with C represents a cancellation. If those transactions are not identified and handled correctly before analysis, they could distort sales-related results.
Dataset source: UCI Machine Learning Repository, Online Retail
Dataset: Online Retail dataset
The original dataset is stored in a ZIP archive on UCI’s website. The code below downloads that archive to a temporary directory, extracts the Excel workbook, and loads it into R. This approach avoids relying on a file stored only on my computer.
library(readxl)
options(width = 100)data_url <- "https://archive.ics.uci.edu/static/public/352/online+retail.zip"
zip_file <- tempfile(fileext = ".zip")
data_directory <- tempfile(pattern = "online-retail-")
dir.create(data_directory)
download.file(data_url, zip_file, mode = "wb", quiet = TRUE)
unzip(zip_file, exdir = data_directory)
excel_file <- list.files(
data_directory,
pattern = "[.]xlsx$",
full.names = TRUE
)[1]
online_retail_raw <- read_excel(excel_file)The source data contains 541,909 rows and 8 columns.
names(online_retail_raw)[1] "InvoiceNo" "StockCode" "Description" "Quantity" "InvoiceDate" "UnitPrice"
[7] "CustomerID" "Country"
head(online_retail_raw)# A tibble: 6 × 8
InvoiceNo StockCode Description Quantity InvoiceDate UnitPrice CustomerID Country
<chr> <chr> <chr> <dbl> <dttm> <dbl> <dbl> <chr>
1 536365 85123A WHITE HANGING HEART… 6 2010-12-01 08:26:00 2.55 17850 United…
2 536365 71053 WHITE METAL LANTERN 6 2010-12-01 08:26:00 3.39 17850 United…
3 536365 84406B CREAM CUPID HEARTS … 8 2010-12-01 08:26:00 2.75 17850 United…
4 536365 84029G KNITTED UNION FLAG … 6 2010-12-01 08:26:00 3.39 17850 United…
5 536365 84029E RED WOOLLY HOTTIE W… 6 2010-12-01 08:26:00 3.39 17850 United…
6 536365 22752 SET 7 BABUSHKA NEST… 2 2010-12-01 08:26:00 7.65 17850 United…
The transformed data frame keeps seven useful source columns and gives them consistent, descriptive names. It also adds three fields:
is_cancelled identifies invoices whose number begins with C.line_value_gbp calculates quantity multiplied by unit price.transaction_type provides a readable label for completed purchases and cancellations.online_retail <- data.frame(
invoice_number = as.character(online_retail_raw$InvoiceNo),
product_description = as.character(online_retail_raw$Description),
quantity = as.integer(online_retail_raw$Quantity),
invoice_date = as.POSIXct(online_retail_raw$InvoiceDate),
unit_price_gbp = as.numeric(online_retail_raw$UnitPrice),
customer_id = as.character(online_retail_raw$CustomerID),
country = as.character(online_retail_raw$Country),
stringsAsFactors = FALSE
)
online_retail$is_cancelled <- startsWith(
toupper(online_retail$invoice_number),
"C"
)
online_retail$line_value_gbp <-
online_retail$quantity * online_retail$unit_price_gbp
online_retail$transaction_type <- ifelse(
online_retail$is_cancelled,
"Cancellation",
"Completed purchase"
)The following output confirms the structure of the transformed data frame and summarizes its transaction types. Keeping cancellations clearly labeled is important because their negative quantities and values could otherwise distort sales totals.
str(online_retail)'data.frame': 541909 obs. of 10 variables:
$ invoice_number : chr "536365" "536365" "536365" "536365" ...
$ product_description: chr "WHITE HANGING HEART T-LIGHT HOLDER" "WHITE METAL LANTERN" "CREAM CUPID HEARTS COAT HANGER" "KNITTED UNION FLAG HOT WATER BOTTLE" ...
$ quantity : int 6 6 8 6 6 2 6 6 6 32 ...
$ invoice_date : POSIXct, format: "2010-12-01 08:26:00" "2010-12-01 08:26:00" "2010-12-01 08:26:00" ...
$ unit_price_gbp : num 2.55 3.39 2.75 3.39 3.39 7.65 4.25 1.85 1.85 1.69 ...
$ customer_id : chr "17850" "17850" "17850" "17850" ...
$ country : chr "United Kingdom" "United Kingdom" "United Kingdom" "United Kingdom" ...
$ is_cancelled : logi FALSE FALSE FALSE FALSE FALSE FALSE ...
$ line_value_gbp : num 15.3 20.3 22 20.3 20.3 ...
$ transaction_type : chr "Completed purchase" "Completed purchase" "Completed purchase" "Completed purchase" ...
head(online_retail) invoice_number product_description quantity invoice_date unit_price_gbp
1 536365 WHITE HANGING HEART T-LIGHT HOLDER 6 2010-12-01 08:26:00 2.55
2 536365 WHITE METAL LANTERN 6 2010-12-01 08:26:00 3.39
3 536365 CREAM CUPID HEARTS COAT HANGER 8 2010-12-01 08:26:00 2.75
4 536365 KNITTED UNION FLAG HOT WATER BOTTLE 6 2010-12-01 08:26:00 3.39
5 536365 RED WOOLLY HOTTIE WHITE HEART. 6 2010-12-01 08:26:00 3.39
6 536365 SET 7 BABUSHKA NESTING BOXES 2 2010-12-01 08:26:00 7.65
customer_id country is_cancelled line_value_gbp transaction_type
1 17850 United Kingdom FALSE 15.30 Completed purchase
2 17850 United Kingdom FALSE 20.34 Completed purchase
3 17850 United Kingdom FALSE 22.00 Completed purchase
4 17850 United Kingdom FALSE 20.34 Completed purchase
5 17850 United Kingdom FALSE 20.34 Completed purchase
6 17850 United Kingdom FALSE 15.30 Completed purchase
transaction_summary <- data.frame(
transaction_type = names(table(online_retail$transaction_type)),
row_count = as.integer(table(online_retail$transaction_type)),
row.names = NULL
)
transaction_summary$percent_of_rows <- round(
100 * transaction_summary$row_count / sum(transaction_summary$row_count),
2
)
transaction_summary transaction_type row_count percent_of_rows
1 Cancellation 9288 1.71
2 Completed purchase 532621 98.29
stopifnot(
nrow(online_retail) == nrow(online_retail_raw),
ncol(online_retail) == 10,
all(online_retail$transaction_type %in%
c("Cancellation", "Completed purchase")),
all(online_retail$is_cancelled ==
startsWith(toupper(online_retail$invoice_number), "C"))
)The final data frame is a smaller and more understandable version of the original dataset. The analysis identified 9,288 cancellation records, representing 1.71% of all transaction rows. It retains the transaction, product, customer, date, price, and country information needed for later analysis while explicitly identifying cancelled transactions and calculating each row’s value in British pounds.
As a next step, I could verify how missing customer and product descriptions should be handled, separate valid product sales from returns or data-entry corrections, and aggregate completed revenue by month or country. I would keep cancellations separate from completed purchases so that summaries do not accidentally treat both transaction types as ordinary sales.
Chen, D. (2015). Online Retail [Dataset]. UCI Machine Learning Repository. https://doi.org/10.24432/C5BW33
AI assistance: ChatGPT was used during development of this assignment. The complete interaction transcript is included in ai-transcript.md.
AI tool citation: OpenAI. (2026). ChatGPT (GPT-5.6 Sol) [Large language model]. Accessed August 30, 2026.