在R中清洗格式不一致的数据?


16

我经常处理凌乱的调查数据,这需要大量清理才能完成任何统计数据。我曾经在Excel中“手动”执行此操作,有时使用Excel公式,有时一个接一个地检查条目。我开始通过编写脚本在R中完成这些任务来做越来越多的任务,这是非常有益的(好处包括记录已完成的操作,减少出错的机会以及在数据集为更新)。

但是我仍然无法有效处理某些类型的数据。例如:

> d <- data.frame(subject = c(1,2,3,4,5,6,7,8,9,10,11),
+   hours.per.day = c("1", "2 hours", "2 hr", "2hr", "3 hrs", "1-2", "15 min", "30 mins", "a few hours", "1 hr 30 min", "1 hr/week"))
> d
   subject hours.per.day
1        1             1
2        2       2 hours
3        3          2 hr
4        4           2hr
5        5         3 hrs
6        6           1-2
7        7        15 min
8        8       30 mins
9        9   a few hours
10      10   1 hr 30 min
11      11     1 hr/week

hours.per.day意思是每天在某项活动上花费的平均小时数,但我们所掌握的正是该主题写的内容。假设我对模糊响应的处理方式做出了一些决定,并且希望得到hours.per.day2如下整理的变量。

   subject hours.per.day hours.per.day2
1        1             1      1.0000000
2        2       2 hours      2.0000000
3        3          2 hr      2.0000000
4        4           2hr      2.0000000
5        5         3 hrs      3.0000000
6        6           1-2      1.5000000
7        7        15 min      0.2500000
8        8       30 mins      0.5000000
9        9   a few hours      3.0000000
10      10   1 hr 30 min      1.5000000
11      11     1 hr/week      0.1428571

假设案例数量很大(例如1000),并且知道受试者可以自由编写自己喜欢的任何东西,那么最好的方法是什么?

Answers:


13

我将使用gsub()识别我知道的字符串,然后手动完成其余的工作。

test <- c("15min", "15 min", "Maybe a few hours", 
          "4hr", "4hour", "3.5hr", "3-10", "3-10")
new_var <- rep(NA, length(test))

my_sub <- function(regex, new_var, test){
    t2 <- gsub(regex, "\\1", test)
    identified_vars <- which(test != t2)
    new_var[identified_vars] <- as.double(t2[identified_vars])
    return(new_var)    
}

new_var <- my_sub("([0-9]+)[ ]*min", new_var, test)
new_var <- my_sub("([0-9]+)[ ]*(hour|hr)[s]{0,1}", new_var, test)

为了与需要手动更改的内容一起工作,我建议如下所示:

# Which have we not found
by.hand <- which(is.na(new_var))

# View the unique ones not found
unique(test[by.hand])
# Create a list with the ones
my_interpretation <- list("3-10"= 5, "Maybe a few hours"=3)
for(key_string in names(my_interpretation)){
    new_var[test == key_string] <- unlist(my_interpretation[key_string])
}

这给出:

> new_var
[1] 15.0 15.0  3.0  4.0  4.0  3.5  5.0  5.0

正则表达式可能会有些棘手,每次我使用正则表达式执行任何操作时,我都会运行一些简单的测试。Se?regex手册。这是一些基本行为:

> # Test some regex
> grep("[0-9]", "12")
[1] 1
> grep("[0-9]", "12a")
[1] 1
> grep("[0-9]$", "12a")
integer(0)
> grep("^[0-9]$", "12a")
integer(0)
> grep("^[0-9][0-9]", "12a")
[1] 1
> grep("^[0-9]{1,2}", "12a")
[1] 1
> grep("^[0-9]*", "a")
[1] 1
> grep("^[0-9]+", "a")
integer(0)
> grep("^[0-9]+", "12222a")
[1] 1
> grep("^(yes|no)$", "yes")
[1] 1
> grep("^(yes|no)$", "no")
[1] 1
> grep("^(yes|no)$", "(yes|no)")
integer(0)
> # Test some gsub, the \\1 matches default or the found text within the ()
> gsub("^(yes|maybe) and no$", "\\1", "yes and no")
[1] "yes"

感谢您的回答。我对正则表达式不熟悉,因此必须了解它们。您介意简要介绍如何手动进行其余操作吗?难道还有比仅仅做这样的事情有更好的方式new_var[by.hand] <- c(2, 1, ...)by.handTRUE用于由手工完成的情况下?
mark999 2012年

@ mark999:添加了一些示例,并建议了如何手动进行操作。
Max Gordon

1
正则表达式对于任何类型的数据处理都非常重要:像OP一样清理数据,或者从文件,HTML等中提取数据。(对于正确的HTML,有一些库XML可以帮助您提取数据,但是HTML格式不正确时无法正常工作。)
韦恩(Wayne

6

@Max的建议是一个好建议。看来,如果您编写一种可以识别数字以及与时间相关的常用单词/缩写的算法,那么您将获得大部分的信息。这不是漂亮的代码,但是它将起作用,并且当您遇到问题案例时,您可以随着时间的推移对其进行改进。

但是,对于更健壮(且最初很耗时)的方法,请尝试使用Google搜索“解析自然语言时间字符串”。一些有趣的发现是这个开放时间API,一个良好的Python模块,以及许多密切线程像这样的在一个堆栈溢出

基本上,自然语言解析是一个常见问题,您应该寻找R以外的其他语言的解决方案。您可以使用R可以访问的另一种语言来构建工具,或者至少可以为自己的算法获得好主意。


4

对于这样的事情,如果它足够长,我想我需要一个正则表达式和转换规则的列表,并将新值带到另一列(这样,您总是有机会在不重新加载原始数据的情况下进行仔细检查) ; 将RE应用于尚未经过转换的数据,直到所有数据都已转换或所有规则都用尽为止。最好还保留一个逻辑值列表,以指示尚未转换的行。

当然,有一些这样的规则很明显,并且可能会处理80-90%的案例,但是问题是总会有一些您不知道的案例会出现(人们很有创造力)。

然后,您需要一个脚本来一次遍历并向您展示由明显规则列表尚未转换的原始值,从而使您有机会进行正则表达式(例如)识别这些案例并针对适合的案例进行新的转换,然后将其添加到原始列表中,并应用于原始向量尚未转换的行,然后再检查是否还有任何案例要呈现给您。

可以选择 跳过一个案例(这样您可以继续进行更简单的案例)的,这样您就可以将非常困难的案例拖到最后。

最坏的情况是,您需要手动执行一些操作。

然后,您可以保留所生成规则的完整列表,以便在数据增长或出现新的类似数据集时再次应用。

我不知道它是否正在接近最佳实践(我认为在那里需要更正式的东西),但是就快速处理大量此类数据而言,它可能具有一定的价值。


谢谢您的回答,格伦。听起来很吸引人。您是否认为一次显示一个尚未转换的值,而不是只显示所有值并查看该输出是一个很大的优势?我从来没有做过像一次展示一件事情那样的事情。
mark999 2012年

1
@ mark999,我认为一次演示既有优点也有缺点。优点是简单-使用cat()显示不明确的时间,使用scan()记录您对该时间的解释很容易实现。缺点是您可能错过了很多条目的整体情况,而这些条目可以用一行正则表达式代码进行整体更正。您可能会想知道自己希望得到什么:如果您只想解决此问题,请手动完成。如果要了解有关R的更多信息,请尝试编写解决方案。
灰烬2012年

很抱歉,没有回复;我大体上同意Ash的评论
Glen_b-恢复莫妮卡(Monica)2012年

4

R含有一些标准为数据操纵功能,其可被用于数据清洗,在其基极封装(gsubtransform等),以及在各种第三方软件包,如stringr重塑reshape2,和plyr。以下文件描述了使用这些包及其功能的示例和最佳实践:http : //vita.had.co.nz/papers/tidy-data.pdf

此外,R提供了一些专门针对数据清理和转换的软件包:

全面和一致的办法数据清理中的R,包括实施例和使用的editrulesdeducorrect包,以及的描述工作流框架数据清理中的R),其示于下面的纸张,我强烈建议:HTTP ://cran.r-project.org/doc/contrib/de_Jonge+van_der_Loo-Introduction_to_data_cleaning_with_R.pdf

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.