【问题标题】:R: Best way around as.POSIXct() in apply functionR:在应用函数中解决 as.POSIXct() 的最佳方法
【发布时间】:2017-08-22 12:14:13
【问题描述】:

我正在尝试设置一个新变量,该变量包含已知日期和给定年份结束之间的差异(天数)。虚拟数据如下:

> Date.event <- as.POSIXct(c("12/2/2000","8/2/2001"), format = "%d/%m/%Y", tz = "Europe/London")
> Year = c(2000,2001)
> Dates.test <- data.frame(Date.event,Year)
> Dates.test
  Date.event Year
1 2000-02-12 2000
2 2001-02-08 2001

我已经尝试应用一个函数来实现这一点,但它返回一个错误

> Time.dif.fun <- function(x) {
+ as.numeric(as.POSIXct(sprintf('31/12/%s', s= x['Year']),format = "%d/%m/%Y", tz = "Europe/London") - x['Date.event'])
+ }
> Dates.test$Time.dif <- apply(
+ Dates.test, 1, Time.dif.fun
+ )

 Error in unclass(e1) - e2 : non-numeric argument to binary operator 

apply() 似乎不喜欢 as.POSIXct(),因为测试仅导出年终日期的函数版本,它以 '978220800' 形式返回为数字(例如对于 end 2000 年)。有没有办法解决?对于真实数据,该函数有点复杂,包括使用不同变量的条件实例,有时还引用以前的行,如果不应用,这将很难做到。

【问题讨论】:

标签: r date apply posixct


【解决方案1】:

这里有一些替代方案:

1) 您的代码适用于这些更改。我们将 s 分解出来,不是因为它是必要的,而只是因为下面的行由于长度太长而很难阅读。请注意,如果x 是一个数据框,那么x["Year"] 也是一个数据框,但x[["Year"]]x$Year 一样是一个向量。由于这些操作都是矢量化的,我们不需要apply

虽然我们没有进行此更改,但将 s 定义为 s &lt;- paste0(x$Year, "-12-31") 会更容易一些,在这种情况下,由于使用默认格式,我们可以省略下一行中的格式参数。

Time.dif.fun <- function(x) {
  s <- sprintf('31/12/%s', x[['Year']])
  as.numeric(as.POSIXct(s, format = "%d/%m/%Y", tz = "Europe/London") -x[['Date.event']])
}
Time.dif.fun(Dates.test)
## [1] 323 326

2) 转换为POSIXlt,设置年月日为年末,减法。请注意,year 组件使用自 1900 年以来的年份,而 mon 组件使用 Jan = 0、Feb = 1、...、Dec = 11。有关这些组件和其他组件的详细信息,请参阅 ?as.POSIXlt

lt <- as.POSIXlt(Dates.test$Date.event)
lt$year <- Dates.test$Year - 1900
lt$mon <- 11
lt$mday <- 31
as.numeric(lt - Dates.test$Date.event)
## [1] 323 326

3) 另一种可能是:

with(Dates.test, as.numeric(as.Date(paste0(Year, "-12-31")) - as.Date(Date.event)))
## [1] 323 326

【讨论】:

  • 真的很方便,谢谢,尤其是在函数中定义临时“s”的顶级解决方法现在很明显,但我从来没有想过。猜猜这就是学习编码的一部分!
  • 关于这一点,请参阅对 (1) 的补充说明。
【解决方案2】:

您可以使用difftime 函数:

Dates.test$diff_days <- difftime(as.POSIXct(paste0(Dates.test[,2],"-12-31"),format = "%Y-%m-%d", tz = "Europe/London"),Dates.test[,1],unit="days")

【讨论】:

    【解决方案3】:

    您可以使用ISOdate 来构建年末日期,并使用difftime(... units='days') 来获取到年底的天数。

    来自?difftime

    “difftime”对象上可用的算术有限:它们可以是 加或减,乘以或除以一个数值向量。

    如果您想做的不仅仅是有限的算术,只需使用as.numeric() 进行强制,但您必须坚持使用您指定的任何单位。

    按照惯例,您可能希望使用明年年初(除夕午夜)作为您当年的终点。例如:

    Dates.test <- data.frame(
      Date.event = as.POSIXct(c("12/2/2000","8/2/2001"), 
                              format = "%d/%m/%Y", tz = "Europe/London")
    )
    # use data.table::year() to get the year of a date
    year <- function(x) as.POSIXlt(x)$year + 1900L
    Dates.test$Date.end <- ISOdate(year(Dates.test$Date.event)+1,1,1)
    
    # if you don't want class 'difftime', wrap it in as.numeric(), as in:
    Dates.test$Date.diff <- as.numeric(
                                 difftime(Dates.test$Date.end, 
                                          Dates.test$Date.event, 
                                          units='days')
                            )
    Dates.test
    #   Date.event            Date.end Date.diff
    # 1 2000-02-12 2001-01-01 12:00:00     324.5
    # 2 2001-02-08 2002-01-01 12:00:00     327.5
    

    apply() 系列基本上是一种干净的for 循环方式,您应该争取更高效的矢量化解决方案。

    【讨论】:

    • 一些有用的提示,谢谢。 year() 是额外包的功能吗?我不承认它。矢量化解决方案的问题在于,真正的函数有不止一个潜在的已知日期,有时它需要回到上一年寻找其他日期,所以我想执行一些 ifelse() 函数。上面的“日期差异”也不是输出的终点,我需要将天数转换为数字以便通过额外的系数进行缩放。
    • 哎呀,year 实际上在data.table 中,但您可以像上面编辑的那样定义它,而不是加载包。
    • @user 查看编辑。 ifelse() 是矢量化的,所以我不确定你在做什么。此解决方案适用于您提供的示例。请确保您的示例能够代表您要提出的问题。
    猜你喜欢
    • 1970-01-01
    • 2023-03-13
    • 1970-01-01
    • 2021-12-11
    • 1970-01-01
    • 2014-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多