在读取文件时,read.table
函数使用type.convert
来区分逻辑,整数,数字,复数或因子列,并相应地存储它们。
我想在混合中添加日期,以便可以自动识别包含日期的列并将其解析为Date
个对象。只应识别几种日期格式,例如
date.formats <- c("%m/%d/%Y", "%Y/%m/%d")
以下是一个例子:
fh <- textConnection(
"num char date-format1 date-format2 not-all-dates not-same-formats
10 a 1/1/2013 2013/01/01 2013/01/01 1/1/2013
20 b 2/1/2013 2013/02/01 a 2013/02/01
30 c 3/1/2013 NA b 3/1/2013"
)
和
的输出dat <- my.read.table(fh, header = TRUE, stringsAsFactors = FALSE,
date.formats = date.formats)
sapply(dat, class)
会给:
num => numeric
char => character
date-format1 => Date
date-format2 => Date
not-all-dates => character
not-same-formats => character # not a typo: date format must be consistent
在我从头开始实施之前,包装中是否已经提供了类似的功能?或者也许有人已经给了它一个破解(或意志),并愿意在这里分享他的代码?谢谢。
答案 0 :(得分:3)
您可以使用lubridate::parse_date_time
,这有点严格(并创建POSIXlt
)数据。
我还添加了一些检查现有NA值(可能没有必要)。
例如
library(lubridate)
my.read.table <- function(..., date.formats = c("%m/%d/%Y", "%Y/%m/%d")) {
dat <- read.table(...)
for (col.idx in seq_len(ncol(dat))) {
x <- dat[, col.idx]
if(!is.character(x) | is.factor(x)) next
if (all(is.na(x))) next
for (format in date.formats) {
complete.x <- !(is.na(x))
d <- as.Date(parse_date_time(as.character(x), format, quiet = TRUE))
d.na <- d[complete.x]
if (any(is.na(d.na))) next
dat[, col.idx] <- d
}
}
dat
}
dat <- my.read.table(fh, stringsAsFactors = FALSE,header=TRUE)
str(dat)
'data.frame': 3 obs. of 6 variables:
$ num : int 10 20 30
$ char : chr "a" "b" "c"
$ date.format1 : Date, format: "2013-01-01" "2013-02-01" "2013-03-01"
$ date.format2 : Date, format: "2013-01-01" "2013-02-01" NA
$ not.all.dates : chr "2013/01/01" "a" "b"
$ not.same.formats: chr "1/1/2013" "2013/02/01" "3/1/2013"
另一种方法是在函数中使用options(warn = 2)
并将parse_date_time(...)
包装在try语句中
my.read.table <- function(..., date.formats = c("%m/%d/%Y", "%Y/%m/%d")) {
dat <- read.table(...)
owarn <-getOption('warn')
on.exit(options(warn = owarn))
options(warn = 2)
for (col.idx in seq_len(ncol(dat))) {
x <- dat[, col.idx]
if(!is.character(x) | is.factor(x)) next
if (all(is.na(x))) next
for (format in date.formats) {
d <- try(as.Date(parse_date_time(as.character(x), format)), silent= TRUE)
if (inherits(d, 'try-error')) next
dat[, col.idx] <- d
}
}
dat
}
答案 1 :(得分:1)
我在这里很快就把它扔了。它没有正确处理最后一列,因为as.Date
函数不够严格(例如,as.Date("1/1/2013", "%Y/%m/%d")
解析ok ...)
my.read.table <- function(..., date.formats = c("%m/%d/%Y", "%Y/%m/%d")) {
dat <- read.table(...)
for (col.idx in seq_len(ncol(dat))) {
x <- dat[, col.idx]
if(!is.character(x) | is.factor(x)) next
if (all(is.na(x))) next
for (f in date.formats) {
d <- as.Date(as.character(x), f)
if (any(is.na(d[!is.na(x)]))) next
dat[, col.idx] <- d
}
}
dat
}
dat <- my.read.table(fh, header = TRUE, stringsAsFactors = FALSE)
as.data.frame(sapply(dat, class))
# sapply(dat, class)
# num integer
# char character
# date.format1 Date
# date.format2 Date
# not.all.dates character
# not.same.formats Date
如果您知道一种方法来解析格式比as.Date
更严格的日期(请参阅上面的示例),请告知我们。
编辑:为了使日期解析超级严格,我可以添加
if (!identical(x, format(d, f))) next
要使其工作,我需要所有输入日期在需要时都有前导零,即01/01/2013
而不是1/1/2013
。如果这是标准方式,我可以忍受。
答案 2 :(得分:1)
您可以尝试使用正则表达式。
my.read.table <- function(..., date.formats = c("%m/%d/%Y", "%Y/%m/%d")) {
require(stringr)
formats <- c(
"%m" = "[0-9]{1,2}",
"%d" = "[0-9]{1,2}",
"%Y" = "[0-9]{4}"
)
dat <- read.table(...)
for (col.idx in seq_len(ncol(dat))) {
for (format in date.formats) {
x <- dat[, col.idx]
if(!is.character(x) | is.factor(x)) break
if (all(is.na(x))) break
x <- as.character(x)
# Convert the format into a regular expression
for( k in names(formats) ) {
format <- str_replace_all( format, k, formats[k] )
}
# Check if it matches on the non-NA elements
if( all( str_detect( x, format ) | is.na(x) ) ) {
dat[, col.idx] <- as.Date(x, format)
break
}
}
}
dat
}
dat <- my.read.table(fh, header = TRUE, stringsAsFactors = FALSE)
as.data.frame(sapply(dat, class))
# sapply(dat, class)
# num integer
# char character
# date.format1 Date
# date.format2 Date
# not.all.dates character
# not.same.formats character