【问题标题】:Converting date and time in a list of values在值列表中转换日期和时间
【发布时间】:2016-10-12 07:15:43
【问题描述】:

我已阅读 Combining date and time into a Date column for plotting 和其他相关解决方案,但找不到此问题的答案: 我有一个包含 7 个元素的字符串列表,其中前两个是日期和时间:

> head(IOlow)
[1] "20160928 142949 0.000172 0.000225 0.000015 0.000172 0.000765"
[2] "20160928 164105 0.000163 0.000227 0.000017 0.000163 0.000908"
[3] "20160928 172826 0.000176 0.000238 0.000017 0.000173 0.001604"

现在我想用包含 6 个元素的列表替换每个这样的字符串,其中第一个元素是从前两个数字构建的 DateTime 对象,其他元素是浮点数(而不是字符串)。所以我定义了这个辅助函数:

dateFormat <- "%Y%m%d %H%M%S"
function (x) {
    x <- strsplit(x, split=" ")
    w <- paste(x[[1]][1], x[[1]][2])
    str(w)
    x <- list(as.POSIXlt(w, format=dateFormat), as.double(x[[1]][3:7]))
}

(str(w) 仅用于调试) 像这样应用这个功能时

lapply(head(IOlow), to_numeric)

结果如下所示:

> lapply(head(IOlow), to_numeric)
 chr "20160928 142949"
 chr "20160928 164105"
 chr "20160928 172826"
[[1]]
[[1]][[1]]
[1] "2016-09-28 14:29:49 CEST"

[[1]][[2]]
[1] 0.000172 0.000225 0.000015 0.000172 0.000765


[[2]]
[[2]][[1]]
[1] "2016-09-28 16:41:05 CEST"

[[2]][[2]]
[1] 0.000163 0.000227 0.000017 0.000163 0.000908


[[3]]
[[3]][[1]]
[1] "2016-09-28 17:28:26 CEST"

[[3]][[2]]
[1] 0.000176 0.000238 0.000017 0.000173 0.001604

由于我不明白的原因,R 似乎在列表中创建了额外的列表(我认为我无法返回向量,因为列表中有不同的数据类型)。 也许只是某些功能没有以我需要的方式记录,以了解真正发生的事情。

下一步将从列表列表中构建一个 data.frame。

我做错了什么,我该如何正确做?

系统信息:

> sessionInfo()
R version 3.3.1 (2016-06-21)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1

locale:
[1] LC_COLLATE=German_Germany.1252  LC_CTYPE=German_Germany.1252   
[3] LC_MONETARY=German_Germany.1252 LC_NUMERIC=C                   
[5] LC_TIME=German_Germany.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
[1] tools_3.3.1

【问题讨论】:

  • 需要使用POSIXlt吗?它在内部将事物存储为列表(可以是好的也可以是坏的)。由于各种原因,我倾向于更多地使用POSIXct
  • 为了消除一些混乱,您能否提供所需的输出?据我了解,最后您希望看到一个包含 6 列的数据框,例如 1 列用于日期,5 列用于数据?通过设计将您的输入直接转换为数据框的函数,如何完全跳过所有这些“列表混乱”?
  • 实际上直到你的帖子我真的不知道POSIXctPOSIXlt 之间的区别是什么(谢谢!)。也许可以改进文档。关于输出格式:第一个列表元素应该是可以被绘图使用的东西,并以人类可读的格式显示为以后处理的日期和/或时间(其余的元素是可处理的数字)。够清楚吗?我愿意接受任何优雅的解决方案(因为我还在学习 R)!

标签: r type-conversion


【解决方案1】:

根据@A.Val 的建议,您是否必须将其作为列表处理?如果它直接在某处的文件中,它会更直接地用read.delimreadr::read_delim 之类的东西读入。

缺乏这一点,把你的字符串变成一个假文件(有两个原因:(1)你的再现性,(2)如果你不能把它当作一个文件来处理,现在你仍然可以使用这个答案):

IOlow <- c("20160928 142949 0.000172 0.000225 0.000015 0.000172 0.000765",
           "20160928 164105 0.000163 0.000227 0.000017 0.000163 0.000908",
           "20160928 172826 0.000176 0.000238 0.000017 0.000173 0.001604")
dat <- read.delim(textConnection(paste(IOlow, collapse = "\n")),
                  sep = " ", header = FALSE)
dat
#         V1     V2       V3       V4      V5       V6       V7
# 1 20160928 142949 0.000172 0.000225 1.5e-05 0.000172 0.000765
# 2 20160928 164105 0.000163 0.000227 1.7e-05 0.000163 0.000908
# 3 20160928 172826 0.000176 0.000238 1.7e-05 0.000173 0.001604

dat$dt <- as.POSIXct(paste(dat$V1, dat$V2, sep = " "), format = "%Y%m%d %H%M%S")
dat <- dat[, -(1:2)]
dat
#         V3       V4      V5       V6       V7                  dt
# 1 0.000172 0.000225 1.5e-05 0.000172 0.000765 2016-09-28 14:29:49
# 2 0.000163 0.000227 1.7e-05 0.000163 0.000908 2016-09-28 16:41:05
# 3 0.000176 0.000238 1.7e-05 0.000173 0.001604 2016-09-28 17:28:26

【讨论】:

  • 相当酷:在这种情况下我没有想到read.delim()。此外,使用 dat[, -(1:2)] 删除前两列对我来说是新事物(或者在我的记忆中没有解决)。
  • 坦率地说,有几种方法可以给这只猫剥皮。也可以使用readr::read_delim(来自文件)、tidyr::unitelubridate::ymd_hmsdplyr 将它们粘合在一起(dplyr-fluidly)。我确信它可以用data.table 完成,尽管我不太了解它。 strsplit 如果您绝对必须将其用作字符串向量,则可以使用。
  • 顺便说一句:一个经常被遗忘的步骤或检查是确保时区正确。像这样的时候,“显式”会省去很多麻烦。
  • 解释为什么我不能从readr::read_delim开始(从给出的例子看不明显):这是我第一次尝试处理纯文本日志文件,IOlow中的结果实际上是过滤和文本替换(正则表达式)日志文件中的行的结果。
【解决方案2】:

我想你想要这样的东西,不是吗?

to_numeric <- function(x,dateFormat) {
  x <- unlist(strsplit(as.character(x), split=" "))
  w <- paste(x[1], x[2])
  x <- list(as.POSIXlt(w, format=dateFormat), as.double(x[3:7]))
}

l <- apply(head(IOlow), 1, function(x) to_numeric(x, '%Y%m%d'))

l[1]

[[1]]
[1] "2016-09-28 IST"

[[2]]
[1] 0.000172 0.000225 0.000015 0.000172 0.000765

【讨论】:

  • 不完全(我不确定我明白了):我认为strsplit() 返回一个元素列表;那么unlist(strsplit(...)) 的结果是什么?好的,由于某些晦涩的原因,`strsplit()` 返回一个列表,其中唯一的元素是结果元素的列表。 1 参数到 apply() 的原因是什么?
  • (按 Enter 太早)似乎 R 喜欢将列表放入列表中,只是因为它很酷(或混淆...);-) @r2evans 的解决方案对我来说看起来更好,但这仍然很有用。
  • (1) 因为最好在一个向量中有 6 个值而不是一个列表,您只需要一级索引:只需 x[1],而不是 x[[1]][1 ] (2) 对于数据帧 IOlow 的每一行,我们要应用该函数,这就是为什么有参数 1 (3) 最好将一个额外的 dateFormat 参数传递给您的函数以使其更灵活。 (4) 最后,数据帧的每一行现在将对应于输出列表中的一个元素(它本身就是一个包含 6 个元素的列表)。
【解决方案3】:

考虑到

dat <- read.delim(textConnection(paste(IOlow, collapse = "\n")),
                  sep = " ", header = FALSE)

在@r2evans 的回答中,首先将列表转换为文本(paste),然后解析文本(read.delim(textConnection(...))),我认为这太复杂了。于是我重新设计了逻辑:

split_line <- function (x) {
    x <- unlist(strsplit(x, split=" "))
}

IOlow <- lapply(IOlow, split_line)
IOlow <- data.frame(do.call(rbind, IOlow), stringsAsFactors=FALSE)

然后我根据日期和时间构建一个新的“when”变量(我已将名称添加到 data.frame):

dateFormat <- "%Y%m%d %H%M%S"
IOlow$when <- with(IOlow,
                   as.POSIXct(paste(date, time, sep = " "), format=dateFormat))

然后我删除日期和时间变量 (IOlow &lt;- IOlow[, -(1:2)])。最后我将剩余的字符串转换为双精度(这一步有更优雅的解决方案吗?):

for (n in names(IOlow)) {
    # convert all strings to double
    if ("character" %in% class(IOlow[,n])) {
        IOlow[n] <- lapply(IOlow[n], as.double)
    }
}

【讨论】:

  • 您说 “太复杂”,但您正在摄取数据 原始,在 R 中进行正则表达式工作,然后尝试解析分隔文件?效率肯定不在窗口(尤其是因为您必须手动转换为浮点数),并且通过使用read.delim(或相关函数),您可以利用它们稳健地处理近乎原始数据的能力以及数字化事物。交给你了,但我认为这种方法——尽管它可能有效——更脆弱,可能更慢。
  • 话虽如此,我也有类似的需求,我发现最快(时间方面)和最安全的方法是使用@987654329 @,根据需要按摩数据(例如,正则表达式按摩),writeLines 回到另一个文件,然后 readr::read_csv。这样做的时间差异是显着,不能轻易丢弃。 (坦率地说,我应该使用 python 进行预读按摩,但我怀疑我和你一样,因为 R 是最接近的并且被用于其他所有事情,所以......)。
  • 也许@r2evans 显示了用于“按摩”的代码草图(作为相关问题的一般解决方案)?我并不是要抹黑这个提议,但我觉得它不适合我的情况。
  • 没有通用的解决方案:它特定于数据和需要修复的问题才能使用标准的read.* 函数。在将数据固定为 7 字段字符串向量之前显示数据的外观会更合适。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-14
  • 1970-01-01
  • 2019-08-20
  • 1970-01-01
  • 2018-03-05
  • 2021-02-12
  • 2021-09-28
相关资源
最近更新 更多