【问题标题】:Complete data frame with missing date ranges for multiple parameters完整的数据框,缺少多个参数的日期范围
【发布时间】:2018-07-12 15:58:19
【问题描述】:

我有以下数据框:

Date_from <- c("2013-02-01","2013-05-10","2013-08-13","2013-02-01","2013-05-10","2013-08-13","2013-02-01","2013-05-10","2013-08-13")
Date_to <- c("2013-05-07","2013-08-12","2013-11-18","2013-05-07","2013-08-12","2013-11-18","2013-05-07","2013-08-12","2013-11-18")
y <- data.frame(Date_from,Date_to)
y$concentration <- c("1.5","2.5","1.5","3.5","1.5","2.5","1.5","3.5","3")
y$Parameter<-c("A","A","A","B","B","B","C","C","C")
y$Date_from <- as.Date(y$Date_from)
y$Date_to <- as.Date(y$Date_to)
y$concentration <- as.numeric(y$concentration)

如果每个参数的日期范围从一年的第一天 (2013-01-01) 开始,到一年的最后一天 (2013-12-31) 结束,我将需要检查数据框。如果不是,我将需要在每个参数的开头和结尾添加一个额外的行,以完成每个参数的完整日期范围。结果应如下所示:

Date_from    Date_to concentration Parameter
2013-01-01 2013-01-31            NA        NA
2013-02-01 2013-05-07           1.5         A
2013-05-10 2013-08-12           2.5         A
2013-08-13 2013-11-18           1.5         A
2013-11-19 2013-12-31            NA        NA
2013-01-01 2013-01-31            NA        NA
2013-02-01 2013-05-07           3.5         B
2013-05-10 2013-08-12           1.5         B
2013-08-13 2013-11-18           2.5         B
2013-11-19 2013-12-31            NA        NA
2013-01-01 2013-01-31            NA        NA
2013-02-01 2013-05-07           1.5         C
2013-05-10 2013-08-12           3.5         C
2013-08-13 2013-11-18           3.0         C
2013-11-19 2013-12-31            NA        NA

请注意:为简化起见,本示例中的日期范围是相等的。

更新:这是我的原始数据 sn-p 和代码:

sm<-read.csv("https://www.dropbox.com/s/tft6inwcrjqujgt/Test_data.csv?dl=1",sep=";",header=TRUE)
cleaned_sm<-sm[,c(4,5,11,14)] ##Delete obsolete columns
colnames(cleaned_sm)<-c("Parameter","Concentration","Date_from","Date_to")
cleaned_sm$Date_from<-as.Date(cleaned_sm$Date_from, format ="%d.%m.%Y")     
cleaned_sm$Date_to<-as.Date(cleaned_sm$Date_to, format ="%d.%m.%Y") 
#detect comma decimal separator and replace with dot decimal separater as comma is not recognised as a number
cleaned_sm=lapply(cleaned_sm, function(x) gsub(",", ".", x))
cleaned_sm<-data.frame(cleaned_sm)
cleaned_sm$Concentration <- as.numeric(cleaned_sm$Concentration)
cleaned_sm$Date_from <- as.Date(cleaned_sm$Date_from)
cleaned_sm$Date_to <- as.Date(cleaned_sm$Date_to)

添加了基于@jasbner 的代码:

cleaned_sm %>%
   group_by(Parameter) %>%
   do(add_row(.,
                 Date_from = ymd(max(Date_to))+1 ,
                 Date_to = ymd(paste(year(max(Date_to)),"1231")),
                 Parameter = .$Parameter[1])) %>%
   do(add_row(.,
                 Date_to = ymd(min(Date_from))-1, 
                 Date_from = ymd(paste(year(min(Date_from)),"0101")) ,
                 Parameter = .$Parameter[1],
                 .before = 0)) %>% 
   filter(!duplicated(Date_from,fromLast = T),!duplicated(Date_to))

【问题讨论】:

  • 中间的空隙有关系吗?

标签: r


【解决方案1】:

我对@9​​87654321@ 和lubridate 的尝试。一起破解,但我认为它应该可以工作。请注意,这不会查找日期范围中间的任何间隙。基本上,对于每个组,您在该特定组之前和之后添加一行。然后,如果存在日期范围从年初开始或在年底结束的任何情况,则会过滤掉添加的行。

library(dplyr)
library(lubridate)
cleaned_sm %>%
  group_by(Parameter) %>%
  do(add_row(.,
             Date_from = ymd(max(.$Date_to))+1 ,
             Date_to = ymd(paste(year(max(.$Date_to)),"1231")),
             Parameter = .$Parameter[1])) %>%
  do(add_row(.,
             Date_to = ymd(min(.$Date_from))-1, 
             Date_from = ymd(paste(year(min(.$Date_from)),"0101")) ,
             Parameter = .$Parameter[1],
             .before = 0)) %>% 
  filter(!duplicated(Date_from,fromLast = T),!duplicated(Date_to))  

# A tibble: 15 x 4
# Groups: Parameter [3]
#    Date_from  Date_to    concentration Parameter
#    <date>     <date>             <dbl> <chr>    
#  1 2013-01-01 2013-01-31         NA    A        
#  2 2013-02-01 2013-05-07          1.50 A        
#  3 2013-05-10 2013-08-12          2.50 A        
#  4 2013-08-13 2013-11-18          1.50 A        
#  5 2013-11-19 2013-12-31         NA    A        
#  6 2013-01-01 2013-01-31         NA    B        
#  7 2013-02-01 2013-05-07          3.50 B        
#  8 2013-05-10 2013-08-12          1.50 B        
#  9 2013-08-13 2013-11-18          2.50 B        
# 10 2013-11-19 2013-12-31         NA    B        
# 11 2013-01-01 2013-01-31         NA    C        
# 12 2013-02-01 2013-05-07          1.50 C        
# 13 2013-05-10 2013-08-12          3.50 C        
# 14 2013-08-13 2013-11-18          3.00 C        
# 15 2013-11-19 2013-12-31         NA    C 

【讨论】:

  • 在我的示例中,您的代码有效,但在我的原始数据框中(请参阅原始帖子中的更新),代码会产生错误:Error in lapply(list(...), .num_to_date) : object 'Date_to' not found。我不知道为什么会产生此错误,因为我的原始数据框的结构与我的示例中的相同。
  • @Matt 立即尝试
  • 现在像魅力一样工作。
  • 必须在每个参数前添加.$ 才能正确引用。不太确定为什么在数据集之间需要/不需要。
【解决方案2】:

这似乎需要组合不同的包来攻击它。我用的是tidyrdata.table,我用的是lubridate

date.start <- seq.Date(as.Date("2013-01-01"), as.Date("2013-12-31"), by = "day")
Date.Int   <- data.frame(Date_from = date.start, Date_to = date.start)
y_wide     <- y %>% spread(Parameter, concentration)
y_wide     <- as.data.table(setkey(as.data.table(y_wide), Date_from, Date_to))
Date.Int    <- as.data.table(setkey(as.data.table(Date.Int), Date_from, Date_to))
dats <- foverlaps(Date.Int, y_wide, nomatch = NA)

fin.dat <- dats %>% 
mutate(A    = ifelse(is.na(A), -5, A), 
       seqs = cumsum(!is.na(A) & A != lag(A, default = -5))) %>% 
group_by(seqs) %>% 
summarise(Date_from = first(i.Date_from),                                                                                                    
          Date_to   = last(i.Date_to)   ,                                                                                                        
          A = first(A),                                                                                                        
          B = first(B),                                                                                                        
          C = first(C)) %>% 
          mutate(A = ifelse(A == -5, NA, A)) %>% 
          ungroup()%>% 
gather(Concentration, Parameter, A:C) %>% 
mutate(Concentration = ifelse(is.na(Parameter), NA, Concentration))

好的,所以我创建了一个从起点到终点的日期向量 (date.start);然后我变成了data.frame,与Date.Int 具有相同的间隔名称和间隔日期。这是因为foverlaps 需要比较两个间隔(Date.Int 中的相同日期开始和结束日期现在是正式的间隔)。然后我拿了你提供的数据和spread,把它从长格式数据变成宽格式数据,然后变成data.tablekeying a data.table 设置它的排列方式,使用foverlaps 时,您必须键入开始日期和结束日期(按此顺序)。 foverlaps 确定一个时间间隔是否在另一个日期时间间隔内。如果你打印出dats,你会看到一堆带有NA的行,因为它们不在一个区间内。所以现在我们必须以某种方式对它们进行分组。我在dats 中选择了按“A”值分组。分组变量称为seqs。但后来我总结了数据,然后将其从宽格式切换回长格式并替换了适当的 NA 值。

【讨论】:

  • 参数名称只是示例。在我的原始数据框中,有超过 15 个不同的参数名称。我觉得修改一长串参数名称的代码会有点麻烦。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-03
  • 1970-01-01
  • 2021-12-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-14
相关资源
最近更新 更多