有效地设置第一个月倒退

时间:2013-12-29 10:01:48

标签: r date

在我的数据集中,我想创建一个新变量,其中月份向后设置一个。我可以这样做:

df$month.min.1 <- gsub('1', '12', df$month)
df$month.min.1 <- gsub('2', '1', df$month)
df$month.min.1 <- gsub('3', '2', df$month)
df$month.min.1 <- gsub('4', '3', df$month)
....

由于我还想创建月份设置为两个月和三个月的变量,我想知道是否有更有效的方法来做到这一点?

5 个答案:

答案 0 :(得分:3)

听起来你只有1到12代表你的“月”。如果是这种情况,您可以编写如下函数:

myfun <- function(x = 1:12, n = 1) c(tail(x, n), head(x, -n))
myfun()
#  [1] 12  1  2  3  4  5  6  7  8  9 10 11

然后,您可以使用它来创建滞后值。

一些例子:

set.seed(1)
x <- sample(12, 20, replace = TRUE)  ## Imagine this is your "month" variable
x
#  [1]  4  5  7 11  3 11 12  8  8  1  3  3  9  5 10  6  9 12  5 10
myfun()[x]                           ## Default -- set one month backwards
#  [1]  3  4  6 10  2 10 11  7  7 12  2  2  8  4  9  5  8 11  4  9
myfun(n = 2)[x]                      ## "n" can be changed
#  [1]  2  3  5  9  1  9 10  6  6 11  1  1  7  3  8  4  7 10  3  8

答案 1 :(得分:3)

如果m是月份的向量,使得每个组件都是1(Jan)到12(Dec)之间的数字,则这是前几个月k个月的月份数:

(m  - k - 1) %% 12 + 1

<强>实施例

m <- 1:12  # input months

# one month before
k <- 1
(m - k - 1) %% 12 + 1    ## 12  1  2  3  4  5  6  7  8  9 10 11

# two months before
k <- 2
(m - k - 1) %% 12 + 1    ## 11 12  1  2  3  4  5  6  7  8  9 10

# three months before
k <- 3
(m - k - 1) %% 12 + 1    ## 10 11 12  1  2  3  4  5  6  7  8  9

# one month in the future
k <- -1
(m - k - 1) %% 12 + 1    ## 2  3  4  5  6  7  8  9 10 11 12  1

注意如果我们使用编码0(Jan)到11(Dec),那么公式简化为此,其中m0是新编码中的月份向量和结果也在新编码中:

(m0 - k) %% 12

答案 2 :(得分:2)

您可以使用difftime但是对于加法和减法,我喜欢lubridate包。请注意,此答案以前使用mydate - months(1)语法,如果日期是在该月的最后一天,则该语法可能会产生看似不正确的结果。 %m-%(或%m+%)语法确实可以像大多数人期望的那样工作。

library(lubridate)
mydate <- as.Date('2013-12-31')
mydate %m-% months(1)

这给出了以下输出:

> library(lubridate)
> mydate <- as.Date('2013-12-31')
> mydate %m-% months(1)
[1] "2013-11-30"

编辑:Hadley在下面的评论中指出,在某些情况下可能很难定义什么是“正确的”。根据{{​​1}}包文档(我的重点):

  

用句点指导算术的逻辑可能不直观。   从版本1.3.0开始,lubridate强制执行可逆   算术的属性(例如日期+期间 - 期间=日期)   如果您通过添加句点创建不可信的日期,则返回NA   以月或年为单位的日期。例如,添加一个月   2013年1月31日结果于2013年2月31日,这不是真实的   日期。 lubridate用户过去曾在2013年2月31日争论过   应该延期到2013年3月3日或者回到2月份   然而,这些修正中的每一个都会破坏   添加的可逆性(3月3日 - 1个月== 2月3日!= 1月31日,2月   28 - 一个月== 1月28日!= 1月31日)。如果你想添加和   以将结果回滚到最后一天的方式减去月份   一个月(适当时)使用特殊运算符%m +%和   %间 - %

这些是合理的论点,但这种设计理念有时会导致lubridate函数产生既不是预期也不直观的结果。例如,下面的代码段可能会让大多数用户在第一次遇到它时感到惊讶:

months

我认为生成NA是因为11月只有30天,因此> z <- as.Date("2008-12-31") > z - months(1) [1] NA 是不可能的日期。相反,这个例子很好用,这是大多数人所期望的:

2013-11-31

故事的寓意是,如果您是> z <- as.Date("2008-12-30") > z - months(1) [1] "2008-11-30" 用户,那么在许多情况下,您应首先考虑使用lubridate%m-%。不幸的是,%m+%不是一种特别令人难忘或易于理解的语法,在扫描文档时它也不会在包的功能列表中脱颖而出。例如,在pdf文档的书签中,%m-%在字母顺序问题上排在倒数第二位,离%m+%很远,大多数用户都在寻找这样的功能

month%m-%的'英语'别名可能是该套餐的有用补充,例如类似%m+%的内容与上述语法相同,但使用方法如下:

months.lastday

答案 3 :(得分:1)

这会更有效率:

n <- 1
df$month.min.1 <- df$month - n
df$month.min.1[df$month.min.1 < 1] <- df$month.min.1[df$month.min.1 < n] + 12

这适用于任何n < 12

答案 4 :(得分:0)

可能效率不高,但这可行

mydate <- as.Date('2013-12-29')
tail(seq(mydate, length.out=2, by="-1 month"),1))

如果要转换数据框,可能需要将其包装在函数中Vectorize