Skip to Content
Course content

16: Data Cleaning Basics

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I want to show you a snippet of code that I actually wrote a few years ago. I had just imported a CSV of sales data, and I wanted to find the average order value. It looked straightforward, but R gave me a result that made zero sense.

# My broken code
sales_data <- read.csv("orders.csv")
avg_sale <- mean(sales_data$amount)
print(avg_sale)
# Output: [1] NA

Now, I looked at the CSV in Excel, and the amount column was clearly full of numbers. Why was R returning NA? This is a classic "dirty data" trap. When I ran str(sales_data), I realized the amount column wasn't a numeric type; it was a character vector. Why? Because the CSV had dollar signs and commas in the values (e.g., "$1,200.50"). R saw that "$" and decided the whole column was text.

The Mystery of the NA Average

The problem here is that R won't implicitly guess that "$1,200.50" should be treated as a number. If you try to run a math function on a character string, it fails. In the case of mean(), if there's even one non-numeric value (or if the whole column is the wrong type), you often end up with NA.

To fix this, we have to explicitly clean the string before we can cast it to a number. We need to strip out everything that isn't a digit or a decimal point.

Stripping Strings to Recover Numbers

Here is how I fixed it. I used gsub() to replace the unwanted characters with nothing, and then wrapped that in as.numeric().

# The fix
sales_data$amount <- gsub("[$,]", "", sales_data$amount) # Remove $ and ,
sales_data$amount <- as.numeric(sales_data$amount)      # Convert to number

avg_sale <- mean(sales_data$amount, na.rm = TRUE)
print(avg_sale)
# Output: [1] 1240.50

Notice the na.rm = TRUE inside the mean() function. That's my second "pro tip" for this lesson. Even after you fix the data types, real-world data almost always has missing values. By default, R is conservative: if there's one NA in your vector, the result of the calculation is NA. Adding na.rm = TRUE tells R to just ignore the blanks and calculate based on the available data.

Handling Inconsistent Text Casing

Cleaning isn't just about numbers. I've spent way too many hours debugging scripts only to realize that "New York", "new york", and "NEW YORK" were being treated as three different cities in my group-by summaries. This is a nightmare for data aggregation.

The easiest way to handle this is to force everything to one case immediately after importing. I usually go with lowercase because it's less jarring to look at in logs.

# Using the base R tolower function
sales_data$city <- tolower(sales_data$city)

If you're using the tidyverse, you can do this inside a mutate() call, but the principle is the same: standardize your categories before you start analyzing them, or your counts will be wrong.

Dealing with Whitespace Ghosts

One of the most frustrating bugs in R is the "invisible space." You'll try to filter for a value—filter(df, status == "Complete")—and it returns zero rows, even though you can clearly see "Complete" in the data. Often, that cell actually contains "Complete " (with a trailing space).

I always recommend running a trimws() (trim white space) on any character column that you plan to use for filtering or joining.

# Remove leading and trailing whitespace
sales_data$status <- trimws(sales_data$status)

Once you've handled the types, the NAs, the casing, and the whitespace, you actually have a dataset you can trust. Do that first, or you'll spend the rest of your project chasing ghosts.




📋 Practical Task

Cleaning the Messy Warehouse Inventory List

You've been handed a messy dataframe representing a warehouse inventory. The data is a disaster: the price column has currency symbols, the category column has inconsistent casing, and some stock_count values are missing.

# Run this code to create the messy dataset
inventory <- data.frame(
  item = c("Widget A", "Widget B", "Gadget X", "Gadget Y", "Sprocket Z"),
  category = c("Tools", "tools", "Electronics", "ELECTRONICS", "Tools "),
  price = c("$10.00", "$15.50", "$100.00", "$120.00", "$5.00"),
  stock_count = c(50, 20, NA, 10, 100),
  stringsAsFactors = FALSE
)

Your Goal: Write a script to clean this dataframe so that:

  • The price column is converted to a numeric type.
  • The category column is standardized to lowercase and has no trailing/leading whitespace.
  • You calculate the total value of the inventory (Price * Stock Count), ensuring that the missing stock_count doesn't break your calculation.

Print the final cleaned dataframe and the total inventory value to the console.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.