【问题标题】:Dynamically Change as.POSIXlt Value动态改变 as.POSIXlt 值
【发布时间】:2019-07-29 03:02:10
【问题描述】:

在 R 中,我试图读取具有时间戳的文件,并根据另一个字段的条件更新时间戳。下面的代码没有问题:

t <- data.frame(user = as.character(c("bshelton@email1.com", "lwong@email1.com")), 
                last_update = rep(as.POSIXlt(Sys.time(), tz = "America/Los_Angeles"), 2))
Sys.sleep(5)
t$last_update <- as.POSIXlt(ifelse(t$user == "bshelton@email1.com", Sys.time(), t$last_update), origin = "1970-01-01")  
print(t)

问题是当我读取现有文件并尝试动态更改 as.POSIXlt 值时。以下代码产生了随后代码块中伴随的错误:

t <- data.frame(user = as.character(c("bshelton@email1.com", "lwong2@email1.com")), 
                last_update = rep(as.POSIXlt(Sys.time(), tz = "America/Los_Angeles"), 2))

write.csv(t, "so_question.csv", row.names = FALSE)
t <- read.csv("so_question.csv")

t$last_update <- as.POSIXlt(t$last_update)
Sys.sleep(5)
t$last_update <- as.POSIXlt(ifelse(t$user == "bshelton@email1.com", Sys.time(), t$last_update), origin = "1970-01-01")  
Error in as.POSIXlt.default(ifelse(t$user == "bshelton@email1.com", Sys.time(),  : 
  do not know how to convert 'ifelse(t$user == "bshelton@email1.com", Sys.time(), t$last_update)' to class “POSIXlt”
In addition: Warning message:
In ans[!test & ok] <- rep(no, length.out = length(ans))[!test &  :
  number of items to replace is not a multiple of replacement length

【问题讨论】:

    标签: r if-statement timestamp


    【解决方案1】:

    第一种情况很奇怪,只是因为你没有你想的那样——那些日期时间实际上是 POSIXct,而不是 POSIXlt:

    last_update <- rep(as.POSIXlt(Sys.time(), tz = "America/Los_Angeles"), 2)
    str(last_update)
    #>  POSIXlt[1:2], format: "2019-07-28 20:52:10" "2019-07-28 20:52:10"
    
    t <- data.frame(user = as.character(c("bshelton@email1.com", "lwong@email1.com")), 
                    last_update = last_update)
    str(t)
    #> 'data.frame':    2 obs. of  2 variables:
    #>  $ user       : Factor w/ 2 levels "bshelton@email1.com",..: 1 2
    #>  $ last_update: POSIXct, format: "2019-07-28 20:52:10" "2019-07-28 20:52:10"
    

    如果你深入了解?data.frame,它会说

    data.frame 通过调用as.data.frame(optional = TRUE) 将其每个参数转换为一个数据框。由于这是一个通用函数,因此可以编写方法来根据它们的类来改变参数的行为:R 带有许多这样的方法。传递给 data.frame 的字符变量将转换为因子列,除非受 I 保护或参数 stringsAsFactors 为 false。如果将列表、数据框或矩阵传递给data.frame,就好像每个组件或列都作为单独的参数传递(受I 保护的矩阵除外)。

    这就是正在发生的事情:as.data.frame.POSIXlt 实际上转换为 POSIXct:

    now <- Sys.time()
    str(now)
    #>  POSIXct[1:1], format: "2019-07-28 22:50:12"
    
    str(data.frame(time = now))
    #> 'data.frame':    1 obs. of  1 variable:
    #>  $ time: POSIXct, format: "2019-07-28 22:50:12"
    
    as.data.frame.POSIXlt
    #> function (x, row.names = NULL, optional = FALSE, ...) 
    #> {
    #>     value <- as.data.frame.POSIXct(as.POSIXct(x), row.names, 
    #>         optional, ...)
    #>     if (!optional) 
    #>         names(value) <- deparse(substitute(x))[[1L]]
    #>     value
    #> }
    #> <bytecode: 0x7fc938a11060>
    #> <environment: namespace:base>
    

    更直接的是,由于Sys.time() 返回一个 POSIXct 对象,ifelse(t$user == "bshelton@email1.com", Sys.time(), t$last_update) 在第二种情况下为一个观察获取 POSIXct 对象,为另一个观察获取 POSIXlt。 POSIXlt 对象的class 属性被ifelse 删除,显示下面的列表,ifelse 然后不知道如何与未分类的 POSIXct 对象(这只是一个数字)一起变成向量。

    那么,这里的解决方案是按照 data.frame 给您的提示使用 POSIXct 而不是 POSIXlt。

    如果您真的想让它与 POSIXlt 一起工作,您可以使用 Mapif/else 迭代条件和 POSIXlt 向量(它维护包括类在内的属性,但仅处理标量条件)并将结果列表强制返回到带有 do.call(c, ...) 的向量:

    t <- data.frame(user = as.character(c("bshelton@email1.com", "lwong@email1.com")), 
                    last_update = rep(as.POSIXlt(Sys.time(), tz = "America/Los_Angeles"), 2))
    t$last_update <- as.POSIXlt(t$last_update)
    
    t$last_update <- do.call(c, Map(
        function(condition, last_update){
            if (condition) {
                as.POSIXlt(Sys.time() + 5)
            } else {
                last_update
            }
        },
        condition = t$user == "bshelton@email1.com",
        last_update = t$last_update
    ))
    t
    #>                  user         last_update
    #> 1 bshelton@email1.com 2019-07-28 23:11:04
    #> 2    lwong@email1.com 2019-07-28 23:10:59
    

    ...但坦率地说,这有点愚蠢。改用 POSIXct,你的生活会更好。

    【讨论】:

    • 明天早上我有机会运行并验证后将接受。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-03
    • 2018-08-24
    • 1970-01-01
    相关资源
    最近更新 更多