R中可扩展的结转以创建每日时间序列

Posted

技术标签:

【中文标题】R中可扩展的结转以创建每日时间序列【英文标题】:scalable carry forward in R to create daily time series 【发布时间】:2013-03-30 02:07:12 【问题描述】:

我正在尝试根据当前仅定期观察到的内容创建每日时间序列数据集。我可以成功地为单个案例执行所需的操作,但不知道如何扩展到整个数据集。例如:

        UNIT <- c(100,100, 200, 200, 200, 200, 200, 300, 300, 300,300)
        STATUS <- c('ACTIVE','INACTIVE','ACTIVE','ACTIVE','INACTIVE','ACTIVE','INACTIVE','ACTIVE','ACTIVE',
                    'ACTIVE','INACTIVE') 
        TERMINATED <- as.Date(c('1999-07-06' , '2008-12-05' , '2000-08-18' , '2000-08-18' ,'2000-08-18' ,'2008-08-18',
                        '2008-08-18','2006-09-19','2006-09-19' ,'2006-09-19' ,'1999-03-15')) 
        START <- as.Date(c('2007-04-23','2008-12-06','2004-06-01','2007-02-01','2008-04-19','2010-11-29','2010-12-30',
                   '2007-10-29','2008-02-05','2008-06-30','2009-02-07'))
        STOP <- as.Date(c('2008-12-05','2012-12-31','2007-01-31','2008-04-18','2010-11-28','2010-12-29','2012-12-31',
                  '2008-02-04','2008-06-29','2009-02-06','2012-12-31'))
        TEST <- data.frame(UNIT,STATUS,TERMINATED,START,STOP)
        TEST                   

这是对间隔单位的观察:

   UNIT   STATUS TERMINATED      START       STOP
1   100   ACTIVE 1999-07-06 2007-04-23 2008-12-05
2   100 INACTIVE 2008-12-05 2008-12-06 2012-12-31
3   200   ACTIVE 2000-08-18 2004-06-01 2007-01-31
4   200   ACTIVE 2000-08-18 2007-02-01 2008-04-18
5   200 INACTIVE 2000-08-18 2008-04-19 2010-11-28
6   200   ACTIVE 2008-08-18 2010-11-29 2010-12-29
7   200 INACTIVE 2008-08-18 2010-12-30 2012-12-31
8   300   ACTIVE 2006-09-19 2007-10-29 2008-02-04
9   300   ACTIVE 2006-09-19 2008-02-05 2008-06-29
10  300   ACTIVE 2006-09-19 2008-06-30 2009-02-06
11  300 INACTIVE 1999-03-15 2009-02-07 2012-12-31            

我想在 START 的整个范围内每天获取每个单元并复制“STATUS”和“TERMINATE”(以及大型数据集中的其他 N 个协变量)上的值和结束日期。为单条记录做这件事....

        A <-  seq(TEST$START[1], TEST$STOP[1], "days") #vector of relevant date sequences 

        #keeping the old data, now with daily date "fill" 
        B <- matrix(NA, length(A), dim(TEST[-c(4,5)])[2]) 
        C <- data.frame(A,B)

        #carry forward observations on covariates through date range 
        TEST[-c(4,5)][1,]  #note terminated has the proper date status:
        UNIT STATUS TERMINATED
         1  100 ACTIVE 1999-07-06

        #now the TERMINATED loses its 'date' status for some reason
        C[-c(1)][1,] <- TEST[-c(4,5)][1,] 
        D <-  na.locf(C)
        colnames(D)[2:4] <-colnames(TEST)[1:3]
        colnames(D)[1] <- "DATE"
        head(D)

        DATE UNIT STATUS TERMINATED
1 2007-04-23  100      1      10778
2 2007-04-24  100      1      10778
3 2007-04-25  100      1      10778
4 2007-04-26  100      1      10778
5 2007-04-27  100      1      10778
6 2007-04-28  100      1      10778

第一行的观测值在 START 到 END 范围内重复,并创建一个新向量:整个期间的每日时间序列。我想对第 2 行执行此操作,通过 UNIT 分析将其绑定到 D 等等。我用 na.locf 编写了一个 for 循环,但尝试概括失败:

for(i in 1:nrow(TEST))
  for(j in 0:nrow(TEST)-1) 
  A <-  seq(TEST$START[i], TEST$STOP[i], "days")

  B <- matrix(NA, length(A), dim(TEST[-c(4,5)])[2])
  C <- data.frame(A,B)

  C[-c(1)][1,] <- TEST[-c(4,5)][i,] 
  assign(paste("D",i, sep=""),na.locf(C)) 

  #below here the code does not work. R does not recognize i and j as I intend
  #I haven't been able to overcome this using assign, evaluate etc. 
  colnames(Di)[2:4] <-colnames(TEST)[1:3]
  colnames(Di)[1] <- "DATE"

  D0 <- matrix(NA, 1, dim(Di)[2])
  assign(paste("D", j, sep = ""),Dj)
  rbind(Di,Dj)

   
              

单一记录“解决方案”的明显问题是处理“终止”日期。就在使用 na.locf 之前,它失去了它的日期状态。

我希望有更好的方法来看待这个问题,我只是因为无知而陷入了复杂的境地。

【问题讨论】:

【参考方案1】:

在SQL中比较容易做到,所以可以使用sqldf, 它将 data.frames 视为 SQL 表。

dates <- data.frame( date = seq.Date( min(TEST$START), max(TEST$STOP), by = 1 ) )
library(sqldf)
result <- sqldf( "
  SELECT *
  FROM TEST, dates
  WHERE START <= date AND date <= STOP
" )
head( result )

如果数据很大,可能值得将数据存储在数据库中, 并在那里进行计算。

# With SQLite, a database is just a file
library(RSQLite)
connection <- dbConnect( SQLite(), "/tmp/test.db" )  

# Copy the data.frames to the "Test" and "Dates" table.
# When transfering data across systems, it is often easier 
# to convert dates to strings.
convert_dates <- function(d) 
  as.data.frame( lapply( 
    d, 
    function(u) if( "Date" %in% class(u) ) as.character(u) else u 
  ) ) 

dbWriteTable(connection, "Test",  convert_dates(TEST),  row.names = FALSE )
dbWriteTable(connection, "Dates", convert_dates(dates), row.names = FALSE )

# Check how many rows the query has: it could be 
# that the result does not fit in memory
dbGetQuery( connection, "
  SELECT COUNT(*) 
  FROM   Test, Dates 
  WHERE  start <= date AND date <= stop
" )

# If it is reasonable, retrieve all the data
dbGetQuery( connection, "
  SELECT * 
  FROM   Test, Dates 
  WHERE  start <= date AND date <= stop
" )

# If not, only retrieve what you need
dbGetQuery( connection, "
  SELECT * 
  FROM   Test, Dates 
  WHERE  start <= date AND date <= stop
  AND    '2013-04-01' <= date AND date <= '2013-04-30'
" )

【讨论】:

太好了,谢谢!这是一个非常有用的数据管理包。 在大数据框架中使用这个包有什么技巧吗?我目前遇到“无法分配大小为 N 的向量”的问题 如果数据太大,你可以在数据库中做任何事情:我已经相应地更新了我的答案。 (但数据量可能会被一些遥远的未来日期所解释,例如,在您的示例中,4712-12-31。) 谢谢!那是 STOP 向量中的错字,我现在已将其更改为 2012-12-31。我认为结果将是至少 100 万行数据。我怀疑结果不适合内存,所以我可能需要去云端。您建议的解决方案使用我的 Mac 在我的玩具示例上运行,但大数据库存储在 Windows 服务器上,并且在 sqliteNewConnection(drv, ...) 中的“连接”错误后出现以下错误:RS-DBI 驱动程序:(可以未连接到 dbname:无法打开数据库文件知道这是从哪里来的吗?我在包手册中找不到任何内容。 表示无法创建文件/tmp/test.db,因为没有/tmp目录。只需更改文件名即可。

以上是关于R中可扩展的结转以创建每日时间序列的主要内容,如果未能解决你的问题,请参考以下文章

在 SQL 查询中结转/结转余额

SQL Server 2012 - 使用积压和结转运行总计

如何在R中提取创建每日首次登机记录

Tensorflow 中可扩展、高效的分层 Softmax?

用友U8做期末处理时提示由于数据移动,未能继续以NOLCOK方式扫描

如何使 CMDIChildWnd 在 MFC 中可停靠?