根据r中的开始时间和结束时间计算白天和晚上的小时数

时间:2019-04-12 04:59:03

标签: r datetime lubridate

我想根据R中的开始时间和结束时间来计算白天和黑夜的分钟数。为了简化问题,我假设日出时间总是6点,日落时间总是6点,不论时区和位置如何。

以下是一些示例数据:

dat = structure(list(
  start_time = structure(c(1431096404, 1431107312, 1431124632, 1431163956, 1431170210, 1431180438, 1431225936, 1431431610, 1431434550, 1431450416, 1431457208), 
class = c("POSIXct", "POSIXt"), tzone = "America/Chicago"), 
  end_time = structure(c(1431104384, 1431119732, 1431126312, 1431168936, 1431179030, 1431193878, 1431240696, 1431432150, 1431447870, 1431455096, 1431465728),
class = c("POSIXct", "POSIXt"), tzone = "America/Chicago")), 
  row.names = c(NA, -11L), 
  class = "data.frame")

它看起来像下面的数据框:

            start_time            end_time
1  2015-05-08 09:46:44 2015-05-08 11:59:44
2  2015-05-08 12:48:32 2015-05-08 16:15:32
3  2015-05-08 17:37:12 2015-05-08 18:05:12
4  2015-05-09 04:32:36 2015-05-09 05:55:36
5  2015-05-09 06:16:50 2015-05-09 08:43:50
6  2015-05-09 09:07:18 2015-05-09 12:51:18
7  2015-05-09 21:45:36 2015-05-10 01:51:36
8  2015-05-12 06:53:30 2015-05-12 07:02:30
9  2015-05-12 07:42:30 2015-05-12 11:24:30
10 2015-05-12 12:06:56 2015-05-12 13:24:56
11 2015-05-12 14:00:08 2015-05-12 16:22:08

我想添加两列minutes_dayminutes_nightminutes_day是该特定时段在白天(早上6点至下午6点)的分钟数,而minutes_night是该特定时段在夜晚(下午6点至早上6点)的分钟数。第二天)。所以我想要的data.frame如下:

            start_time            end_time minutes_day minutes_night
1  2015-05-08 09:46:44 2015-05-08 11:59:44         133             0
2  2015-05-08 12:48:32 2015-05-08 16:15:32         207             0
3  2015-05-08 17:37:12 2015-05-08 18:05:12          23             5
4  2015-05-09 04:32:36 2015-05-09 05:55:36           0            83
5  2015-05-09 06:16:50 2015-05-09 08:43:50         147             0
6  2015-05-09 09:07:18 2015-05-09 12:51:18         224             0
7  2015-05-09 21:45:36 2015-05-10 01:51:36           0           246
8  2015-05-12 06:53:30 2015-05-12 07:02:30           9             0
9  2015-05-12 07:42:30 2015-05-12 11:24:30         222             0
10 2015-05-12 12:06:56 2015-05-12 13:24:56          78             0
11 2015-05-12 14:00:08 2015-05-12 16:22:08         142             0

由于start_timeend_time之间的某些日期不同,这个问题对我来说变得非常棘手。

有人在这个问题上有线索吗?谢谢!

2 个答案:

答案 0 :(得分:1)

library(lubridate)
library(dplyr)

此函数创建从start_timeend_time的分钟序列,提取结果时间的小时数,将它们分隔为白天和黑夜,并获取最早时间与最新时间之间的时差白天和晚上。

get_minutes <- function(start_time, end_time) {
  mins_in_range <- seq(start_time, end_time, by = "mins")
  h_between <- hour(mins_in_range)

  hours_day <- mins_in_range[h_between >= 6 &
                     h_between < 18]
  hours_night <- mins_in_range[h_between < 6 |
                                 h_between >= 18]
  minutes_day <- tryCatch(as.numeric(difftime(max(hours_day),
                                              min(hours_day),
                                              units = "mins")),
                          warning = function(w) {
                            0
                          })

  minutes_night <- tryCatch(as.numeric(difftime(max(hours_night),
                                                min(hours_night),
                                                units = "mins")),
                            warning = function(w) {
                              0
                            })
  return(list(minutes_day = minutes_day, 
              minutes_night = minutes_night))
}

然后,您可以遍历每行数据,应用函数(返回list列)并将列表分隔为列(借助data.tablerbindlist

dat %>% 
  rowwise() %>% 
  mutate(temp = list(get_minutes(start_time, end_time))) %>% 
  cbind(data.table::rbindlist(.$temp)) %>% 
  select(-temp)

最终结果如下:

             start_time            end_time minutes_day minutes_night
 1: 2015-05-08 09:46:44 2015-05-08 11:59:44         133             0
 2: 2015-05-08 12:48:32 2015-05-08 16:15:32         207             0
 3: 2015-05-08 17:37:12 2015-05-08 18:05:12          22             5
 4: 2015-05-09 04:32:36 2015-05-09 05:55:36           0            83
 5: 2015-05-09 06:16:50 2015-05-09 08:43:50         147             0
 6: 2015-05-09 09:07:18 2015-05-09 12:51:18         224             0
 7: 2015-05-09 21:45:36 2015-05-10 01:51:36           0           246
 8: 2015-05-12 06:53:30 2015-05-12 07:02:30           9             0
 9: 2015-05-12 07:42:30 2015-05-12 11:24:30         222             0
10: 2015-05-12 12:06:56 2015-05-12 13:24:56          78             0
11: 2015-05-12 14:00:08 2015-05-12 16:22:08         142             0

答案 1 :(得分:1)

它需要很多编码,但是我认为这可以完成工作。实际上,它每天从suncalc包中获取实际的日落和日出时间。

我会尽快对其进行注释。

一天的计算

当开始日期和结束日期相同时,此功能以秒为单位计算昼夜。为了获得确切的日出和日落时间,您需要提供位置的纬度和经度。

library(lubridate)
library(tidyverse)
library(suncalc)

calc_in_oneday <- function(st, ed, lon = 0, lat = 0) {
    sunlight_times <- getSunlightTimes(as.Date(st), lat = lat, lon = lon)
    sunset <- sunlight_times$sunset
    sunrise <- sunlight_times$sunrise
    sec_night <- sec_day <- 0

    if(st > sunset | ed<=sunrise) { # when the period includes the night only
        sec_night  <- difftime(ed, st, units = "secs")
    } else if(st > sunrise & ed<=sunset) { # when the period includes the daytime only
        sec_day  <- difftime(ed, st, units = "secs")
    } else { # when things are bit more complicated
        if (st<=sunrise) { # if "start" is before sunrise time until sunrise will be added to night
            sec_night <- sec_night + difftime(sunrise, st, units = "secs")
        } else {  
            # if otherwise time until sunset will be added to daytime 
            # in this condition "end" will come after sunset (otherwise the second condition above will be satisfied)
            sec_day <- sec_day + difftime(sunset, st, units = "secs")
        } 
        if (ed<=sunset) { # The same logic
            sec_day <- sec_day + difftime(ed, sunrise, units = "secs")
        } else {
            sec_night <- sec_night + difftime(ed, sunset, units = "secs")
        } 
        if(st <= sunrise & ed > sunset) { # above will not add the entire daytime when "start" before sunrise and "end" after sunset
            sec_day <- sec_day + difftime(sunset, sunrise, units = "secs")
        }
    }
    sec_night <- unclass(sec_night)
    sec_day <- unclass(sec_day)
    attr(sec_day, "units") <- NULL
    attr(sec_night, "units") <- NULL
    return(list(sec_day = sec_day, sec_night = sec_night))
}

嵌套条件很复杂。我相信这是正确的,但请您自己检查。

多天

使用以上功能,处理多日内的支票。该功能的作用是检查开始日期和结束日期,如果不相同,请计算直到第一个日期结束的白天/夜晚时间,然后将开始时间滑动到第二天的开始。 (编辑:开始/结束时间的时区)。


calc_day_night <- function(st, ed, lon = 0, lat = 0) {
    attr(st, "tzone") <- "UTC"
    attr(ed, "tzone") <- "UTC"

    sec_night <- sec_day <- 0
    while(as.Date(st) != as.Date(ed)) {
        tmp_ed <- as.Date(st) + days(1)
        day_night_oneday <- calc_in_oneday(st, tmp_ed, lon, lat)
        sec_night <- sec_night + day_night_oneday$sec_night
        sec_day <- sec_day + day_night_oneday$sec_day
        st <- tmp_ed
    }
    day_night_oneday <- calc_in_oneday(st, ed, lon, lat)
    sec_night <- sec_night + day_night_oneday$sec_night
    sec_day <- sec_day + day_night_oneday$sec_day
    return(list(sec_day = sec_day, sec_night = sec_night))
}

测试

使用测试数据,结果如下:

dat %>%  
    rowwise() %>%
    mutate(temp = list(calc_day_night(start_time, end_time, lat = 41, lon = -85))) %>%
    mutate(sec_day = temp$sec_day) %>%
    mutate(sec_night = temp$sec_night) %>%
    mutate(min_day = round(sec_day / 60)) %>%
    mutate(min_night = round(sec_night / 60)) %>%
    select(-matches("sec")) %>%
    select(-temp) 

## Source: local data frame [11 x 4]
## Groups: <by row>
## 
## # A tibble: 11 x 4
##    start_time          end_time            min_day min_night
##    <dttm>              <dttm>                <dbl>     <dbl>
##  1 2015-05-08 09:46:44 2015-05-08 11:59:44     133         0
##  2 2015-05-08 12:48:32 2015-05-08 16:15:32     207         0
##  3 2015-05-08 17:37:12 2015-05-08 18:05:12      28         0
##  4 2015-05-09 04:32:36 2015-05-09 05:55:36      26        57
##  5 2015-05-09 06:16:50 2015-05-09 08:43:50     147         0
##  6 2015-05-09 09:07:18 2015-05-09 12:51:18     224         0
##  7 2015-05-09 21:45:36 2015-05-10 01:51:36       0       246
##  8 2015-05-12 06:53:30 2015-05-12 07:02:30       9         0
##  9 2015-05-12 07:42:30 2015-05-12 11:24:30     222         0
## 10 2015-05-12 12:06:56 2015-05-12 13:24:56      78         0
## 11 2015-05-12 14:00:08 2015-05-12 16:22:08     142         0

我搜索了芝加哥的经纬度,并使用了这些值。如您所见,对于某些记录,结果有所变化(例如,由于芝加哥的黎明是在夏季初,所以记录#4并不完全是夜晚。