WK7 Data Science Books

HTML and JSON

Andre Thomosn

2026-10-15

Introduction

For this assignment I picked three books about the mathematical and statistical foundations of data science. I entered the same book information in an HTML table and a JSON file, then read both files into R to see if the results matched. I used title, authors, year, publisher, and ISBN. All three books have more than one author.

HTML file

I used rvest to read the table from books.html.

Show or hide code
library(rvest)
library(jsonlite)

# Read the local HTML file and select its first table.
html_page <- read_html("books.html")

html_data <- html_page |>
  html_element("table") |>
  html_table()

# Convert the imported table to a regular data frame.
# Use the same column types that will be used for the JSON data.
html_data <- as.data.frame(html_data, stringsAsFactors = FALSE)
html_data$Year <- as.integer(html_data$Year)
html_data$ISBN <- as.character(html_data$ISBN)

# Display the imported HTML records in the report.
knitr::kable(
  html_data,
  col.names = c("Title", "Authors", "Publication Year", "Publisher", "ISBN-13"),
  row.names = FALSE,
  caption = "Books Imported from HTML"
)
Books Imported from HTML
Title Authors Publication Year Publisher ISBN-13
An Introduction to Statistical Learning Gareth James; Daniela Witten; Trevor Hastie; Robert Tibshirani 2021 Springer 9781071614174
Practical Statistics for Data Scientists Peter Bruce; Andrew Bruce; Peter Gedeck 2020 O’Reilly Media 9781492072935
Foundations of Data Science Avrim Blum; John Hopcroft; Ravindran Kannan 2020 Cambridge University Press 9781108485067

JSON file

I used jsonlite to read books.json.

Show or hide code
# Read the array of book objects from the local JSON file.
json_data <- fromJSON("books.json")

# Convert the result to a data frame and standardize its types.
json_data <- as.data.frame(json_data, stringsAsFactors = FALSE)
json_data$Year <- as.integer(json_data$Year)
json_data$ISBN <- as.character(json_data$ISBN)

# Display the imported JSON records in the report.
knitr::kable(
  json_data,
  col.names = c("Title", "Authors", "Publication Year", "Publisher", "ISBN-13"),
  row.names = FALSE,
  caption = "Books Imported from JSON"
)
Books Imported from JSON
Title Authors Publication Year Publisher ISBN-13
An Introduction to Statistical Learning Gareth James; Daniela Witten; Trevor Hastie; Robert Tibshirani 2021 Springer 9781071614174
Practical Statistics for Data Scientists Peter Bruce; Andrew Bruce; Peter Gedeck 2020 O’Reilly Media 9781492072935
Foundations of Data Science Avrim Blum; John Hopcroft; Ravindran Kannan 2020 Cambridge University Press 9781108485067

Comparison

Before comparing the data frames, I put the columns in the same order and made the Year and ISBN columns the same type.

Show or hide code
# Put both data frames in the same column order.
columns <- c("Title", "Authors", "Year", "Publisher", "ISBN")

html_data <- html_data[, columns]
json_data <- json_data[, columns]

# Record the size, structure, and exact-match results.
comparison_summary <- data.frame(
  Check = c(
    "HTML dimensions",
    "JSON dimensions",
    "Column names match",
    "Column classes match",
    "All values and attributes match"
  ),
  Result = c(
    paste(dim(html_data), collapse = " x "),
    paste(dim(json_data), collapse = " x "),
    identical(names(html_data), names(json_data)),
    identical(vapply(html_data, class, character(1)),
              vapply(json_data, class, character(1))),
    identical(html_data, json_data)
  ),
  stringsAsFactors = FALSE
)

knitr::kable(comparison_summary, row.names = FALSE,
             caption = "HTML and JSON Comparison")
HTML and JSON Comparison
Check Result
HTML dimensions 3 x 5
JSON dimensions 3 x 5
Column names match TRUE
Column classes match TRUE
All values and attributes match TRUE

Both data frames have three rows and five columns. The final comparison is TRUE.

A few checks

I also checked for missing values, duplicate rows, and valid ISBN-13 check digits. The ISBN check only tests the number. It does not prove that the book information is correct.

Show or hide code
# Check whether a value follows the ISBN-13 check-digit formula.
validate_isbn13 <- function(isbn) {
  # Remove punctuation so only the 13 digits remain.
  isbn <- gsub("[^0-9]", "", isbn)

  if (nchar(isbn) != 13) {
    return(FALSE)
  }

  # Apply alternating weights of 1 and 3 to the first 12 digits.
  digits <- as.integer(strsplit(isbn, "")[[1]])
  weights <- rep(c(1, 3), 6)
  total <- sum(digits[1:12] * weights)
  check_digit <- (10 - (total %% 10)) %% 10

  # The calculated check digit must equal the last digit.
  check_digit == digits[13]
}

# Count missing cells and duplicated complete rows in each source.
quality_summary <- data.frame(
  Check = c("Missing cells", "Duplicate complete rows"),
  HTML = c(sum(is.na(html_data)), sum(duplicated(html_data))),
  JSON = c(sum(is.na(json_data)), sum(duplicated(json_data)))
)

# Run the ISBN-13 check on each book identifier.
isbn_validation <- data.frame(
  ISBN = html_data$ISBN,
  Valid_ISBN13 = vapply(html_data$ISBN, validate_isbn13, logical(1)),
  stringsAsFactors = FALSE
)

knitr::kable(quality_summary, row.names = FALSE,
             caption = "Missing-Value and Duplicate Checks")
Missing-Value and Duplicate Checks
Check HTML JSON
Missing cells 0 0
Duplicate complete rows 0 0
Show or hide code
knitr::kable(isbn_validation, row.names = FALSE,
             col.names = c("ISBN-13", "Valid ISBN-13"),
             caption = "ISBN-13 Check-Digit Results")
ISBN-13 Check-Digit Results
ISBN-13 Valid ISBN-13
9781071614174 TRUE
9781492072935 TRUE
9781108485067 TRUE

Conclusion

The two files gave me the same data after I fixed the column types and order. There were no missing values or duplicate rows, and the three ISBN numbers passed the check-digit test.

The book details were checked against the publishers’ pages. The HTML and JSON files match, but this comparison only shows that I entered the same values in both formats. It does not prove that every bookstore uses the same ISBN for every format.

Sources and AI use

Book sources

Learning sources

AI assistance

I used ChatGPT and VS Code with GitHub Copilot to review the code and look for errors. I checked the final results by running the R code. The book records came from the publisher and retailer sources listed above.