在R的Lubridate中查找无法解析的日期

14

作为一个 R 的初学者,我正在努力调试晦涩难懂的 R 错误。我有一个包含150k行的csv文件,我将其加载到名为"date"的数据框中。然后我使用lubridate将此字符列转换为日期时间,以便找到最小/最大日期。

  dates <- csv[c('datetime')]
  dates$datetime <- ymd_hms(dates$datetime)

运行此代码,我收到以下错误信息:

Warning message:
3 failed to parse. 

我接受这个,因为CSV文件中可能有一些杂乱的日期,在下一次运行时:

min(dates$datetime) 
max(dates$datetime)

这两个都返回 NA,我想这是由于数据框中仍存储着一些损坏的日期。我已经搜索了一些快速修复的方法,甚至尝试构建一个 foreach 循环来识别问题日期,但都没有成功。有什么简单的方法可以识别这三个损坏的日期吗?

example date format: 2015-06-17 17:10:16 +0000

您可以检查格式是否一致。还可以检查?guess_formats - akrun
1
如果您对日期时间向量进行排序,那么 NA 值会在头部还是尾部? - lawyeR
@akrun 我尝试在Excel中导入CSV文件,检查每个单元格的长度并过滤任何不合适的内容。但是这没有得到任何结果,有没有办法在R中实现这个功能?我已经尝试了guess_formats和parse_date_time,但都失败了。 - Korben Dallas
@lawyeR,就是这样!我已经对未解析的原始csv进行了排序,然后tail(dates)显示了三个不正确的字段。感谢您的帮助! - Korben Dallas
4
你可以使用which(is.na(dates$datetime))来找到缺失日期的行号。 - Stibu
@Stibu 这正是我正在寻找的,快速简便。 - Korben Dallas
7个回答

15

感谢上面评论区的LawyeR和Stibu:

  1. 我首先按原始csv列进行了排序,并使用head()和tail()方法找到了导致问题的前3个日期。
  2. 或者可以用一个简单的一行代码which(is.na(dates$datetime))来得到答案。

18
很好,但它并不能回答一般性的问题。如果问题是您的数据中有字符“purpleElephant”,它不是NA,但仍然无法解析,我们仍然需要某种方法来查看Lubridate给出的警告。 - Monica Heddneck
这个问题是关于识别三个错误日期的,这个解决方案完美地实现了这一点。 - Jon
12
它只是因为这3个日期恰好是NA,所以才能实现。我有一个包含约17个NA的93个日期/时间的向量,并且出现了“2个解析失败”的情况。因此,这种解决方案并没有解决一般性问题,只是解决了OP特定情况的问题。 - dez93_2000

4
Lubridate会在尝试解析因夏令时而不存在的日期时抛出错误。
例如:
library(lubridate)
mydate <- strptime('2020-03-08 02:30:00', format = "%Y-%m-%d %H:%M:%S")
ymd_hms(mydate, tz = "America/Denver")

[1] NA
Warning message:
 1 failed to parse. 

我的数据来自于一个不智能的传感器,它并不知道夏令时的存在,因此在我的时间序列中会出现不可能的(但格式正确的)日期。

3
这里有一个简单的函数,可以解决通用问题:
parse_ymd = function(x){
  d=lubridate::ymd(x, quiet=TRUE)
  errors = x[!is.na(x) & is.na(d)]
  if(length(errors)>0){
    cli::cli_warn("Failed to parse some dates: {.val {errors}}")
  }
  d
}

x = c("2014/20/21", "2014/01/01", NA, "2014/01/02", "foobar")
my_date = lubridate::ymd(x)
#> Warning: 2 failed to parse.
my_date = parse_ymd(x)
#> Warning: Failed to parse some dates: "2014/20/21" and "foobar"

本文于2022年9月29日使用reprex v2.0.2创建。

当然,将ymd()替换为您想要的内容即可。


它消除了失败警告,但如果有大量数据,则不显示故障发生的位置(我将options(max.print = .Machine $ integer.max)更改为避免打印限制,但仍然没有出现消息)。它解决了我的问题。 - yav dat
通过使用这个功能,我能够确定我有一个不可能的日期2000-11-38,我进行了修正,不再出现解析错误。 - undefined

1
为了提供更通用的答案,首先过滤掉 NA,然后尝试解析,最后仅过滤出 NA。这将显示失败情况。类似以下内容:
dates2 <- dates[!is.na(dates2$datetime)]
dates2$datetime <- ymd_hms(dates2$datetime)

Warning message:
 3 failed to parse.

dates2[is.na(dates2$datetime)]

1
如果您希望了解lubridate失败的位置索引,可以使用带有stopifnot()的for循环,并打印每个成功的解析结果。
创建一些日期,在其中随机位置引入错误。
library(lubridate)
set.seed(1)
my_dates<-as.character(sample(seq(as.Date('1900/01/01'), 
as.Date('2000/01/01'), by="day"), 1000))
my_dates[sample(1:length(my_dates), 1)]<-"purpleElephant"

现在使用for循环并使用stopifnot()打印每个成功的解析。
for(i in 1:length(my_dates)){
   print(i)
   stopifnot(!is.na(ymd(my_dates[i])))
}


0
使用截断参数。日期时间数据中最常见的不规则性是由于四舍五入或时间戳不可用而导致的截断。
因此,尝试截断=1,然后可能增加到截断=3:
  dates <- csv[c('datetime')]
  dates$datetime <- ymd_hms(dates$datetime, truncated = 1)

0
lubridate::ymd_hms()在尝试解析时间戳字符串时,需要一个特定的字符数(通常为19个字符)。有时,午夜时刻的时间戳只会显示日期部分,意味着字符串只有10个字符。还有其他原因,比如简单的数据错误可能导致非标准的字符串长度,但基于NA的操作无法找到这些错误。
如果您想查看字符串长度是否存在异常,您可以使用以下代码:
table(nchar(dates$datetime))
如果您发现存在问题字符串的条目,您可以使用以下代码来确定这些行:
which(nchar(dates$datetime) != 19)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接