R:应用函数中 as.POSIXct() 的最佳方法
R: Best way around as.POSIXct() in apply function
我正在尝试设置一个新变量,该变量包含已知日期与给定年份结束之间的差异(天数)。以下虚拟数据:
> 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
我试过应用一个函数来实现这个,但是 returns 出错了
> 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' 形式的数字形式返回(例如 2000 年底)。有没有办法解决?对于真实数据,该函数有点复杂,包括使用不同变量的条件实例,有时会引用前几行,如果不应用,这将很难做到。
您可以使用 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")
您可以使用 ISOdate
构建年末日期,并使用 difftime(... units='days')
获取到年末的天数。
来自?difftime
:
Limited arithmetic is available on "difftime" objects: they can be
added or subtracted, and multiplied or divided by a numeric vector.
如果您想做的不仅仅是有限的算术运算,只需使用 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
循环的一种干净方式,您应该争取更高效的矢量化解决方案。
这里有一些备选方案:
1) 您的代码适用于这些更改。我们将 s 排除在外,不是因为它是必要的,而只是因为如果没有它,下面的行会因为它的长度而变得很难阅读。请注意,如果 x
是数据框,那么 x["Year"]
也是数据框,但 x[["Year"]]
和 x$Year
一样是向量。由于操作都是矢量化的,因此我们不需要 apply
。
虽然我们没有进行此更改,但将 s 定义为 s <- 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
我正在尝试设置一个新变量,该变量包含已知日期与给定年份结束之间的差异(天数)。以下虚拟数据:
> 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
我试过应用一个函数来实现这个,但是 returns 出错了
> 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' 形式的数字形式返回(例如 2000 年底)。有没有办法解决?对于真实数据,该函数有点复杂,包括使用不同变量的条件实例,有时会引用前几行,如果不应用,这将很难做到。
您可以使用 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")
您可以使用 ISOdate
构建年末日期,并使用 difftime(... units='days')
获取到年末的天数。
来自?difftime
:
Limited arithmetic is available on "difftime" objects: they can be added or subtracted, and multiplied or divided by a numeric vector.
如果您想做的不仅仅是有限的算术运算,只需使用 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
循环的一种干净方式,您应该争取更高效的矢量化解决方案。
这里有一些备选方案:
1) 您的代码适用于这些更改。我们将 s 排除在外,不是因为它是必要的,而只是因为如果没有它,下面的行会因为它的长度而变得很难阅读。请注意,如果 x
是数据框,那么 x["Year"]
也是数据框,但 x[["Year"]]
和 x$Year
一样是向量。由于操作都是矢量化的,因此我们不需要 apply
。
虽然我们没有进行此更改,但将 s 定义为 s <- 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