R循环-有没有更有效的方法?

R循环-有没有更有效的方法?,r,loops,for-loop,nested-loops,R,Loops,For Loop,Nested Loops,我有一个数据框,其中每一行都有一个唯一的ID。我需要根据开始日期与结束日期和批准日期之间的最大天数复制这些行中的每一行 ID <- c(1,2) Value <- c(10,20) StartDate <- c(as.Date("01/01/2015", '%d/%m/%Y'), as.Date("01/01/2015", '%d/%m/%Y')) EndDate <- c(as.Date("31/01/2015", '%d/%m/%Y'), as

我有一个数据框,其中每一行都有一个唯一的ID。我需要根据开始日期与结束日期和批准日期之间的最大天数复制这些行中的每一行

ID <- c(1,2)

Value <- c(10,20)

StartDate <- c(as.Date("01/01/2015", '%d/%m/%Y'),
    as.Date("01/01/2015", '%d/%m/%Y'))

EndDate <- c(as.Date("31/01/2015", '%d/%m/%Y'), 
    as.Date("15/01/2015", '%d/%m/%Y'))

AppDate <- c(as.Date("15/01/2015", '%d/%m/%Y'), 
    as.Date("15/02/2015", '%d/%m/%Y'))

df <- data.frame(ID, Value, StartDate, EndDate, AppDate)

df <- df[rep(row.names(df), ifelse(as.numeric(df$AppDate) >
    as.numeric(df$EndDate),as.numeric(df$AppDate-df$StartDate),
    as.numeric(df$EndDate-df$StartDate)) + 1),]

ID这里有一个矢量化的解决方案。注意:您的代码不符合我尝试使用的EndDate和AppDate最大值的概念,但是如果这不是您想要的,您可以相应地修改代码

library(dplyr)
df <- df %>% group_by(ID) %>% mutate(Days = rep(seq(min(StartDate), max(EndDate, df$AppDate), 'days'), ceiling(nrow(df) / n()))[1:n()])

这是一个矢量化的解决方案。注意:您的代码不符合我尝试使用的EndDate和AppDate最大值的概念,但是如果这不是您想要的,您可以相应地修改代码

library(dplyr)
df <- df %>% group_by(ID) %>% mutate(Days = rep(seq(min(StartDate), max(EndDate, df$AppDate), 'days'), ceiling(nrow(df) / n()))[1:n()])

通常,我建议使用交叉连接SQL查询返回笛卡尔乘积(两个集合之间的所有组合)。但是,您可以使用
merge()
在R中复制交叉联接,而不使用任何
by
参数,并且使用
all=True
。从那里,筛选
EndDate
截止日期:

# CALCULATE CONDITIONAL END DATE
df$TrueEndDate <- as.Date(ifelse(df$AppDate > df$EndDate,
                                 df$AppDate,
                                 df$EndDate), origin="1970-01-01")

# CREATE A SEQUENTIAL DATES DATA FRAME (HERE IS 60 DAYS FROM 2015-01-01)
dates <- data.frame(Date=as.Date(unlist(lapply(0:60, function(x) 
                                      as.Date("2015-01-01") + x)),                    
                                 origin="1970-01-01"))   

# RUN CROSS JOIN MERGE, PULLING ONLY NEEDED FIELDS
mergedf <- merge(df[c('ID', 'StartDate', 'TrueEndDate')], dates, all=TRUE)

# FILTER OUT DATES PAST ROW'S TRUE END DATE
mergedf <- mergedf[(mergedf$Date <= mergedf$TrueEndDate),]

# CLEANUP
mergedf <- mergedf[with(mergedf, order(ID)), ]     # ORDER BY ID
row.names(mergedf) <- 1:nrow(mergedf)              # RESET ROW NAMES
#计算条件结束日期
df$TrueEndDate df$EndDate,
df$AppDate,
df$EndDate),origin=“1970-01-01”)
#创建连续日期数据框(这里是从2015-01-01开始的60天)

日期通常,我建议使用交叉连接SQL查询返回笛卡尔乘积(两个集合之间的所有组合)。但是,您可以使用
merge()
在R中复制交叉联接,而不使用任何
by
参数,并且使用
all=True
。从那里,筛选
EndDate
截止日期:

# CALCULATE CONDITIONAL END DATE
df$TrueEndDate <- as.Date(ifelse(df$AppDate > df$EndDate,
                                 df$AppDate,
                                 df$EndDate), origin="1970-01-01")

# CREATE A SEQUENTIAL DATES DATA FRAME (HERE IS 60 DAYS FROM 2015-01-01)
dates <- data.frame(Date=as.Date(unlist(lapply(0:60, function(x) 
                                      as.Date("2015-01-01") + x)),                    
                                 origin="1970-01-01"))   

# RUN CROSS JOIN MERGE, PULLING ONLY NEEDED FIELDS
mergedf <- merge(df[c('ID', 'StartDate', 'TrueEndDate')], dates, all=TRUE)

# FILTER OUT DATES PAST ROW'S TRUE END DATE
mergedf <- mergedf[(mergedf$Date <= mergedf$TrueEndDate),]

# CLEANUP
mergedf <- mergedf[with(mergedf, order(ID)), ]     # ORDER BY ID
row.names(mergedf) <- 1:nrow(mergedf)              # RESET ROW NAMES
#计算条件结束日期
df$TrueEndDate df$EndDate,
df$AppDate,
df$EndDate),origin=“1970-01-01”)
#创建连续日期数据框(这里是从2015-01-01开始的60天)

日期我认为本问答中的
数据表
答案提供了有效解决方案的一般原则:。谷歌“R expand date range data.table”应该提供几个类似的例子。我认为本问答中的
data.table
答案提供了有效解决方案的一般原则:。谷歌的“R扩展日期范围数据表”应该提供几个类似的例子。感谢它的完美工作。与我编写的代码相比,我喜欢矢量化版本的简单性。这比我预期的要慢得多,这是最快的方法吗?取决于数据的大小、组的数量和处理的日期范围。此外,如果您定义“慢”,它可能会有所帮助。有一个名为
data.table
的不同包被认为比
dplyr
更有效。我不使用它,因为我发现
dplyr
更加通用和直观(个人观点)。其他人可能会帮助您将代码转换为
data.table
版本。谢谢您,它工作得非常好。与我编写的代码相比,我喜欢矢量化版本的简单性。这比我预期的要慢得多,这是最快的方法吗?取决于数据的大小、组的数量和处理的日期范围。此外,如果您定义“慢”,它可能会有所帮助。有一个名为
data.table
的不同包被认为比
dplyr
更有效。我不使用它,因为我发现
dplyr
更加通用和直观(个人观点)。其他人可能可以帮助您将代码转换为
data.table
version。
SELECT ID.ID, ID.Value, ID.StartDate, 
       CASE WHEN ID.AppDate > ID.EndDate 
            THEN ID.AppDate 
            ELSE ID.EndDate 
       END As TrueEndDate, 
Dates.Dates
FROM ID, Dates
WHERE  Dates.Dates <= CASE WHEN ID.AppDate > ID.EndDate 
                           THEN ID.AppDate ELSE ID.EndDate 
                      END
ORDER BY ID.ID, Dates.Dates