Handling Missing Values & Outliers
Handling Missing Values & Outliers
Data preparation is the very first thing that you do and spend a lot of time on as a data analyst much before
trying to build predictive models using that data.
In essence data preparation is all about processing data to get it ready for all kinds of analysis. All industry
data collection is mostly driven by business process at front , not by the needs of predictive models. These
various processes at some or the other point become reason for introduction of errors here and there in the
data.
There can be many kind of reasons [not necessarily errors ] for which we’d need to pre process our data and
change it for better.
• Missing data
• Potentially incorrect data
• Need for changing form of the data
We’ll discuss various reasons and methods to achieve our pre-processing goals going forward.
You’ll figure out that treatment of both missing values and outliers can at times be very similar. Reason being
, both kind of observations are basically not in a state to be used because of missing/ or miss information.
This is the most common method in the industry. Reason being that missing values are generally a very
very small chunk of the data that you deal with. However you need to keep following things in mind while
removing the observations because of missing data:
1. If observations with missing values are significant chunk of the data then you should not drop all
observations with missing values
2. If the variable which had missing values has entered in your model, you need to plan what to do when
you encounter missing values in the unseen data while model has been put in production.
• Imputing [filling up] missing values with mean/median/mode of the respective variables.
Many at times , we know what a missing value might mean in the context of business process. For example,
If account balance is missing for the bank account , it might mean that the account balance is zero.
1
Treatment of Outliers:
There are two issues with including outliers in the predictive analysis
1. Because of otuliers , the predictor variables ranges get inflated artificially . The model that you get
might not be applicable across that range
2. Some outliers have high leverage in context of the modelling process. In presence of such observations
you’ll get a model which is not a good fit for the general population [data].
If you are preparing data for predictive modelling , you need to remove outliers. However if the variable with
outliers is present in the model, you need to figure out what to do when you encounter outlier values in the
unseen data while model has been put in production.
• Flooring/Capping
In some cases it might make sense to impute outlying values with upper and lower limits when they exceed
either of these values. Imputing with lower limit is called flooring and imputing with upper limit is called
capping.
Many at times , we know what an outlier value might mean in the context of business process.
Transforming and extracting information from the existing data Consider a simple transaction
date and time column for an eCommerce website. A simple column containing dates will not be of much use
but a lot of information can be extracted from this simple looking data. E.g. : Information regarding gaps
between transactions, number of transactions happening every week or day or month etc.
Collapsing and Summarising Data: Many at times we need to collapse data based on some grouping
variables [This is more or less same as what we discussed in univariate statistics]. E.g. Finding out monthly
summary of the data from a daily transaction data. In addition to tools which we learned in Univariate
Statistics module we will learn few new things in the “Data Prep with R” section.
Reshaping Data This is one of the very useful procedures we’ll learn here. Below given is an example of
long data
2
sometimes it’d make sense to this kind of the data into a wide format .Below given is an example of same
data in a wide format.
We’ll learn how to achieve the same and more with tidyr package in R.
Reading Data to R
Reading data in R is fairly simple. We’ll be looking at function [Link] which helps you in reading data
from the flat file formats. Flat files are files which you can open in a simple notepad and view the data. Excel
files or any other proprietry data file format is NOT a flat file and can not be read using [Link]. Each
properietry data file format has dedicated packages and associated functions for them . For example Excel
files can be read using function [Link] found in the packaga xlsx
function [Link] comes with a lot options which you can see in the documentation. You dont need to
pass values to all those options most of the time and set defaults work alright. However we are going to
discuss few of them which you might use from time to time.
file : this is the name of the file to be read. In case file is in your working directory , only the file name is
enough. If it is not in your working directory, you need to include entire path to the folder where file is along
with the file name.
header : This is by default set to FALSE, if you set it to true, variable names are read and assigned from
the first row of the file
sep : This is set to blank by default. This tells R what symbol separates different columns in a row. For
example a comma separated file has “,” as separator
[Link] : You can pass a vector of row names if you want to set row names for your data
[Link] : In case you want to force some other variable names you can pass those names as a vector to
this option. Length of this vector or number of names that you are passing should match with number of
columns
stringsAsFactors : This is be default set to TRUE, you should always set this to FALSE while reading a
flat file. What setting this to FALSE does that it imports character columns as character columns. You can
later convert them to factors if you want after pre processing the data.
[Link] : This is by default set to “NA”. This means that any value which is written as “NA” will be
assigned a missing/NA after reading. You can change this to other strings as well.
colClasses : By default this is set to NA or no forced classes. However you can pass a vector to force classes
on the incoming columns. Without forcing , a column which contains only numbers or NA strings will be
read as numeric. Column which contains even a single character value will be read as character [ given that
you have set stringsAsFactors to FALSE , otherwise it’ll be stored as factors]
nrows : By default it is set to -1 which means all the rows from the file will be read. You can restrict that
by passing a number smaller than the number of rows in file.
skip : By default this is set to 0, by assigning some number you can force R to skip first few rows of the file.
3
We will skip discussion on rest of the rarely used options. Lets look at one example. We’ll be using function
[Link]. It is same as [Link] , just that sep is set to “,” by default.
Remember if you are going to pass just file name you need to set your working directory to the folder which
contains the file. You can do this by using function setwd. This is short for setting working directory.
setwd(" Here/Goes/Path/To/Your/Data/Folder/")
Also note that if you are working on a windows machine, you’d need to replace all “" in your path with”/" or
“\”.
you can check what is your current working directory by typing in getwd()
getwd()
We are going to import data file [Link] here. Lets begin, we’ll start with passing just the file name
and let all other be option take their defaults. We’ll change some as we come across issue with the imported
data.
bd=[Link]("[Link]")
head(bd,2)
## [Link]
## 1 58;management;married;tertiary;no;2143;yes;no;unknown;5;may;261;
## 2 44;technician;single;secondary;no;29;yes;no;unknown;5;may;151;
you can see that we have been fooled by the file extension and assumed that the separator for the data is
comma where as in reality it is “;”. Lets tell that to R by using option sep.
bd=[Link]("[Link]",sep=";")
head(bd,2)
library(dplyr)
glimpse(bd)
## Observations: 45211
## Variables:
## $ age (int) 58, 44, 33, 47, 33, 35, 28, 42, 58, 43, 41, 29, 53, ...
## $ job (fctr) management, technician, entrepreneur, blue-collar, ...
## $ marital (fctr) married, single, married, married, single, married,...
## $ education (fctr) tertiary, secondary, secondary, unknown, unknown, t...
## $ default (fctr) no, no, no, no, no, no, no, yes, no, no, no, no, no...
## $ balance (int) 2143, 29, 2, 1506, 1, 231, 447, 2, 121, 593, 270, 39...
4
## $ housing (fctr) yes, yes, yes, yes, no, yes, yes, yes, yes, yes, ye...
## $ loan (fctr) no, no, yes, no, no, no, yes, no, no, no, no, no, n...
## $ contact (fctr) unknown, unknown, unknown, unknown, unknown, unknow...
## $ day (int) 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5...
## $ month (fctr) may, may, may, may, may, may, may, may, may, may, m...
## $ duration (int) 261, 151, 76, 92, 198, 139, 217, 380, 50, 55, 222, 1...
## $ campaign (int) 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...
## $ pdays (int) -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ...
## $ previous (int) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ poutcome (fctr) unknown, unknown, unknown, unknown, unknown, unknow...
## $ y (fctr) no, no, no, no, no, no, no, no, no, no, no, no, no,...
you can see that all of our character columns have been stored as factors. This needs to be avoided. And we
can do so by using option stringsAsFactors. Best of us make mistake by misspelling that option. Dont get
frustrated. It’ll become a parctice to make mistake, realise and correct.
bd=[Link]("[Link]",sep=";",stringsAsFactors = FALSE)
glimpse(bd)
## Observations: 45211
## Variables:
## $ age (int) 58, 44, 33, 47, 33, 35, 28, 42, 58, 43, 41, 29, 53, ...
## $ job (chr) "management", "technician", "entrepreneur", "blue-co...
## $ marital (chr) "married", "single", "married", "married", "single",...
## $ education (chr) "tertiary", "secondary", "secondary", "unknown", "un...
## $ default (chr) "no", "no", "no", "no", "no", "no", "no", "yes", "no...
## $ balance (int) 2143, 29, 2, 1506, 1, 231, 447, 2, 121, 593, 270, 39...
## $ housing (chr) "yes", "yes", "yes", "yes", "no", "yes", "yes", "yes...
## $ loan (chr) "no", "no", "yes", "no", "no", "no", "yes", "no", "n...
## $ contact (chr) "unknown", "unknown", "unknown", "unknown", "unknown...
## $ day (int) 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5...
## $ month (chr) "may", "may", "may", "may", "may", "may", "may", "ma...
## $ duration (int) 261, 151, 76, 92, 198, 139, 217, 380, 50, 55, 222, 1...
## $ campaign (int) 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...
## $ pdays (int) -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ...
## $ previous (int) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ poutcome (chr) "unknown", "unknown", "unknown", "unknown", "unknown...
## $ y (chr) "no", "no", "no", "no", "no", "no", "no", "no", "no"...
So thats taken care of , big relief. Next lets look at what values our variable job takes.
table(bd$job)
##
## admin. blue-collar entrepreneur housemaid management
## 5171 9732 1487 1240 9458
## retired self-employed services student technician
## 2264 1579 4154 938 7597
## unemployed unknown
## 1303 288
you can see that there are 288 observations where the value is unknown , if you want you can set it to missing
by using option [Link]. But remember this will set the value unknown as missing for all the columns. If
you want to do it only for one of columns then do that after you have imported the data.
5
bd=[Link]("[Link]",sep=";",stringsAsFactors = FALSE,[Link] = "unknown")
sum([Link](bd$job))
## [1] 288
You can see that , now column job has 288 missing values. This was to show you how to use option [Link].
In general it is not a good practice to set any random value as missing . So , for practice its alright, but
dont set unknown to missing in general unless you have good reason to do so. In fact in many of the cases of
categorical variables , unknown itself can be taken as a valid category as you’ll realise later.
We dont need to change default values of other options for this importing. Same will be the case for you as
well for most of the data. If it is not , feel free to use any of the option described above.
Apply Functions
We’ll start with discussion on apply family of function which are very handy way to summarize as well as do
other operations collectively on your data. Lets say we want to get means of all columns present in the data
mtcars. We could achieve that writing a for loop across columns with function mean.
for(i in 1:ncol(mtcars)){
print(mean(mtcars[,i]))
}
## [1] 20.09062
## [1] 6.1875
## [1] 230.7219
## [1] 146.6875
## [1] 3.596563
## [1] 3.21725
## [1] 17.84875
## [1] 0.4375
## [1] 0.40625
## [1] 3.6875
## [1] 2.8125
Fine you get the result, but its not in a very convenient format and code is not pretty. What if there exists a
function which lets us do this without writing these loops and having to go through managing iterations for a
function. This is such a common scenario in data processing, R has a family of dedicated funcitons for this.
We’ll first talk about lapply. This lets you apply a function repeatedly on a list/vector , the outcome is a
list of results. Lets see an example.
x=round(rnorm(10),2)
x
## [1] 0.21 -0.61 1.38 -1.49 0.36 0.39 -0.28 0.71 0.28 2.55
lapply(x,log10)
## [[1]]
## [1] -0.6777807
##
6
## [[2]]
## [1] NaN
##
## [[3]]
## [1] 0.1398791
##
## [[4]]
## [1] NaN
##
## [[5]]
## [1] -0.4436975
##
## [[6]]
## [1] -0.4089354
##
## [[7]]
## [1] NaN
##
## [[8]]
## [1] -0.1487417
##
## [[9]]
## [1] -0.552842
##
## [[10]]
## [1] 0.4065402
But above operation can be easily achieved using vector operations. Infact a simple log(x) will give you the
same result and in a much usable format as well. But then what good is this lapply thing? Lets put it to a
better use and with more options. How about , if you had a lot of text files in your folder and you wanted to
import them all. One solution will be to write one line of [Link] for all these files or may be you can run
a loop. Better you could pass all those names to function [Link] using lapply. Lets see
See, we can pass other common options to function [Link] in lapply. File names in the object file_names
are passed one by one to function [Link]. The output files is simply a list of data frames. If you want
to combine them, you can do so by using function [Link].
file=[Link](rbind,files)
[Link] here passes all the elements in the second argument to function mentioned in first argument.
In just three simple lines of code , we have read data from 50+ files and combined it into one. In just three
lines! All thanks to lapply. At times the output in form of a list becomes difficult to handle. You can use
sapply in these cases. sapply works exactly like lapply, only difference being, it tries to vectorise output of
lapply [if it is possible].
7
sapply(x, log)
Coming back to our first problem of getting mean for all columns . Yes you can use lapply, because data
frames are nothing but list of vectors. There is another function in apply family named apply which provides
better output and a little more funcitonality when you want to apply function iteratively on data frame
elements.
apply(mtcars,2,mean)
The first argument to apply is the name of the data frame. 3rd Argument is the function which is going to
get applied. Second argument takes two values : [1 or 2]. 1 stands for rows and 2 for columns. Which means,
if you put 2 in the second argument , function in the 3rd argument will be applied on the columns of the
data frames. If the value is given as 1, function gets applied on rows. But it seems kind of odd functionality
to provide, when will we really need to apply a function across rows? lets see an example. Here is a data
which tells temperature recorded for a month , thrice a day.
## days T1 T2 T3
## 1 1 26 20 27
## 2 2 22 24 20
## 3 3 28 22 22
## 4 4 28 24 27
## 5 5 22 23 26
## 6 6 29 28 27
How to add a column to data with max temperature for the day?
temps$max_temp=apply(temps,1,max)
head(temps)
## days T1 T2 T3 max_temp
## 1 1 26 20 27 27
## 2 2 22 24 20 24
## 3 3 28 22 22 28
## 4 4 28 24 27 28
## 5 5 22 23 26 26
## 6 6 29 28 27 29
Functions whic you pass to apply are not limited to pre existing function in R. You can write your own and
pass it to apply family functions. Lets write a function which returns uuper limit of outliers given a variable
column [vector]. µ + 3 ∗ σ
8
outlier_upper=function(x){
m=mean(x);
s=sd(x);
return(m+3*s);
}
apply(mtcars,2,outlier_upper)
you can even write on the fly functions. What you need to remember here is what goes as input to the
function. In case of apply , input is the entire row or column. Lets use apply to find out how many outliers
each column has according to function oulier_upper.
apply(mtcars,2,function(x) sum(x>outlier_upper(x)))
This is all good, but what if i want to get a group wise summary of any variable. tapply comes to your
rescue. First arguement to tapply is the column for which we are looking for summary, second argument is
the grouping variable, 3rd argument is the function which will be applied on the groups.
tapply(mtcars$mpg,mtcars$am,mean)
## 0 1
## 17.14737 24.39231
For getting group wise summary of all the variable in the dataset mtcars you can use a combination of apply
and tapply.
apply(mtcars,2,function(x) tapply(x,mtcars$am,mean))
I am leaving function mapply for you to explore on your own. ### Useful function for Data Prep: ifelse
Creating variables/vectors with simple algebraic operations is straight forward. Just as a recap lets add one
variable to data frame Arthritis.
library(vcd)
Arthritis$new=log(Arthritis$Age)
head(Arthritis)
9
## ID Treatment Sex Age Improved new
## 1 57 Treated Male 27 Some 3.295837
## 2 46 Treated Male 29 None 3.367296
## 3 77 Treated Male 30 None 3.401197
## 4 17 Treated Male 32 Marked 3.465736
## 5 36 Treated Male 46 Marked 3.828641
## 6 23 Treated Male 58 Marked 4.060443
What if we wanted to create an indicator variable which takes value 0 or 1 according to Age being less than
or greater than 40? These simple algebraic operations will not work. We’ll have to use conditional operators.
Arthritis$new=[Link](Arthritis$Age<40)
head(Arthritis)
This seems trivial too, now what if we want Age to be floored to 40 whenever it is less than 40 and other
wise kept as it is. We wont be able to achieve this with simple conditional statement either. We will be using
function ifelseto achieve the same.
x=sample(40,10)
x
## [1] 19 11 24 21 38 36 33 22 25 1
y=ifelse(x>20,20,x)
y
## [1] 19 11 20 20 20 20 20 20 20 1
Arthritis$new=ifelse(Arthritis$Age<40,40,Arthritis$Age)
head(Arthritis)
Another function that i wanted to discuss here is lag, this can be used to create lag counter parts to your
data. Look at this monthly sales data.
10
## months sales
## 1 1 1813
## 2 2 1040
## 3 3 1731
## 4 4 1409
## 5 5 1577
## 6 6 1059
## 7 7 1280
## 8 8 1480
## 9 9 1926
## 10 10 1281
## 11 11 1504
## 12 12 1282
If you were asked to get month on month growth you can achieve it by differencing with lag counter part of
sales. Lets see how to do it.
We have seen ways to modify and summarise data in base R. Again those functionalities are kind of scattered
and not streamlined. If you think about it you can achieve almost all kind of modifications to data using
these verbs:
Package dplyr comes with these verbs for data wrangling. Next we’ll see how to achieve different data
wrangling task in base R and the same in dplyr. Of course dplyr comes with some addtional fuctionlaities
too and we’ll be looking at those as well. Before you start , install packages dplyr and hflights. We’ll be
using data set hflights. You can get details of the data hflights after you have loaded library hflights
by typing ?hflights.
library(dplyr)
library(hflights)
data(hflights)
head(hflights)
11
## 5427 428 N403AA 70 39 3 3 IAH
## 5428 428 N492AA 62 44 -3 5 IAH
## 5429 428 N262AA 64 45 -7 -1 IAH
## Dest Distance TaxiIn TaxiOut Cancelled CancellationCode Diverted
## 5424 DFW 224 7 13 0 0
## 5425 DFW 224 6 9 0 0
## 5426 DFW 224 5 17 0 0
## 5427 DFW 224 9 22 0 0
## 5428 DFW 224 9 9 0 0
## 5429 DFW 224 6 13 0 0
We’ll start with our first function tbl_df which converts a [Link] to a tabular format for which display
on console is better. It changes nothing else about the data frame
flights=tbl_df(hflights)
flights
Lets look at condition filtering of the data. We’ll start with base R approach to view all flights on January 1
12
## 10 2011 1 1 6 1639 2110 B6 622
## .. ... ... ... ... ... ... ... ...
## Variables not shown: TailNum (chr), ActualElapsedTime (int), AirTime
## (int), ArrDelay (int), DepDelay (int), Origin (chr), Dest (chr),
## Distance (int), TaxiIn (int), TaxiOut (int), Cancelled (int),
## CancellationCode (chr), Diverted (int)
dplyr approach:
13
# you can also use %in% operator
filter(flights, UniqueCarrier %in% c("AA", "UA"))
See, you don’t need to bother with that $ reference to data frame all the time. Code is much neater and
readable. Lets look at column selection dropping by name. You’ll be definietly pleseantly surprised by the
additional functionalities by dplyr.
# dplyr approach
select(flights, DepTime, ArrTime, FlightNum)
14
## 3 1352 1502 428
## 4 1403 1513 428
## 5 1405 1507 428
## 6 1359 1503 428
## 7 1359 1509 428
## 8 1355 1454 428
## 9 1443 1554 428
## 10 1443 1553 428
## .. ... ... ...
Use colon to select multiple contiguous columns, and use contains to match columns by name note:
“starts_with”, “ends_with” can also be used to match columns by name
you can drop variable by simply putting a - sign in front of the variable name.
Now what if we wanted to do many operations at once; for example, selction and conditional filtering. We
can do so by nesting our functions.
15
This nesting methodology becomes very cumbersome very fast . This defies the purpose with which we
started , making our code more readable. Comes to your rescue %>% operator , also called chaining operator.
You can read it as then. Basically when you use this operator , every subsequent line of code inherits inputs
from the previous line. You’ll be able to better understand this with the following example. Later on we’ll
rewrite the above nested code with the chaining operator.
x=sample(10,6)
x %>%
log() %>%
sum()
## [1] 9.15377
See, you don’t have to pass any input to those functions , x goes as input to log and then modified x as
log(x) goes as input to sum. Lets see how we can use this to rewrite the nested function that we saw above.
# chaining method
flights %>%
select(UniqueCarrier, DepDelay) %>%
filter(DepDelay > 60)
See , no need to nest or keep on giving data reference for every operation. Isn’t that neat!!
Next we move to ordering/sorting our data by using verb arrange.
16
## 6 EV -18
## 7 XE -17
## 8 CO -17
## 9 XE -17
## 10 MQ -17
## .. ... ...
# dplyr approach
flights %>%
select(UniqueCarrier, DepDelay) %>%
arrange(DepDelay)
# dplyr approach
flights %>%
select(Distance, AirTime) %>%
mutate(Speed = Distance/AirTime*60)
17
## Source: local data frame [227,496 x 3]
##
## Distance AirTime Speed
## 1 224 40 336.0000
## 2 224 45 298.6667
## 3 224 48 280.0000
## 4 224 39 344.6154
## 5 224 44 305.4545
## 6 224 45 298.6667
## 7 224 43 312.5581
## 8 224 40 336.0000
## 9 224 41 327.8049
## 10 224 45 298.6667
## .. ... ... ...
What we have been doing is getting the output to display, if you wanted to save it could do as we usually do
in R. Say, we wanted to save above output to some data frame.
flight_sub=flights %>%
select(Distance, AirTime) %>%
mutate(Speed = Distance/AirTime*60)
We are done with data wrangling without collapsing it. Next we look at exactly that, summarising data by
groups or collapsing data to its group wise summaries using dplyr.
This pretty much finishes our discussion on dplyr verbs and adverbs. I have given few more examples to
learn new useful functionalities which we havent introduced yet.
# for each day of the year, count the total number of flights
# and sort in descending order
z=flights %>%
18
group_by(Month, DayofMonth) %>%
summarise(flight_count = n()) %>%
arrange(desc(flight_count))
19
table() %>%
head()
## Cancelled
## Dest 0 1
## ABQ 2787 25
## AEX 712 12
## AGS 1 0
## AMA 1265 32
## ANC 125 0
## ASE 120 5
20
## 9 9 18065 -2111
## 10 10 18696 631
## 11 11 18021 -675
## 12 12 19117 1096
## Observations: 227496
## Variables:
## $ Year (int) 2011, 2011, 2011, 2011, 2011, 2011, 2011, 20...
## $ Month (int) 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,...
## $ DayofMonth (int) 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1...
## $ DayOfWeek (int) 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6,...
## $ DepTime (int) 1400, 1401, 1352, 1403, 1405, 1359, 1359, 13...
## $ ArrTime (int) 1500, 1501, 1502, 1513, 1507, 1503, 1509, 14...
## $ UniqueCarrier (chr) "AA", "AA", "AA", "AA", "AA", "AA", "AA", "A...
## $ FlightNum (int) 428, 428, 428, 428, 428, 428, 428, 428, 428,...
## $ TailNum (chr) "N576AA", "N557AA", "N541AA", "N403AA", "N49...
## $ ActualElapsedTime (int) 60, 60, 70, 70, 62, 64, 70, 59, 71, 70, 70, ...
## $ AirTime (int) 40, 45, 48, 39, 44, 45, 43, 40, 41, 45, 42, ...
## $ ArrDelay (int) -10, -9, -8, 3, -3, -7, -1, -16, 44, 43, 29,...
## $ DepDelay (int) 0, 1, -8, 3, 5, -1, -1, -5, 43, 43, 29, 19, ...
## $ Origin (chr) "IAH", "IAH", "IAH", "IAH", "IAH", "IAH", "I...
## $ Dest (chr) "DFW", "DFW", "DFW", "DFW", "DFW", "DFW", "D...
## $ Distance (int) 224, 224, 224, 224, 224, 224, 224, 224, 224,...
21
## $ TaxiIn (int) 7, 6, 5, 9, 9, 6, 12, 7, 8, 6, 8, 4, 6, 5, 6...
## $ TaxiOut (int) 13, 9, 17, 22, 9, 13, 15, 12, 22, 19, 20, 11...
## $ Cancelled (int) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ CancellationCode (chr) "", "", "", "", "", "", "", "", "", "", "", ...
## $ Diverted (int) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ Speed (dbl) 336.0000, 298.6667, 280.0000, 344.6154, 305....
As moving slowly towards predictive modelling , you’d need to take randome sample from your data for
different purposes. You can achieve that in a rather simple manner by using the function sample which we
have used a lot so far. Here goes an example for taking 70% random data from data frame mtcars.
[Link](1)
s=sample(1:nrow(mtcars),0.7*nrow(mtcars))
# we are using [Link] for our random sample to be reproducible
You can now use this vector s as row index vector to take sample data from the data frame.
mtcars_sample=mtcars[s,]
How do I get the rest of the observations which are not in sample taken above? simple:
mtcars_remaining=mtcars[-s,]
How to randomly bootstrap your data? Again, you can achieve that by using sampling with replacement
with function sample
[Link](1)
s=sample(1:nrow(mtcars),100,replace = TRUE)
mtcars_bootstrapped=mtcars[s,]
Reshaping your data with tidyr requires two simple tasks gather and seperate. It also has counterparts to
these which are spread and unite. Lets look at few examples to understand what we can achieve with this.
Look at this data. This data correspond to three peopel being given two different drugs and then their heart
rate being recorded. heart rate corresponding to each drug is put in their respective columns named a and b:
## name a b
## 1 Wilbur 67 56
## 2 Petunia 80 90
## 3 Gregory 64 50
Now what if you want all the heart rates , instead of being spread across multiple columns, gathered into a
single column. We can do that using the gather function.
22
library(tidyr)
messy %>%
gather(drug,heartrate,a:b)
What this does is , it gathers column names from a to b in the new variable drug and the column heartrate
contains values which were in wide format previously in the data in columns a to b.
Lets look at another example which will let us look more into function gather and we’ll also see where we
can use function separate. The data set that we start with is this
Here after second argument -id,-trt tells gather to use rest of the variable for gathering. which are work.T1,
home.T1, work.T2,home.T2.
Now if we want to convert this variable key in to two separate variables because of the information contained
we can do that by using function separate.
23
tidier=tidier %>% separate(key,into=c("location","shift"),sep="\\.")
tidier
Now lets use function spread and unite to roll this back to original format.
step2=step1 %>%
spread(key,time)
step2
24
## 3 3 treatment 0.6516557 0.39879073 0.27453052 0.3580500
## 4 4 control 0.5677378 0.83613414 0.27230507 0.4288094
Usage of unite is straight forward, i will not discuss that in detail. What spread is doing that it makes
column names by different values of variable key and then fills in those columns values from variable time.
Ofcourse these observations need to be uniquely identifiable with the help of other variables in the data.
Lastly I’d like to cover a very imprtant topic in data prep that is to handle date time data . Why the special
attention? Because the way date time data is recorded is essentially as a character and when it is dealt with,
its like numbers . Now parsing these seemingly character strings to numbers in a way that they can represent
data time and then throw those time zones in the mix and you have pretty difficult situation to handle. Fret
not , we have package lubridate to make our life pretty easy.
Please install package lubridate before you begin running these codes.
Note: All our examples are based on converting character strings to dates using functions. When you are
reading data from a file, read a date time column as character and convert it to a date time object type later.
Its always easier to do.
We’ll start with converting various dates in different formats stored as characters. These formats can be
different in terms of with in that data in what order day, month and year appear.
Identify the order in which the year, month, and day appears in your dates. Now arrange “y”, “m”, and “d”
in the same order. This is the name of the function in lubridate that will parse your dates. For example,
library(lubridate)
ymd("20110604")
mdy("06-04-2011")
dmy("04/06/2011")
you include time components and timezones as well by simply adding the order of time components hours
(“h”), minutes (“m”) and seconds (“s”). Appropriate function name exists.
you can extract and set individual elements of the date as well.
second(arrive)
## [1] 0
25
second(arrive) = 25
arrive
second(arrive) = 0
wday(arrive)
## [1] 7
## [1] Sat
## Levels: Sun < Mon < Tues < Wed < Thurs < Fri < Sat
I’m leaving for you to find functions for rest of the elements. They are there and names aren’t difficult to
figure out.
Time zones is something which can cause significant amount of frustration. This has been made very simple
in lubridate.
You can find what time this meeting will be in CDT time zone (America/Chicago)
with_tz(meeting, "America/Chicago")
you can do arithmatic with dates also for example you want to add an year to a date.
leap_year(2011)
## [1] FALSE
ymd(20110101) + dyears(1)
ymd(20110101) + years(1)
Functions dyears and year are identical in this respect. The difference is that dyears will always equal to
fixed duration of 365 days. where as a years will take into account leap years as well. Here is an example.
26
leap_year(2012)
## [1] TRUE
ymd(20120101) + dyears(1)
ymd(20120101) + years(1)
So far we have seen seemingly well formatted character strings as input for dates which is not always the case
. Dates can have months as character names or even abreviated form of three letter words. And same goes
for other date components as well. You can handle that by specifying your own formats too these format
builders and function parse_date_time.
Although there are too many format builders here , you’ll generally use few. we’ll see example with those.
you can handle hetrogenous formats as well.
parse_date_time("01-12-Jan","%d-%y-%b")
27
parse_date_time(x, "%y%m%d")
## ** heterogenuous formats **
We’ll conclude here. In case of any doubts regarding content of this study material, please post on QA forum
in LMS.
Prepared By: Lalit Sachan
Contact: [Link]@[Link]
28