This was hands down the messiest set of data that I have attempted to manage. The original dataset contained 74 observations across 191 variables, only about half housed any data. The EPA seems to use the same Excel template for the various types of vehicles it tracks, hence a large amount of unused fuel economy and dinosaur drinking engine specs. The formatting of the CSV was also very strange, the names of all of the variables were found on row seven of the dataset, and at the end of each manufacturer’s section, they added the name of the vehicle to its own row at a fixed column interval.
library(tidyverse)
## Loading tidyverse: ggplot2
## Loading tidyverse: tibble
## Loading tidyverse: tidyr
## Loading tidyverse: readr
## Loading tidyverse: purrr
## Loading tidyverse: dplyr
## Conflicts with tidy packages ----------------------------------------------
## filter(): dplyr, stats
## lag(): dplyr, stats
library(ggrepel)
AKA: Racks on racks of NAs
EV <- read.csv("EVs.csv", header = TRUE, stringsAsFactors = FALSE, na.strings = c("", "NA"))
str(EV)
## 'data.frame': 74 obs. of 191 variables:
## $ X : logi NA NA NA NA NA NA ...
## $ X2010.and.Later.Electric.Vehicles...data.in.EPA.s.Verify.data.base...plus.all.electric.driving.range.information: chr NA NA NA NA ...
## $ X.1 : chr NA NA NA NA ...
## $ X.2 : chr NA NA NA NA ...
## $ X.3 : chr NA NA NA NA ...
## $ X.4 : chr NA NA NA NA ...
## $ X.5 : chr NA NA NA NA ...
## $ X.6 : chr "(Not Confidential)" NA NA NA ...
## $ X.7 : chr NA NA NA NA ...
## $ X.8 : chr NA NA NA NA ...
## $ X.9 : chr NA NA NA NA ...
## $ X.10 : chr NA NA NA NA ...
## $ X.11 : chr NA NA NA NA ...
## $ X.12 : chr "D.Good" NA NA NA ...
## $ X.13 : chr NA NA NA NA ...
## $ X.14 : chr NA NA NA NA ...
## $ X.15 : chr NA NA NA NA ...
## $ X.16 : chr NA NA NA NA ...
## $ X.17 : chr NA NA NA NA ...
## $ X.18 : chr NA NA NA NA ...
## $ X.19 : chr NA NA NA NA ...
## $ X.20 : chr NA NA NA NA ...
## $ X.21 : chr NA NA NA NA ...
## $ X.22 : chr NA NA NA NA ...
## $ X.23 : chr NA NA NA NA ...
## $ X.24 : chr NA NA NA NA ...
## $ X.25 : chr NA NA NA NA ...
## $ X.26 : chr NA NA NA NA ...
## $ X.27 : chr NA NA NA NA ...
## $ X.28 : chr NA NA NA NA ...
## $ X.29 : chr NA NA NA NA ...
## $ X.30 : chr NA NA NA NA ...
## $ X.31 : chr NA NA NA NA ...
## $ X.32 : chr NA NA NA NA ...
## $ X.33 : chr NA NA NA NA ...
## $ X.34 : chr NA NA NA NA ...
## $ X.35 : chr NA NA NA NA ...
## $ X.36 : chr NA NA NA NA ...
## $ X.37 : chr NA NA NA NA ...
## $ X.38 : chr NA NA NA NA ...
## $ X.39 : chr NA NA NA NA ...
## $ X.40 : chr NA NA NA NA ...
## $ X.41 : chr NA NA NA NA ...
## $ X.42 : chr NA NA NA NA ...
## $ X.43 : chr NA NA NA NA ...
## $ X.44 : chr NA NA NA NA ...
## $ X.45 : chr NA NA NA NA ...
## $ X.46 : chr NA NA NA NA ...
## $ X.47 : chr NA NA NA NA ...
## $ X.48 : chr NA NA NA NA ...
## $ X.49 : chr NA NA NA NA ...
## $ X.50 : chr NA NA NA NA ...
## $ X.51 : chr NA NA NA NA ...
## $ X.52 : chr NA NA NA NA ...
## $ X.53 : chr NA NA NA NA ...
## $ X.54 : chr NA NA NA NA ...
## $ X.55 : chr NA NA NA NA ...
## $ X.56 : chr NA NA NA NA ...
## $ X.57 : chr NA NA NA NA ...
## $ X.58 : chr NA NA NA NA ...
## $ X.59 : chr NA NA NA NA ...
## $ X.60 : chr NA NA NA NA ...
## $ X.61 : chr NA NA NA NA ...
## $ X.62 : chr NA NA NA NA ...
## $ X.63 : chr NA NA NA NA ...
## $ X.64 : chr NA NA NA NA ...
## $ X.65 : chr NA NA NA NA ...
## $ X.66 : chr NA NA NA NA ...
## $ X.67 : chr NA NA NA NA ...
## $ X.68 : chr NA NA NA NA ...
## $ X.69 : chr NA NA NA NA ...
## $ X.70 : chr NA NA NA NA ...
## $ X.71 : chr NA NA NA NA ...
## $ X.72 : chr NA NA NA NA ...
## $ X.73 : chr NA NA NA NA ...
## $ X.74 : logi NA NA NA NA NA NA ...
## $ X.75 : chr NA NA NA NA ...
## $ X.76 : chr NA NA NA NA ...
## $ X.77 : chr NA NA NA NA ...
## $ X.78 : chr NA NA NA NA ...
## $ X.79 : chr NA NA NA NA ...
## $ X.80 : chr NA NA NA NA ...
## $ X.81 : chr NA NA NA NA ...
## $ X.82 : chr NA NA NA NA ...
## $ X.83 : chr NA NA NA NA ...
## $ X.84 : chr NA NA NA NA ...
## $ X.85 : chr NA NA NA NA ...
## $ X.86 : chr NA NA NA NA ...
## $ X.87 : chr NA NA NA NA ...
## $ X.88 : chr NA NA NA NA ...
## $ X.89 : chr NA NA NA NA ...
## $ X.90 : chr NA NA NA NA ...
## $ X.91 : chr NA NA NA NA ...
## $ X.92 : chr NA NA NA NA ...
## $ X.93 : chr NA NA NA NA ...
## $ X.94 : chr NA NA NA NA ...
## $ X.95 : chr NA NA NA NA ...
## $ X.96 : chr NA NA NA NA ...
## $ X.97 : chr NA NA NA NA ...
## [list output truncated]
AKA: Dplyr and Base collide
colnames(EV) <- EV[7,]
EV <- EV[-1:-7,]
EV <- EV[,-1]
EV <- EV %>% select(`Mfr Name`, `Range1 - Model Type Driving Range - Conventional Fuel`, Carline, `Drive Desc`, `Carline Class Desc`, `Batt Specific Energy (Watt-hr/kg)`)
EV <- EV %>% na.omit()
newNames <- c("Make", "Range", "Model", "Drive", "Class", "Watt-Hr/Kg")
colnames(EV) <- newNames
Take a peek at what the data looks like.
str(EV)
## 'data.frame': 33 obs. of 6 variables:
## $ Make : chr "BMW" "BMW" "General Motors" "FCA US LLC" ...
## $ Range : chr "81" "114" "238" "84" ...
## $ Model : chr "I3 BEV (60 Amp-hour battery)" "I3 BEV (94 Amp-hour battery)" "BOLT" "500e" ...
## $ Drive : chr "2-Wheel Drive, Rear" "2-Wheel Drive, Rear" "2-Wheel Drive, Front" "2-Wheel Drive, Front" ...
## $ Class : chr "Subcompact Cars" "Subcompact Cars" "Small Station Wagons" "Minicompact Cars" ...
## $ Watt-Hr/Kg: chr "93" "132" "140" "88" ...
## - attr(*, "na.action")=Class 'omit' Named int [1:34] 1 3 4 6 7 9 10 13 16 18 ...
## .. ..- attr(*, "names")= chr [1:34] "8" "10" "11" "13" ...
Headed in the right direction. Time to convert some classes and rename some observations.
EV <- EV %>% mutate_at(vars(Range, `Watt-Hr/Kg`), funs(as.numeric))
EV$Drive[grepl("Front",EV$Drive)] <- "Front"
EV$Drive[grepl("Rear", EV$Drive)] <- "Rear"
EV$Drive[grepl("All", EV$Drive)] <- "AWD"
EV <- EV %>% mutate_at(vars(Drive), funs(as.factor))
EV <- EV %>% distinct(Model, .keep_all = TRUE)
The data should be in decent shape to do something with now…
str(EV)
## 'data.frame': 20 obs. of 6 variables:
## $ Make : chr "BMW" "BMW" "General Motors" "FCA US LLC" ...
## $ Range : num 81 114 238 84 115 124 93 87 59 107 ...
## $ Model : chr "I3 BEV (60 Amp-hour battery)" "I3 BEV (94 Amp-hour battery)" "BOLT" "500e" ...
## $ Drive : Factor w/ 3 levels "AWD","Front",..: 3 3 2 2 2 2 2 2 3 2 ...
## $ Class : chr "Subcompact Cars" "Subcompact Cars" "Small Station Wagons" "Minicompact Cars" ...
## $ Watt-Hr/Kg: num 93 132 140 88 110 ...
…looks good enough to plot.
A plot looking at the different ranges achieved by each car by brand.
ggplot(EV, aes(Make, Range)) +
geom_label_repel(aes(label = paste(Model, Range, sep = ": ")), color = "dodgerblue", size = 2) +
theme_classic() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
All in all, this was a fun dataset to practice cleaning messy data. There were some challenging methods that I hadn’t used before (i.e. mutate_at and .keep_all), and as a bonus, StackOverflow was down for maintenance which was both a blessing and a curse. On top of gaining some new cleaning skills, this is my first time using RMarkdown which I really liked messing with. I didn’t get too deep into ggplot other than a basic plot because I really wanted to focus on getting a useable data frame that could be explored later.
Data can be found at fueleconomy.gov
Check out more non-sense:
Twitter: Randhyllcho
GitHub: Ryan Christensen