Объедините 2 кадра данных, используя условия «час» и «мин» df1 в datetime df2 - PullRequest
3 голосов
/ 23 мая 2019

У меня есть датафрейм 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)

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

Ответы [ 4 ]

4 голосов
/ 23 мая 2019

Использование неэквивалентного соединения из пакета 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

Создано в 2019-05-23 с помощью пакета Представления (v0.3.0)

1 голос
/ 23 мая 2019

Я извлек десятичный час из каждого предоставленного вами кадра данных, чтобы я мог спросить, найдено ли значение в течение этого десятичного часа. Но сначала вам нужно объединить наборы данных на основе идентификатора (при условии, что у вас есть другие идентификаторы) и даты (при условии, что в день существует только одно состояние; или, другими словами, в наборе данных 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 , чтобы извлекать десятичные часы и вот этот: Проверьте, находится ли значение в пределах диапазона? , чтобы узнать, было ли время выборки в пределах времени вашего состояния.

1 голос
/ 23 мая 2019

Это можно сделать, сначала добавив столбец времени к вашему 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 
1 голос
/ 23 мая 2019

(Важное подготовительное примечание: 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...