Объедините 2 кадра данных, используя условия «час» и «минута» df1 в datetimes df2

У меня есть датафрейм df.sample вот так

id <- c("A","A","A","A","A","A","A","A","A","A","A")
date <- c("2018-11-12","2018-11-12","2018-11-12","2018-11-12","2018-11-12",
          "2018-11-12","2018-11-12","2018-11-14","2018-11-14","2018-11-14",
          "2018-11-12")
hour <- c(8,8,9,9,13,13,16,6,7,19,7)
min <- c(47,59,6,18,22,36,12,32,12,21,47)
value <- c(70,70,86,86,86,74,81,77,79,83,91)
df.sample <- data.frame(id,date,hour,min,value,stringsAsFactors = F) 
df.sample$date <- as.Date(df.sample$date,format = "%Y-%m-%d")

У меня есть еще один фрейм данных df.state вот такой

id <- c("A","A","A")
starttime <- c("2018-11-12 08:59:00","2018-11-14 06:24:17","2018-11-15 09:17:00")
endtime <- c("2018-11-12 15:57:00","2018-11-14 17:22:16","2018-11-15 12:17:32")
state <- c("Pass","Pass","Pass")

df.state <- data.frame(id,starttime,endtime,state,stringsAsFactors = F) 
df.state$starttime <- as.POSIXct(df.state$starttime,format = "%Y-%m-%d %H:%M:%S")
df.state$endtime <- as.POSIXct(df.state$endtime,format = "%Y-%m-%d %H:%M:%S")

Я пытаюсь объединить эти 2 фрейма данных на основе условия

если hour и min в df.sample находятся внутри starttime и endtime в df.state, то объедините state = Pass в df.sample.

Например, строка 2 в df.sample содержит hour = 8, min = 59, и поскольку она находится внутри starttime = 2018-11-12 08:59:00 в df.state, добавляется значение Pass.

Вот мой желаемый результат

   id       date hour min value state
    A 2018-11-12    8  47    70      
    A 2018-11-12    8  59    70  Pass
    A 2018-11-12    9   6    86  Pass
    A 2018-11-12    9  18    86  Pass
    A 2018-11-12   13  22    86  Pass
    A 2018-11-12   13  36    74  Pass
    A 2018-11-12   16  12    81      
    A 2018-11-14    6  32    77  Pass
    A 2018-11-14    7  12    79  Pass
    A 2018-11-14   19  21    83      
    A 2018-11-12    7  47    91      

Я могу объединить эти 2 фрейма данных, как это, но не могу найти час и минуту df.sample в начальное и конечное время df.state

library(tidyverse)
df.sample <- df.sample %>%
  left_join(df.state)

Может ли кто-нибудь указать мне в правильном направлении

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
98
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

(Важное предварительное примечание: as.POSIXct создает значения POSIXct с местным часовым поясом, тогда как lubridate::ymd создает время UTC. Вы получите неожиданные результаты, если часовые пояса различаются в вашем соединении ниже.)

df.state$starttime <- lubridate::ymd_hms(df.state$starttime)
df.state$endtime <- lubridate::ymd_hms(df.state$endtime)

Это можно сделать с помощью fuzzyjoin:

library(fuzzyjoin)
df.sample %>%
  mutate(sample_time = lubridate::ymd_hm(paste(date, hour, min))) %>%
  fuzzy_left_join(df.state, 
                  by = c("id" = "id",
                         "sample_time" = "starttime",
                         "sample_time" = "endtime"),
                  match_fun = list(`==`, `>=`, `<=`))

   id.x       date hour min value         sample_time id.y           starttime             endtime state
1     A 2018-11-12    8  47    70 2018-11-12 08:47:00 <NA>                <NA>                <NA>  <NA>
2     A 2018-11-12    8  59    70 2018-11-12 08:59:00    A 2018-11-12 08:59:00 2018-11-12 15:57:00  Pass
3     A 2018-11-12    9   6    86 2018-11-12 09:06:00    A 2018-11-12 08:59:00 2018-11-12 15:57:00  Pass
4     A 2018-11-12    9  18    86 2018-11-12 09:18:00    A 2018-11-12 08:59:00 2018-11-12 15:57:00  Pass
5     A 2018-11-12   13  22    86 2018-11-12 13:22:00    A 2018-11-12 08:59:00 2018-11-12 15:57:00  Pass
6     A 2018-11-12   13  36    74 2018-11-12 13:36:00    A 2018-11-12 08:59:00 2018-11-12 15:57:00  Pass
7     A 2018-11-12   16  12    81 2018-11-12 16:12:00 <NA>                <NA>                <NA>  <NA>
8     A 2018-11-14    6  32    77 2018-11-14 06:32:00    A 2018-11-14 06:24:17 2018-11-14 17:22:16  Pass
9     A 2018-11-14    7  12    79 2018-11-14 07:12:00    A 2018-11-14 06:24:17 2018-11-14 17:22:16  Pass
10    A 2018-11-14   19  21    83 2018-11-14 19:21:00 <NA>                <NA>                <NA>  <NA>
11    A 2018-11-12    7  47    91 2018-11-12 07:47:00 <NA>                <NA>                <NA>  <NA>
Ответ принят как подходящий

Использование неэквивалентного соединения из пакета data.table намного быстрее и проще, если у вас есть большие фреймы данных:Ориентир | видео

library(data.table)

## convert both data.frames to data.tables by reference
setDT(df.sample)
setDT(df.state) 

## create a `time` column in df.sample 
df.sample[, time := as.POSIXct(paste0(date, " ", hour, ":", min, ":00"))]
## change column order
setcolorder(df.sample, c("id", "time"))

# join by id and time within start & end time limits
# "x." is used so we can refer to the column in other data.table explicitly
df.state[df.sample, .(id, time, date, hour, min, value, state = x.state), 
         on = .(id, starttime <= time, endtime >= time)]
#>     id                time       date hour min value state
#>  1:  A 2018-11-12 08:47:00 2018-11-12    8  47    70  <NA>
#>  2:  A 2018-11-12 08:59:00 2018-11-12    8  59    70  Pass
#>  3:  A 2018-11-12 09:06:00 2018-11-12    9   6    86  Pass
#>  4:  A 2018-11-12 09:18:00 2018-11-12    9  18    86  Pass
#>  5:  A 2018-11-12 13:22:00 2018-11-12   13  22    86  Pass
#>  6:  A 2018-11-12 13:36:00 2018-11-12   13  36    74  Pass
#>  7:  A 2018-11-12 16:12:00 2018-11-12   16  12    81  <NA>
#>  8:  A 2018-11-14 06:32:00 2018-11-14    6  32    77  Pass
#>  9:  A 2018-11-14 07:12:00 2018-11-14    7  12    79  Pass
#> 10:  A 2018-11-14 19:21:00 2018-11-14   19  21    83  <NA>
#> 11:  A 2018-11-12 07:47:00 2018-11-12    7  47    91  <NA>

### remove NA
df.state[df.sample, .(id, time, date, hour, min, value, state = x.state), 
         on = .(id, starttime <= time, endtime >= time), nomatch = 0L]
#>    id                time       date hour min value state
#> 1:  A 2018-11-12 08:59:00 2018-11-12    8  59    70  Pass
#> 2:  A 2018-11-12 09:06:00 2018-11-12    9   6    86  Pass
#> 3:  A 2018-11-12 09:18:00 2018-11-12    9  18    86  Pass
#> 4:  A 2018-11-12 13:22:00 2018-11-12   13  22    86  Pass
#> 5:  A 2018-11-12 13:36:00 2018-11-12   13  36    74  Pass
#> 6:  A 2018-11-14 06:32:00 2018-11-14    6  32    77  Pass
#> 7:  A 2018-11-14 07:12:00 2018-11-14    7  12    79  Pass

Created on 2019-05-23 by the reprex package (v0.3.0)

Этот один сравнивает dplyr и data.table при выполнении неэквивалентного соединения для кадров больших данных.

Tung 23.05.2019 21:20

Это можно сделать, сначала добавив столбец времени в ваш df.sample data.frame, а затем выполнив оценку на основе ваших критериев, используя sapply и добавив этот результат в df.sample

df.sample$time <- paste0(df.sample$date, ' ', sprintf('%02d', df.sample$hour),':', sprintf('%02d', df.sample$min), ':00')
df.sample$state <- sapply(df.sample$time, function(x) {
  after_start <- x >= df.state$starttime
  before_end <- x <= df.state$endtime
  y <- cbind(after_start, before_end)
  pass_check <- apply(y, 1, sum)
  if (2 %in% pass_check) {'PASS'} else {''}
  })

df.sample

   id       date hour min value                time state
1   A 2018-11-12    8  47    70 2018-11-12 08:47:00      
2   A 2018-11-12    8  59    70 2018-11-12 08:59:00  PASS
3   A 2018-11-12    9   6    86 2018-11-12 09:06:00  PASS
4   A 2018-11-12    9  18    86 2018-11-12 09:18:00  PASS
5   A 2018-11-12   13  22    86 2018-11-12 13:22:00  PASS
6   A 2018-11-12   13  36    74 2018-11-12 13:36:00  PASS
7   A 2018-11-12   16  12    81 2018-11-12 16:12:00      
8   A 2018-11-14    6  32    77 2018-11-14 06:32:00  PASS
9   A 2018-11-14    7  12    79 2018-11-14 07:12:00  PASS
10  A 2018-11-14   19  21    83 2018-11-14 19:21:00      
11  A 2018-11-12    7  47    91 2018-11-12 07:47:00 

Что я сделал, так это извлек десятичный час из каждого предоставленного вами фрейма данных, чтобы я мог спросить, найдено ли значение в этом десятичном часе. Но сначала вам нужно объединить наборы данных на основе идентификатора (при условии, что у вас есть другие идентификаторы) и даты (при условии, что в день существует только одно состояние или, другими словами, одна дата существует в день в наборе данных df.state).

id <- c("A","A","A","A","A","A","A","A","A","A","A")
date <- c("2018-11-12","2018-11-12","2018-11-12","2018-11-12","2018-11-12",
          "2018-11-12","2018-11-12","2018-11-14","2018-11-14","2018-11-14",
          "2018-11-12")
hour <- c(8,8,9,9,13,13,16,6,7,19,7)
min <- c(47,59,6,18,22,36,12,32,12,21,47)
value <- c(70,70,86,86,86,74,81,77,79,83,91)
df.sample <- data.frame(id,date,hour,min,value,stringsAsFactors = F) 
df.sample$date <- as.Date(df.sample$date,format = "%Y-%m-%d")

df.sample$dec.hour <- as.numeric(df.sample$hour) +
  as.numeric(df.sample$min)/60

Все, что я добавил выше, - это последние несколько строк для расчета десятичного часа из предоставленных вами значений часа и минуты.

id <- c("A","A","A")
starttime <- c("2018-11-12 08:59:00","2018-11-14 06:24:17","2018-11-15 09:17:00")
endtime <- c("2018-11-12 15:57:00","2018-11-14 17:22:16","2018-11-15 12:17:32")
state <- c("Pass","Pass","Pass")

df.state <- data.frame(id,starttime,endtime,state,stringsAsFactors = F) 

Здесь я добавил вектор даты (для слияния). Я произвольно выбрал время начала, предполагая, что даты начала и окончания всегда одинаковы.

df.state$date <- as.Date(df.state$starttime,format = "%Y-%m-%d") 

Затем я получаю десятичный час для времени начала и окончания в эту дату.

t.str <- strptime(df.state$starttime, "%Y-%m-%d %H:%M:%S")
df.state$dec.hour.start <- as.numeric(format(t.str, "%H")) +
  as.numeric(format(t.str, "%M"))/60

t.end <- strptime(df.state$endtime, "%Y-%m-%d %H:%M:%S")
df.state$dec.hour.end <- as.numeric(format(t.end, "%H")) +
  as.numeric(format(t.end, "%M"))/60

объединить кадры данных по идентификатору и дате

df<-merge(df.sample, df.state, by=c("id","date"))

если десятичный час выборки находится в пределах начального или конечного десятичного часа (для этой даты), то возвращает TRUE для состояния.

df<-df %>% 
  mutate(state = dec.hour >= dec.hour.start & dec.hour <= dec.hour.end) 

Теперь, если вы хотите избавиться от всех этих дополнительных столбцов, которые я создал (так что это выглядит как желаемый результат):

df<-df[,-c(6:8,10:11)]

Поскольку df$state логичен, вам нужно сначала преобразовать значения в символы, если вы хотите изменить TRUE на проход, а FALSE на пробел:

df$state<-as.character(df$state)
df$state[df$state= = "TRUE"]<-"pass"
df$state[df$state= = "FALSE"]<-""

Посмотри:

df

> df
   id       date hour min value state
1   A 2018-11-12    8  47    70      
2   A 2018-11-12    8  59    70  pass
3   A 2018-11-12    9   6    86  pass
4   A 2018-11-12    9  18    86  pass
5   A 2018-11-12   13  22    86  pass
6   A 2018-11-12   13  36    74  pass
7   A 2018-11-12   16  12    81      
8   A 2018-11-12    7  47    91      
9   A 2018-11-14    6  32    77  pass
10  A 2018-11-14    7  12    79  pass
11  A 2018-11-14   19  21    83      

Я использовал этот пост: извлекать часы и секунды из POSIXct для построения графиков в R для извлечения десятичных часов и этот: Проверить, находится ли значение в диапазоне?, чтобы увидеть, было ли время выборки в пределах времени вашего состояния.

Другие вопросы по теме