我有一个带有两个变量,名称和日期的df。我想创建一个新列(new_dates),它取第一个属于每个人的日期(每个人在此列中只有一个重复的日期),并在行下降时为每个日期添加30天。
所需的输出如下。因此每个人的row1保存原始日期,row2保存row1 + 30,row3保持row2 + 30,依此类推。
dff
names dates new_dates
1 john 2010-06-01 2010-06-01
2 john 2010-06-01 2010-07-01
3 john 2010-06-01 2010-07-31
4 john 2010-06-01 2010-08-30
5 mary 2010-07-09 2010-07-09
6 mary 2010-07-09 2010-08-08
7 mary 2010-07-09 2010-09-07
8 mary 2010-07-09 2010-10-07
9 tom 2010-06-01 2010-06-01
10 tom 2010-06-01 2010-07-01
11 tom 2010-06-01 2010-07-31
12 tom 2010-06-01 2010-08-30
我以为我可以使用变换。这是我的尝试 - 但它对我不起作用。
dt <- transform(df, new_date = c(dates[2]+30, NA))
答案 0 :(得分:1)
绝对是一种蛮力方法,我的编程不是,你怎么说,优雅,但它似乎给出了理想的结果:
df <- psych::read.clipboard()
df <- data.frame(names = df$names,
dates = as.Date(df$dates))
library(lubridate)
tmp <- unlist(lapply(unique(df$names), function(x) {
tmp <- df[df$names == x, 2, drop = FALSE]
sapply(1:dim(tmp)[1], function(y) {
tmp[1, 1] + days(30) * (y - 1)
})
} ))
df$new_dates <- as.Date(tmp, origin = '1970-01-01')
> df
names dates new_dates
1 john 2010-06-01 2010-06-01
2 john 2010-06-01 2010-07-01
3 john 2010-06-01 2010-07-31
4 john 2010-06-01 2010-08-30
5 mary 2010-07-09 2010-07-09
6 mary 2010-07-09 2010-08-08
7 mary 2010-07-09 2010-09-07
8 mary 2010-07-09 2010-10-07
9 tom 2010-06-01 2010-06-01
10 tom 2010-06-01 2010-07-01
11 tom 2010-06-01 2010-07-31
12 tom 2010-06-01 2010-08-30
答案 1 :(得分:1)
data.table
让这很容易。转换为数据表后,它基本上就是一个命令。您的版本遇到的主要问题是您需要先按名称拆分数据,这样您就可以获得每个人的最短日期,然后在每个日期添加适当的30天。
library(data.table)
df$dates <- as.Date(df$dates)
dt <- as.data.table(df)
dt[,
list(dates, new_dates=min(dates) + 0:(length(dates) - 1L) * 30),
by=names
]
# names dates new_dates
# 1: john 2010-06-01 2010-06-01
# 2: john 2010-06-01 2010-07-01
# 3: john 2010-06-01 2010-07-31
# 4: john 2010-06-01 2010-08-30
# 5: mary 2010-07-09 2010-07-09
# 6: mary 2010-07-09 2010-08-08
# 7: mary 2010-07-09 2010-09-07
# 8: mary 2010-07-09 2010-10-07
# 9: tom 2010-06-01 2010-06-01
# 10: tom 2010-06-01 2010-07-01
# 11: tom 2010-06-01 2010-07-31
# 12: tom 2010-06-01 2010-08-30
编辑:这是一个版本,希望能说明你的工作原因。我仍然更喜欢data.table
,但希望因为这基本上非常接近你正在做的事情,所以它清楚地说明你需要改变什么:
re_date <- function(df) {
transform(
df[order(df$dates), ],
new_dates=min(dates) + 30 * 0:(length(dates) - 1L)
) }
do.call(rbind, lapply(split(df, df$name), re_date))
从底线(do.call...
)开始,split
调用会生成一个包含三个数据框的列表,一个包含John的值,一个用于Mary的值,另一个用于Tom的值。然后,lapply
会通过re_date
函数运行每个数据框,这会添加new_dates
列,最后do.call
/ rbind
将其重新拼接一起成为一个数据框架。
答案 2 :(得分:0)
你正在寻找的东西对我来说有点混乱。我假设您从一个看起来像这样的小数据框开始:
> df <- data.frame(names=c("john","mary","tom"),dates=c(as.Date("2010-06-01"),as.Date("2010-07-09"),as.Date("2010-06-01")))
> df
names dates
1 john 2010-06-01
2 mary 2010-07-09
3 tom 2010-06-01
然后想要在数据框中添加N行,这些行包含新的日期列。如果是这样,我确信有一些预先打包的方法可以执行此操作,但您也可以使用两个嵌套的lapply()
调用。最里面的调用只是添加一个新列,其中newdates被设置为30的倍数加上你的原始日期,然后最外面的调用将以30的倍数传递。例如:
> do.call(rbind,lapply(30*0:3,function(y) do.call(rbind,lapply(1:nrow(df),function(x) data.frame(names=df$names[x],dates=df$dates[x],newdates=df$dates[x]+y)))))
names dates newdates
1 john 2010-06-01 2010-06-01
2 mary 2010-07-09 2010-07-09
3 tom 2010-06-01 2010-06-01
4 john 2010-06-01 2010-07-01
5 mary 2010-07-09 2010-08-08
6 tom 2010-06-01 2010-07-01
7 john 2010-06-01 2010-07-31
8 mary 2010-07-09 2010-09-07
9 tom 2010-06-01 2010-07-31
10 john 2010-06-01 2010-08-30
11 mary 2010-07-09 2010-10-07
12 tom 2010-06-01 2010-08-30
无论如何,这种方法并不理想,可能会令人困惑,所以请告诉我这是否是您正在寻找的内容,我可以提供有关正在发生的事情的更多详细信息。