处理凌乱的日期

时间:2011-05-31 22:26:54

标签: r

我希望你不认为我是在寻求关系建议。

我不得不为调查受访者提供指定事件发生时间的能力。什么结果是一个可怕的凌乱的字符串,老实说,我不知道该怎么做。超越手工编码。

这是一个简短的样本,数以千计:

c("May2/ 12 noon", "9:45 am", "11:00 AM AST", "April 27 / 12:00 AST", 
"11:40 AM AST", "April 25 2011", "April 12th 2011 / 8:44", "April 12 2011 / 8:36am", 
"April 12 2011 / 8:30am", "April 12th 2011 / 8:18", "April 12 2011 / 8:12am", 
"April 11th 2011 / 5:57pm", "April 11th 2011 / 5:49pm", "April 11th 2011 / 5:42pm", 
"April 11th 2011 / 5:36pm", "April 11th 2011 / 5:27", "April 5 @ 11:26am", 
"8:50", "April 4th 12:45pm", "April 4th around 10am", "April 4th around 10am", 
"Mar 18, 2011 9:33am", "Mar 18, 2011 9:27am", "df", "fg", "12:16", 
"9:50", "Feb 8, 2011 / 12:20pm", "8:34 am  2/4/11", "Jan 31, 2011 2:50pm", 
"Jan 31, 2011 2:45pm", "Jan 31, 2011 2:38pm", "Jan 31, 2011 2:26pm", 
"11h09", "11:00 am", "1h02 pm", "10h03", "2h10", "Jan 13, 2011 9:50am Van", 
"Jan 12, 2011", "Jan 12, 2011 3:59pm", "Jan 12     14:19PM", 
"Jan 12, 2011 1:35pm", "Jan 12,2011 1:28pm", "1h36", "9h15", 
"9h09", "8h51", "8h45", "8h35", "1h12 pm", "12h59", "11h52 am", 
"10h45", "15h55", "Dec 31, 10 11:11am", "Dec 31,10 10:15am", 
"Dec 30, 2010 12:32pm", "Dec 30, 2010 12:18pm", "9:16 am", "11h16 am", 
"11h12", "9h29 am", "11h38", "Dec 16, 2010", "December 16, 2010", 
"December 16, 2010", "Dec 15,2010", "DEC 14 2010", "Dec 14 11:38", 
"Dec 14 11:35", "Dec 14 11:25", "December 13, 2010", "Dec 10, 1:38 pm", 
"Dec 10, 1:26 pm", "Dec 10, 1:20 pm", "Dec 10, 1:12 pm", "December 9 2010", 
"11h10 am", "10h59 am", "10:50 am", "Tues Dec 7th, 9:45 Van time", 
"Dec 3, 2010 12:30pm", "Dec 3, 2010 12:20pm", "Dec 3, 2010 12:10 pm", 
"November 30, 2010 4.02pm", "November 30, 2010", "november 29 120pm", 
"November 29 2010 11:27", "10:12am November 29, 2010", "Nov 26/10 1:18pm", 
"10:56 am", "Nov 24", "nov 24/ 4:20 PM AST", "Nov 24/4:00 PM AST", 
"NOVEMBER 24/10  2:10 pm", "November 24/10  11:00 a.m.", "12:05 MST", 
"3.55PM", "Nov. 17/10 12:45 pm", "Nov. 16/10  12:00 noon", "Nov. 16/10 11;50 a.m.", 
"nov 16/10  11:30 a.m.", "November 12, 2010 @ 12:23pm", "november 11 2010  2:20pm", 
"November 11 2010  2:15pm", "November 11 2:00pm", "Nov. 10/10:22am", 
"nov. 8/10...3:19 pm", "Nov 8/10  1;50 p.m.", "November 8/10...12 noon", 
"November 8/10..10: am", "Nov 5, 2010  1:10 pm", "11:32 am CST", 
"Nov 4  11:10", "nov 3 10am", "9:30 am", "11/02/2010 1:50PM", 
"Oct 29/10 2:50PM", "Oct 28 @ 11:20am", "27Oct10 10:40am", "10/26/2010 11:18", 
"Oct 26/10 11am", "Oct 26/10 10:30 am", "Oct 26 10:50", "10/25/2010 13:50", 
"10/22/2010  10:15", "Oct 22/10 10AM", "Oct 21, 2010 3:00 pm", 
"Oct 21, 2010 2:59", "10/21/2010 11:50", "10/21/2010 11:45", 
"10/21/2010 11:40", "10/21/2010 11:30", "11:30", "Oct 20 approx 1pm", 
"Oct 20/10 4:50PM", "13:48", "13:45", "Oct 20, 2010 11:45 am", 
"October 19th 3:05pm", "Oct 18,2010 2:15pm", "Oct 18/10 3:10PM", 
"10:30 am", "Oct 15/10 11:50am", "oct 14 @ 11:05am", "Oct 14/ 11:06", 
"4:40 oct 13 atlantic", "oct 13 4:05 pm atlantic", "oct 13 1:45 atlantic time", 
"Oct 13 / 10:37", "OCT 12 3:33", "Oct 12,2010 1:10pm", "Oct 12 / 11:45", 
"Oct 12 / 9:45", "Oct 8. 2010/ 2:00", "Oct 8/10- 1145am", "2 Sept 2010 3.52pm", 
"2 Sept 2010 10.21am", "1 Sept 2010 2.05pm", "1 Sept 2010", "31 Aug 2010 - 11.52am", 
"31 aug 10:40am", "31 aug 2010 - 10am")

一般来说,这些事件发生在受访者填写调查的日期附近,但并非总是如此。调查日期自动以一致的格式记录,并且可以使用as.Date轻松转换为POSIX,因此,可以忽略仅包含时间的元素,并将其与填写调查的日期合并。

非常感谢您的想法。

注意1:有些人可能会说,你应该在验证回复方面做过X,Y或Z.对你说,我说 - 下次是的 - 下次。我没有设计它!我只需处理它。

一些可以帮助解决方法的事实:

  • 时间总是工作时间,上午9点至下午6点(因此上午/下午无关紧要)
  • 这些年份并不重要,因为我可以将它们从另一个领域拉出来(它永远只会是2011/2010,幸好在任何表示法的可能时间范围之外)
  • 我不关心时区,因为我有他们的地理位置

到目前为止我做了什么:

mos <- strsplit('
jan
feb
mar
apr
may
jun
jul
aug
sep
oct
nov
dec
january
february
march
april
may
june
july
august
september
october
november
december
', '\n')[[1]][-1]

days <- strsplit('
mon
tue
wed
thu
fri
sat
sun
monday
tuesday
wednesday
thursday
friday
saturday
sunday
', '\n')[[1]][-1]
## Messy Date Wrangling
x <- ## that hot ghetto mess above
# minimize
x <- tolower(x)
# remove unnecessary crap
x <- sub("2011"," ",x)
x <- sub("2010"," ",x)
x <- sub("am"," ",x)
x <- sub("pm"," ",x)
x <- sub("[p][.][m]"," ",x)
x <- sub("[a][.][m]"," ",x)
x <- sub("[.]{3}"," ",x)
x <- str_trim(x, side="both")
# divide
x <- strsplit(x,c(" "))
# conquer?

lapply(x, function(x) pmatch(x,mos))
lapply(x, function(x) pmatch(x,days))

5 个答案:

答案 0 :(得分:21)

我同情你的约会并没有像预期的那样漂亮。 ; - )

我已经按照@Rguy建议的方式构建了一个(仍然是部分的)解决方案。

(请注意,此代码仍有错误:它并不总是返回正确的时间。由于某种原因,它并不总是在冒号前的数字上进行贪婪匹配,因此有时会返回1:00当时间是11:00。)

首先,构建一个包裹gsubgrep的辅助函数。此函数将字符向量作为其参数之一,并将其​​折叠为由|分隔的单个字符串。这样做的效果是允许您轻松传递多个模式以匹配正则表达式:

find.pattern <- function(x, pattern_list){
  pattern <- paste(pattern_list, collapse="|")
  ret <- gsub(paste("^.*(", pattern, ").*", sep=""), "\\1", x, ignore.case=TRUE)
  ret[ret==x] <- NA 
  ret2 <- grepl(paste("^(", pattern, ")$", sep=""), x, ignore.case=TRUE)
  ret[ret2] <- x[ret2] 
  ret
}

接下来,使用一些内置变量名来构造月份和缩写的向量:

all.month <- c(month.name, month.abb)

最后,构建一个具有不同提取的数据框:

ret <- data.frame(
    data = dat, 
    date1 = find.pattern(dat, "\\d+/\\d+/\\d+"),
    date2 = find.pattern(dat, 
      paste(all.month, "\\s*\\d+[(th)|,]*\\s{0,3}[(2010)|(2011)]*", collapse="|", sep="")),
    year = find.pattern(dat, c(2010, 2011)),
    month = find.pattern(dat, month.abb), #Use base R variable called month.abb for month names
    hour = find.pattern(dat, c("\\d+[\\.:h]\\d+", "12 noon")),
    ampm = find.pattern(dat, c("am", "pm"))
)

结果:

head(ret, 50)
                      data  date1        date2 year month  hour ampm
20   April 4th around 10am   <NA>   April 4th  <NA>   Apr  <NA>   am
21   April 4th around 10am   <NA>   April 4th  <NA>   Apr  <NA>   am
22     Mar 18, 2011 9:33am   <NA> Mar 18, 2011 2011   Mar  9:33   am
23     Mar 18, 2011 9:27am   <NA> Mar 18, 2011 2011   Mar  9:27   am
24                      df   <NA>         <NA> <NA>  <NA>  <NA> <NA>
25                      fg   <NA>         <NA> <NA>  <NA>  <NA> <NA>
26                   12:16   <NA>         <NA> <NA>  <NA> 12:16 <NA>
27                    9:50   <NA>         <NA> <NA>  <NA>  9:50 <NA>
28   Feb 8, 2011 / 12:20pm   <NA>  Feb 8, 2011 2011   Feb  2:20   pm
29         8:34 am  2/4/11 2/4/11         <NA> <NA>  <NA>  8:34   am
30     Jan 31, 2011 2:50pm   <NA> Jan 31, 2011 2011   Jan  2:50   pm
31     Jan 31, 2011 2:45pm   <NA> Jan 31, 2011 2011   Jan  2:45   pm
32     Jan 31, 2011 2:38pm   <NA> Jan 31, 2011 2011   Jan  2:38   pm
33     Jan 31, 2011 2:26pm   <NA> Jan 31, 2011 2011   Jan  2:26   pm
34                   11h09   <NA>         <NA> <NA>  <NA> 11h09 <NA>
35                11:00 am   <NA>         <NA> <NA>  <NA>  1:00   am
36                 1h02 pm   <NA>         <NA> <NA>  <NA>  1h02   pm
37                   10h03   <NA>         <NA> <NA>  <NA> 10h03 <NA>
38                    2h10   <NA>         <NA> <NA>  <NA>  2h10 <NA>
39 Jan 13, 2011 9:50am Van   <NA> Jan 13, 2011 2011   Jan  9:50   am
40            Jan 12, 2011   <NA> Jan 12, 2011 2011   Jan  <NA> <NA>

答案 1 :(得分:10)

这可能是少数除R之外的其他工具最好使用的情况之一。我知道Perl的一些模块已经被开发用于解析凌乱的日期,在模块DateTime :: Format :: Natural :: Lang :: EN可以解析字符串,如:“11月1日星期二”。我似乎还记得另一个可以理解“2月第一个星期一之后的第二个星期二”的模块。

http://www.datasciencetoolkit.org/还有一个工具可以抓取文本中的日期并将其转换为标准格式。

答案 2 :(得分:6)

我现在不打算尝试编写这个功能,但我有一个可能有效的想法。

在每个字符串中搜索一个4位数字来调用年份。

使用grep在每个字符串中搜索月份缩写的前3个字母。看起来几乎所有的数据(至少在上面)都有这样的标识符。我将存储在“月”向量中找到的值,并在没有找到值的地方放置空格。这是一个非常丑陋的代码版本(我将在以后提高效率,并在月份未大写时添加案例!)

mos <- c("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")   
blah <- lapply(1:12, function(i) grepl(mos[i], test))   
lapply(blah, function(i) which(i))   
months <- 0*(1:length(test))   
for (i in 1:12) {   
  months[blah[[i]]] <- i   
}  


   months
  [1]  5  0  0  4  0  4  4  4  4  4  4  4  4  4  4  4  4  0  4  4  4  3  3  0  0  0  0  2  0  1
 [31]  1  1  1  0  0  0  0  0  1  1  1  1  1  1  0  0  0  0  0  0  0  0  0  0  0 12 12 12 12  0
 [61]  0  0  0  0 12 12 12 12  0 12 12 12 12 12 12 12 12 12  0  0  0 12 12 12 12 11 11  0 11 11
 [91] 11  0 11  0 11  0 11  0  0 11 11 11  0 11  0 11 11 11  0 11 11 11 11  0 11  0  0  0 10 10
[121] 10  0 10 10 10  0  0 10 10 10  0  0  0  0  0 10 10  0  0 10 10 10 10  0 10  0 10  0  0  0
[151] 10  0 10 10 10 10 10  9  9  9  9  8  0  0 

“day”最常见的是当月使用的单词。因此,如果月份之后有一个或两位数字(即字符),请提取该数字并在当天调用。

时代最常见的是“:”或“。”其中包含符号,因此搜索该字符的每个字符串。如果在字符串中找到,则创建一个“时间”向量,其中包含该字符之前和之后的所有数字(理论上,包括2之前和2之后不应该导致问题)。每当符号不存在时放空格。如果所有数据都被限制在<12小时的时间段内会很好,因为那样你就不必担心AM和PM了。如果没有,也可以在字符串中搜索“AM”和“PM”。

然后,尝试将具有上述所有四个字符串的字符串转换为POSIXct。那些不转换的,当然你必须手动输入。我认为编写上述功能需要花费几个小时的时间,并且根据数据集的可变性和大小,它可能值得也可能不值得。此外,存在输出错误的风险,因此添加可接受的时间范围将有助于避免这种情况。

总而言之,听起来你将不得不编写一个包含很多异常的函数,然后最终手动编码大部分时间。我希望有人可以为你提供更好的解决方案。

祝你好运!

答案 3 :(得分:3)

狼队阿尔法 http://www.wolframalpha.com/ 绝对是做这项工作的好工具。

至少,它成功地解释了数据中一些混乱的输入。 值得一试。

我不确定该网站是否适合超大型数据集,但如果数据不是那么大,那么它将非常有用。

编写一个发送查询,获取数据并解析它的自动化脚本并不困难,尽管我不确定该站点是否允许这样使用。

答案 4 :(得分:3)

其他人已经解决了标准方法和包。我会采取不同的观点。使用正则表达式和固定格式将使您获得最大的收益。对于其他人,我只是接近它,就像“模式匹配”中的任何问题一样:统计方法或机器学习。您已经指定了日期和时间范围,日志的时间戳也提供了信息。通过提取大量文本特征(这是正则表达式证明有用的地方),您可以尝试映射到感兴趣的时间。

使这项工作只有三件事:

  1. 特征提取
  2. 训练集生成
  3. 构建&amp;部署模型
  4. 构建和部署模型?让我向你介绍我的朋友R和machine learning task view。 :)要探索的基本模型包括多项模型(查看glmnet),决策树和支持向量机。您可以使用决策树和SVM作为多项模型的输入(毕竟可能不需要SVM)。说实话,这部分是模糊的:人们可以将此建模作为断开连接的日期组件或作为细化过程,例如,得到年份,如果可能的话,然后是分钟(因为范围远大于小时,天,月),然后是月份,最后是几小时和几个月。从本质上讲,我的目标是尝试识别数字/字符串组件的“时间部分”(类似于词性)。

    特征提取:我尝试用冒号,逗号,斜线,短划线,句点等进行拆分。任何不是数值的东西。然后,我将按顺序和任何顺序(即所看到的特征的指示值,忽略位置)基于特征创建数据集。

    培训数据:亚马逊的Mechanical Turk。

    或者,你知道什么,只是忽略所有的编程和统计mumbo jumbo并将所有内容发送给Mechanical Turk。 :)