R语言 将时间间隔划分为每个小时并按比例分配值

relj7zay  于 2023-03-15  发布在  其他
关注(0)|答案(2)|浏览(225)

我正试图从这样的数据中得出小时持续时间

df1=data.frame(Start=c("18:12","18:42","20:12","22:30"),
               End=c("18:30","20:00","22:30","22:36"),
               Duration (hour)=c(0.3,1.3,2.3,0.1))

| 开始|完|持续时间(小时)|
| - ------|- ------|- ------|
| 十八点十二分|十八点半|0.3分|
| 十八点四十二分|晚上八点整|1.3岁|
| 20点12分|22点半|二、三|
| 22点半|二十二点三十六分|0.1分|
例如:在18-19时持续时间=0.6,因为0.3 +(18:42-19:00)在19-20时= 1,20-21时= 0.8(没有20:00-20:12(0.2)),21-22时= 1,22-23时= 0.6(在22:36时停止)
我期待这样
| 时钟|持续时间(小时)|
| - ------|- ------|
| 十八至十九岁|0.6分|
| 19岁至20岁|1个|
| 二十至二十一岁|0.8分|
| 二十一至二十二岁|1个|
| 二十二至二十三岁|0.6分|

hs1ihplo

hs1ihplo1#

下面我使用dplyover::over()中的自定义函数calc_time()为每个小时创建虚拟变量。我们可以将自定义函数 Package 在dplyr::summarise()中的sum中,以获得每个小时的总计数,然后我们只需要pivot_longer()

library(dplyr)
library(tidyr)
library(dplyover)

calc_time <- function(hour, start, end) {
  hour_start <- strptime(paste0(hour, ":00"), format = "%H:%M") 
  hour_end   <- strptime(paste0(hour + 1, ":00"), format = "%H:%M") 
  
  started_this_hour <- start >= hour_start & start < hour_end
  ended_this_hour   <- end <= hour_end & end >= hour_start
  
  started_before <- start < hour_start & end > hour_start
  
  case_when(started_this_hour & ended_this_hour ~ as.numeric((end - start)/60),
            started_before & ended_this_hour ~ as.numeric((end - hour_start)/60),
            started_this_hour ~ as.numeric((hour_end - start)/60),
            started_before ~ 1,
            TRUE ~ 0)
}

df1 %>% 
  mutate(across(c(Start, End), \(x) strptime(x, format = "%H:%M"))) %>% 
  summarise(over(18:22, # specify the hours you want to loop over, max is 0:23
              ~ sum(calc_time(.x, Start, End)),
              .names = "{x}to{x+1}"
              )
         ) %>% 
  pivot_longer(cols = everything(),
               names_to = "clock",
               values_to = "Duration (hour)")

#> # A tibble: 5 × 2
#>   clock  `Duration (hour)`
#>   <chr>              <dbl>
#> 1 18to19               0.6
#> 2 19to20               1  
#> 3 20to21               0.8
#> 4 21to22               1  
#> 5 22to23               0.6

数据来自OP

df1 <- data.frame(
  Start = c("18:12","18:42","20:12","22:30"),
  End   = c("18:30","20:00","22:30","22:36"),
  Duration = c(0.3,1.3,2.3,0.1)
  )

免责声明:我是{dplyover}的维护者,它不在CRAN上。
创建于2023年3月8日,使用reprex v2.0.2

7nbnzgx9

7nbnzgx92#

这是另一个完全基于dplyr的解决方案。我将时间转换为datetime对象,并创建了一个StartEnd之间的序列,间隔为1分钟。
然后,如果下一个小时的0分钟存在,我也计算前一个小时的行数,否则,只计算行数(参见mutate(val = if_else(...;)。这有一个缺陷,因为如果我们没有完整的小时,但有一个小时的开始或下一个小时的开始,那么我们将计数前一个小时和下一个小时,而我们不应该这样做。因此,我检查是否完整的小时存在或如果这个小时的开始和下一个小时不存在,如果是,我只是扣除1,否则我从总和扣除2。
我创建了一个数据框来显示所有可能的情况。

df1 <- data.frame(Start = c("06:16","07:36","08:18", "18:12",
                            "18:42","20:12","22:30", "23:15"),
                  End = c("06:42","08:18","10:00","18:30",
                          "20:00","22:30","22:36", "23:30"), 
                  Duration = c(0.433,0.7,1.7,0.3,1.3,2.3,0.1, 0.25))

#>   Start   End Duration
#> 1 06:16 06:42    0.433
#> 2 07:36 08:18    0.700
#> 3 08:18 10:00    1.700
#> 4 18:12 18:30     0.30
#> 5 18:42 20:00     1.30
#> 6 20:12 22:30     2.30
#> 7 22:30 22:36     0.10
#> 8 23:15 23:30     0.25
library(dplyr)
library(lubridate)

df1 %>% 
  mutate(across(Start:End, ~strptime(.x, format = "%H:%M"))) %>% 
  rowwise() %>% 
  do(data.frame(time=seq.POSIXt(.$Start,.$End,by="1 min"))) %>% 
  ungroup() %>% 
  mutate(Hour = hour(time), Minute = minute(time)) %>% 
  arrange(time) %>% 
  mutate(val = if_else(lead(Hour, default = Hour[n()]) == Hour + 1 & 
                       lead(Minute, default = Minute[n()]) == 0, 2, 1)) %>% 
  group_by(Clock = Hour) %>% 
  summarise(Duration = if_else( (sum(val) %in% c(61, 1)) | 
                                (all(Minute != 0) & all(diff(Minute) == 1)), 
                           sum(val)-1, sum(val)-2) / 60) %>% 
  ungroup() %>% 
  filter(Duration != 0) %>% 
  mutate(Clock = paste(Clock, Clock + 1, sep = "to"))
#> # A tibble: 10 x 2
#>    Clock  Duration
#>    <chr>     <dbl>
#>  1 6to7      0.433
#>  2 7to8      0.4  
#>  3 8to9      1    
#>  4 9to10     1    
#>  5 18to19    0.6  
#>  6 19to20    1    
#>  7 20to21    0.8  
#>  8 21to22    1    
#>  9 22to23    0.6  
#> 10 23to24    0.25

相关问题