登录
首页精彩阅读用R语言做数据清理之一:数据的清理
用R语言做数据清理之一:数据的清理
2017-04-07
收藏

R语言做数据清理之一:数据的清理

数据的清理

一份好的,干净而整洁的数据至少包括以下几个要素:

1、每一个观测变量构成一列
2、每一个观测对象构成一行
3、每一个类型的观测单元构成一个表
就像我们最常接触的鸢尾花数据:

##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species

## 1          5.1         3.5          1.4         0.2  setosa

## 2          4.9         3.0          1.4         0.2  setosa

## 3          4.7         3.2          1.3         0.2  setosa

## 4          4.6         3.1          1.5         0.2  setosa

## 5          5.0         3.6          1.4         0.2  setosa

每一列就是观测的指标:花瓣长度,花瓣宽度,萼片长度,萼片宽度,种类;每一行就是一株鸢尾花的观测值,构成整张表的元素就是四个数值变量,一个分类分类变量。

然而出于排版的考虑我们抓下来的数据往往不是那么的友好,比如说我们可以看到的数据通常是这样的:

##   religion <10k 10k-50k 50k-100k

## 1 Agnostic   12      31       23

## 2 Buddhist   58      43       43

## 3 Catholic   79      56       23

而不是:

##   religion   income freq

## 1 Agnostic     <10k   12

## 2 Agnostic  10k-50k   58

## 3 Agnostic 50k-100k   79

## 4 Buddhist     <10k   31

当然,除了这种把列表每一列代表一些数值这种情况外,还有多个变量储存为一列(比如列表不仅以"<10k","10k-50k","50k-100k"做表头,甚至还加上性别信息"m<10k","m10k-50k","m50k-100k","f<10k","f10k-50k","f50k-100k",其中m代表男性,f代表女性),还有更过分的将列表的变量不仅储存在列中,行中也有统计变量。

面对这些不好的table,我们首先要做的就是数据管理,将数据整理为一个干净的数据集。

数据管理

按照en:DAMA的定义:“数据资源管理,致力于发展处理企业数据生命周期的适当的建构、策略、实践和程序”。这是一个高层而包含广泛的定义,而并不一定直接涉及数据管理的具体操作(如关系数据库的技术层次上的管理)。我们这里主要讲述对于数据的变量命名与数据的合并,旨在方便数据共享。

数据管理首先要做的就是大致上了解你的数据,比如有什么样的变量,每一行大致长成什么样,最常用的就是head(),tail().
我们要做的基本上就是这么几项工作:

给每一个变量命名,而不是V1,V2,如果有必要可以给出code book。

每个变量名最好具有可读性,除非过长,否则不要用缩写,例如AgeAtDiagnosis这个命名远好于AgeDx。

通常来说,最好将数据放在一张表里面,如果因为数据过多,项目过杂,分成了几张表。那么一定需要有一列使得这些表之间能够连接起来,但尽量避免这样做。

我们以UCI的Human Activity Recognition Using Smartphones Data Set 为例来看看数据是如何变成一个基本符合要求的数据。这个数据我们已经下载下来了,其中关于数据的详细信息可以参阅read me文档,由于UCI的数据通常都是一个基本合乎规范的数据集(主要是指它的数据集的变量名都是以V1,V2来命名的)加上一个code book。那么我们看看各个数据的名称(在feature文件里)

setwd("F:/coursera/Data Science/peer assignment/get clean and tidy data/UCI HAR Dataset")name <- read.table("./features.txt", stringsAsFactors = F)

head(name)

##   V1                V2

## 1  1 tBodyAcc-mean()-X

## 2  2 tBodyAcc-mean()-Y

## 3  3 tBodyAcc-mean()-Z

## 4  4  tBodyAcc-std()-X

## 5  5  tBodyAcc-std()-Y

## 6  6  tBodyAcc-std()-Z

我们可以看到各个特征的名称直接标在数据上是非常不友善的,我们为了让他具有可读性,我们以展示在我们眼前的6个数据为例:

variablename <- head(name)# 将标签中的大写字母转为小写,我们这里没有所以不再赋值,如果需要全变为大写,可以使用touppertolower(variablename$V2)

## [1] "tbodyacc-mean()-x" "tbodyacc-mean()-y" "tbodyacc-mean()-z"

## [4] "tbodyacc-std()-x"  "tbodyacc-std()-y"  "tbodyacc-std()-z"

# 将变量名分离成3部分splitNames <- strsplit(variablename$V2, "-")splitNames[[1]]

## [1] "tBodyAcc" "mean()"   "X"

# 将变量名合成有意的名称named <- function(x) {    rr <- paste(x[2], x[1], "-", x[3],sep = "")

chartr("()", "of", rr)

}

sapply(splitNames, named)

## [1] "meanoftBodyAcc-X" "meanoftBodyAcc-Y" "meanoftBodyAcc-Z"

## [4] "stdoftBodyAcc-X"  "stdoftBodyAcc-Y"  "stdoftBodyAcc-Z"

用这样的名字给数据集命名就感觉舒服多了,我们将一些R中对字符串常用的操作函数总结如下,方便我们对数据名称的修改:

sub:替换字符串中的第一个模式为设定模式(pattern).

gsub:全局替换字符串中的相应模式 

grep,grepl:这两个函数返回向量水平的匹配结果,grep仅返回匹配项的下标,而grepl返回所有的查询结果,并用逻辑向量表示有没有找到匹配。

nchar:统计字符串单字数目

substr:取子串

paste:将字符串链接起来,sep参数可以设置连接符

str_trim:去掉字符串空格

变量的名称建议满足如下要求:

英文变量名尽可能用小写

尽可能的描述清楚变量特征 (Diagnosis versus Dx)

不要太复杂

不要有下划线、点、空格 

字符型变量应该满足:

是因子类型的应该转化为factor

因子尽可能具有一定的描述性 (例如:如果0/1表示真假,那么用TRUE/FALSE代替0/1;在表示性别时用Male/Female代替M/F)

接下来我们讨论数据集的合并,主要使用函数merge。
我们以下面两个数据集的合并为例:

df1 <- data.frame(id = sample(1:10), reviewer_id = sample(5:14),time_left = sample(1321:1330),

x = rnorm(10))df2 <- data.frame(id = sample(1:10), answer = rep("B", 10),time_left = sample(321:330),

y = rnorm(10))

head(df1, n = 3)

##   id reviewer_id time_left       x

## 1  3           9      1326 -0.9232

## 2 10           5      1322  2.5069

## 3  1          14      1330  2.2478

head(df2, n = 3)

##   id answer time_left      y

## 1  1      B       329 0.8180

## 2 10      B       327 1.4639

## 3  9      B       323 0.8141

merge函数调用格式为:

merge(x, y, by = intersect(names(x), names(y)),

by.x = by, by.y = by, all = FALSE, all.x = all, all.y = all,

sort = TRUE, suffixes = c(".x",".y"),

incomparables = NULL, ...)

参数说明:


x,y:两个数据框

by, by.x, by.y:指定用于合并的列的名称。

all,all.x,all.y:默认的all = FALSE相当于自然连接, 或者说是内部链接. all.x = TRUE是一个左连接, all.y = TRUE是一个又连接, all = TRUE 相当于一个外部链接.

仔细观察下面3个例子你就会发现其中的奥秘:

mergedData <- merge(df1,df2,by.x="reviewer_id",by.y="id",all=TRUE)

head(mergedData)

##   reviewer_id id time_left.x      x answer time_left.y       y

## 1           1 NA          NA     NA      B         329  0.8180

## 2           2 NA          NA     NA      B         330 -0.7706

## 3           3 NA          NA     NA      B         325 -0.4851

mergedData <- merge(df1,df2,by.x="id",by.y="id",all=TRUE)

head(mergedData)

##   id reviewer_id time_left.x        x answer time_left.y       y

## 1  1          14        1330  2.24783      B         329  0.8180

## 2  2          12        1324  1.03181      B         330 -0.7706

## 3  3           9        1326 -0.92317      B         325 -0.4851

## 4  4           7        1321 -0.07841      B         322  0.1801

mergedData2 <- merge(df1,df2,all=TRUE)

head(mergedData2)

##   id time_left reviewer_id       x answer       y

## 1  1       329          NA      NA      B  0.8180

## 2  1      1330          14  2.2478   <NA>      NA

## 3  2       330          NA      NA      B -0.7706

在plyr包中还提供了join,join_all,arrange等函数来实现表的连接,但我想merge这个函数已经足够用了,所以我们不在多说。当然,在极少数特别好的情况下(比如列的变量是一致的,或者行的观测个体是一致的时候)rbind,cbind也是有用的。

有些时候我们会遇到一些特殊的字符串:日期。R中提供了各式各样的函数来处理时间:

Sys.setlocale("LC_TIME", "C")

## [1] "C"

x <- c("1jan1960", "2jan1960", "31mar1960", "30jul1960")z <- as.Date(x, "%d%b%Y")

format(z, "%a %b %d")

## [1] "Fri Jan 01" "Sat Jan 02" "Thu Mar 31" "Sat Jul 30"

weekdays(z)

## [1] "Friday"   "Saturday" "Thursday" "Saturday"

julian(z)

## [1] -3653 -3652 -3563 -3442

## attr(,"origin")

## [1] "1970-01-01"

transform(z, weekend = as.POSIXlt(z, format = "%Y/%m/%d")$wday %in% c(0, 6))

##       X_data weekend

## 1 1960-01-01   FALSE

## 2 1960-01-02    TRUE

## 3 1960-03-31   FALSE

## 4 1960-07-30    TRUE


数据分析咨询请扫描二维码

客服在线
立即咨询