如何将时期分为年,月等

时间:2016-09-04 06:26:48

标签: r dplyr lapply lubridate

我有一个包含许多时间列的数据框。我想为年,月,日等等每次添加列。

这是我到目前为止所做的:

library(dplyr)
library(lubridate)

times <- c(133456789, 143456789, 144456789 ) 
train2 <- data.frame(sent_time = times, open_time = times)

time_col_names <- c("sent_time", "open_time")
dt_part_names <- c("year", "month", "hour", "wday", "day")

train3 <- as.data.frame(train2)

dummy <- lapply(time_col_names, function(col_name) { 
  pct_times <- as.POSIXct(train3[,col_name], origin = "1970-01-01", tz = "GMT")
  lapply(dt_part_names, function(part_name) {
    part_col_name <- paste(col_name, part_name, sep = "_")
    train3[, part_col_name] <- rep(NA, nrow(train3))
    train3[, part_col_name] <- factor(get(part_name)(pct_times))
  })
})

除了永远不会创建或分配列之外,一切似乎都有效。组件确实被提取,并且赋值成功而没有错误,但是train3没有任何新列。

我已经检查过,当我在嵌套的lapply上下文之外调用它时,赋值是有效的:

train3[, "x"] <- rep(NA, nrow(train3))

在这种情况下,确实会创建列x。

1 个答案:

答案 0 :(得分:3)

apply循环相比,人们常常认为for系列在性能方面具有优势。但是for循环与来自*apply()系列的循环之间最重要的区别是后者被设计为没有副作用

缺乏副作用有利于开发干净,结构良好且简洁的代码。如果一个希望产生副作用,则会出现问题,这通常是代码设计有缺陷的症状。

以下是一个简单的例子来说明这个

myvector <- 10:1
sapply(myvector,prod,2)
# [1] 20 18 16 14 12 10  8  6  4  2

看起来不错,对吗? sapply()循环似乎将myvec的条目乘以2(已授予,此结果可以更容易实现,但这只是讨论*apply()的功能的一个简单示例)

然而,在检查时,人们意识到此操作根本没有改变myvector

> myvector
# [1] 10  9  8  7  6  5  4  3  2  1

这是因为sapply()没有副作用来修改myvector。在此示例中,sapply()循环等效于命令print(myvector*2),而不是myvector <- myvector * 2*apply()循环返回一个对象,但它们不会修改原始对象。

如果确实想要在循环中更改对象,则需要超级对齐运算符<<-来修改循环范围之外的对象。这应该几乎永远不会完成,在这种情况下,事情变得非常难看。例如,以下循环确实更改了我的myvector

sapply(seq_along(myvector), function(x) myvector[x] <<- myvector[x]*2)
> myvector
# [1] 20 18 16 14 12 10  8  6  4  2

R中的编码不应该是这样的。请注意,在这种更复杂的情况下,如果使用正常赋值运算符<-而不是<<-,则myvector保持不变。正确的方法是分配*apply返回的对象,而不是在循环内修改它。

在OP描述的特定情况下,如果循环中的命令正确,则变量dummy可以包含所需的输出。但是人们不能指望在循环中修改对象train3。为此,<<-运算符是必要的。

fortunes::fortune(212)中提到的引用可能总结了问题:

  

基本上R不愿意让你在脚下射击,除非   你真的有决心这样做。 - 比尔维纳布尔斯