рассчитать часы в день и ночь на основе времени начала и окончания в г - PullRequest
3 голосов
/ 12 апреля 2019

Я хочу вычислить минуты днем ​​и ночью, основываясь на времени начала и времени окончания в 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_day и minutes_night. minutes_day - это сколько минут этого определенного периода было в течение дня (с 6:00 до 18:00), тогда как minutes_night было, сколько минут этого особого периода было в течение ночи (с 6:00 до 6:00 на следующий день) , Итак, мой желаемый 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_time до end_time.

У кого-нибудь есть подсказки по этой проблеме? Спасибо!

Ответы [ 2 ]

1 голос
/ 12 апреля 2019

Требуется немало кодирования, но я думаю, что это сработает.Фактически он получает фактическое время заката и восхода из пакета 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 не совсем ночная, так как рассвет Чикаго наступил в начале лета).

1 голос
/ 12 апреля 2019
library(lubridate)
library(dplyr)

Эта функция создает последовательность в минутах от start_time до end_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.table иrbindlist:

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
...