本书的第一章并不像其他书的第一章,仅仅是简单的介绍本书,很多代码需要细细推敲。
ufo <- read.delim("E:/ML/ufo_awesome.tsv",
sep = "\t",
stringsAsFactors = FALSE,
header = FALSE,
na.strings = "")
head(ufo)
# 数据框赋列名
names(ufo) <- c("DateOccurred","DateReported",
"Location","ShortDescription",
"Duration","LongDescription")
na.strings="" --表示将空元素赋值为NA,注意引号中没有空格
# 先查看错误行
head(ufo[which(nchar(ufo$DateOccurred)!=8|nchar(ufo$DateReported)!=8),1])
# 向量化操作,移除畸变数据
good.rows <- ifelse(nchar(ufo$DateOccurred) != 8 |
nchar(ufo$DateReported) != 8,
FALSE,
TRUE)
length(which(!good.rows))
ufo <- ufo[good.rows,]
# 修改日期格式
ufo$DateOccurred <- as.Date(ufo$DateOccurred,format = "%Y%m%d")
ufo$DateReported <- as.Date(ufo$DateReported,format = "%Y%m%d")
3.逗号分隔符的清洗与处理
地点数据的形式是“City,State”,要将它拆分成两列,并识别出不规范的数据行(挑出属于美国的数据)
定义输入字符串的函数,执行数据清洗工作
get.location <- function(l){
split.location <- tryCatch(strsplit(l,",")[[1]],
error = function(e) return(c(NA,NA)))
clean.location <- gsub("^ ","",split.location)
if(length(clean.location) > 2){
return(c(NA,NA))
}
else {
return(clean.location)
}
}
strsplit()只要遇到字符串就自动处理,不会报错;只有input不是字符串时就才会出现异常,此时tryCatch()函数就会处理异常,返回两个NA;
当遇到有多个逗号的字符串,在检查向量的长度时,依然返回两个NA。
即tryCatch()函数处理输入的是非字符串,长度检验处理的是input带多个逗号的情况,还有一种情况,input没有逗号,此时只返回它本身,即只有一个元素,若把此结果放到矩阵中,就会报错(矩阵的元素不能为empty)
gsub() :R的正则表达式相关函数之一
^:匹配输入字符串开始的位置,程序中因为要删除第一个位置的空格所以要写成”^ “,注意要加空格。
lapply()对每个输入的字符串应用我们定义的函数,返回列表
city.state <- lapply(ufo$Location,get.location)
head(city.state)
思考?
city.state1 <- sapply(ufo$Location,get.location,simplify = “matrix”)
若用sapply()函数simplify成矩阵,会产生错误,见上分析。
这时已经有了一个列表city.state,每个子表的元素是 城市,州
将此列表并入原始数据框中,首先用do.call()函数,转换为矩阵,再加入到数据框中。
# 将较长的列表转换为一个两列的矩阵
location.matrix <- do.call(rbind,city.state)
# 将矩阵并入数据框
ufo <- transform(ufo,USCity = location.matrix[,1],
USState = tolower(location.matrix[,2]),
stringsAsFactors = FALSE)
tolower()把所有的州的名字的缩写都变成小写形式
transform(_data,…) …参数要被处理成数据框中的对象,即给数据框添加新列
下面再识别非美国的数据
处理非美国境内的数据,形式上符合”city.state”,但实际上并不在美国境内的数据
# 构造一个美国各州缩写的向量,用数据框中USState列数据来匹配这个向量,将匹配上的保留下来,从而识别出非美国地名
us.states <- c("ak","al","ar","az","ca","co","ct","de","fl","ga","hi","ia","in",
"ks","ky","la","ma","md","me","mi","mn","mo","ms","mt","nc","nd",
"ne","nh","nj","nm","nv","ny","oh","ok","or","pa","ri","sc","sd",
"tn","tx","ut","va","vt","wa","wi","wv","wy")
ufo$USState <- us.states[match(ufo$USState,us.states)]
ufo.us <- subset(ufo,!is.na(USState))
head(ufo.us)
match()返回第一个参数在第二个参数中第一次匹配成功的位置,
返回值的长度与第一个参数的长度相同,如果没有匹配成功则返回NA。
# 按year-month的聚合方式来显示出周期的变化(数据框添加新列)
ufo.us$YearMonth <- strftime(ufo.us$DateOccurred,
format = "%Y-%m")
library(plyr)
# 统计美国的各个州在每个年-月期间目击UFO的次数
sightings.counts <- ddply(.data = ufo.us,
.variables = .(USState,YearMonth),.fun = nrow)
head(sightings.counts)
ddplyr()函数将数据按照一定的方式分组统计,在这里按州名和年-月来给数据分组。
sightings是一个数据框,记录了分组统计的结果,此结果中有一些缺失值,缺少了某些月份的统计情况。
下面进行补齐
阿拉斯加州1990年的2月和4月并没有记录,估计在这期间并没有目击记录,因此需要把这些时间记录加上,并设为0
# 创建UFO目击事件的日期序列
date.range <- seq.Date(from = as.Date(min(ufo.us$DateOccurred)),
to = as.Date(max(ufo.us$DateOccurred)),
by = "month")
# 然后把格式设定为数据框中的日期格式
date.strings <- strftime(date.range,"%Y-%m")
# 创建一个包含所有年-月和州的数据框
# lapply()函数增加列
states.dates <- lapply(us.states,function(s)
cbind(s,date.strings))
# do.call()函数将列表转换为矩阵,然后再转换为数据框
states.dates <- data.frame(do.call(rbind,states.dates),
stringsAsFactors = FALSE)
head(states.dates)
现在states.dates包含了每一年,每月,各个州的组合,下面给缺失的值补0
merge()函数将这个新数据框与原来的数据框(sightings.count)合并
all.sightings <- merge(states.dates,sightings.counts,
by.x = c("s","date.strings"),
by.y=c("USState","YearMonth"),
all = TRUE)
head(all.sightings)
merge()函数纵向合并两个数据框,通过一个或多个共有变量进行联结的(inner join)
merge(x,y,by,by.x,by.y,all,…)
x,y是要合并的数据框对像,默认情况通过两个数据框共有的列名进行合并
by.x,b.y分别指定两个数据框的列名,即确定观测所需要的标识符;根据指定的列,提取行观测,并连接起来
all设置为TRUE,告诉函数要把没有匹配上的数据也包含进来并填充为NA
接下来将all.sightings数据框的列名改成有意义的名称,NA值改为0
names(all.sightings) <- c("States","YearMonth","Sightings")
all.sightings$Sightings[is.na(all.sightings$Sightings)] <- 0
all.sightings$YearMonth <-as.Date(rep(date.range,length(us.states)))
all.sightings$States <- as.factor(toupper(all.sightings$States))
日期保存成Date对象而不是字符串,因为Date对象易于比较;
州名缩写最好用分类变量表示,而不用字符串,因此可以将其转换为factor类型;
toupper()函数将小写改成大写。