Как группировать по значению в предыдущем ряду? - PullRequest
0 голосов
/ 27 сентября 2018

edit: Ответ ниже решил мой первоначальный вопрос, но я не упомянул, что в список также включены отдельные суда, и их необходимо учитывать.

У меня есть список судов, и я хочу создать маршруты на основе определенных критериев.Я создал следующий список и хочу создать TRACKID для каждого сегмента маршрута для каждого отдельного корабля.

VESSEL  Date    Sailing/Port
1      01.02.2016   SAILING
1      02.02.2016   SAILING
1      03.02.2016   SAILING
1      04.02.2016   SAILING
1      05.02.2016   SAILING
1      06.02.2016   PORT
1      07.02.2016   PORT
1      08.02.2016   PORT
1      09.02.2016   PORT
1      10.02.2016   SAILING
2      11.02.2016   SAILING
2      12.02.2016   SAILING
2      13.02.2016   SAILING
2      14.02.2016   PORT
2      15.02.2016   PORT
2      16.02.2016   SAILING

Если корабль плывет, я хочу, чтобы он сохранял тот же TRACKID, пока не войдет в ПОРТ.Первая половина позиций в PORT должна быть назначена тому же TRACKID, что и до входа в порт.Второй половине должен быть присвоен новый TRACKID, который остается до следующего порта.Мне также нужно изменить TRACKID, когда появится новый корабль.

VESSEL      Date      Sailing/Port  TRACKID     
   1      01.02.2016    SAILING     1
   1      02.02.2016    SAILING     1
   1      03.02.2016    SAILING     1
   1      04.02.2016    SAILING     1
   1      05.02.2016    SAILING     1
   1      06.02.2016    PORT        1
   1      07.02.2016    PORT        1
   1      08.02.2016    PORT        2
   1      09.02.2016    PORT        2
   1      10.02.2016    SAILING     2
   2      11.02.2016    SAILING     3
   2      12.02.2016    SAILING     3
   2      13.02.2016    SAILING     3
   2      14.02.2016    PORT        3
   2      15.02.2016    PORT        4
   2      16.02.2016    SAILING     4

Приведенные ниже ответы смогли создать TRACKID с использованием sailed / port, но они не приняли во внимание новое судно.Случайно, если трек начинается в порту или в режиме плавания, и это также случайная дата.

Ответы [ 3 ]

0 голосов
/ 27 сентября 2018

Вот решение без for с использованием пакета tidyverse и пакета zoo (для na.locf).Он создает отдельный порядковый номер «a» для каждой последовательной последовательности SAILING или PORT, а затем меняет начало и конец каждой группы («PORT», a) на соответственно предыдущую или следующую группу SAILING.

group_number <- (function(){i = 0L; function() i <<- i+1L })()

df %>% 
  mutate(id=row_number(),
         a=ifelse(is.na(lag(Sailing.Port))|(lag(Sailing.Port)!=Sailing.Port),id,NA)) %>%
  mutate(a=na.locf(a)) %>%         # propagate the id of the 1st row of sequence
  group_by(a) %>%
  mutate(g=group_number()) %>%
  mutate(g=ifelse(Sailing.Port=="PORT",ifelse(row_number()<=(n()/2),g-1,g+1),g)) %>%
  ungroup %>% select(-a,-id)
## A tibble: 16 x 3
#   Date       Sailing.Port     g
#   <fct>      <fct>        <dbl>
# 1 01.02.2016 SAILING          1
# 2 02.02.2016 SAILING          1
# 3 03.02.2016 SAILING          1
# 4 04.02.2016 SAILING          1
# 5 05.02.2016 SAILING          1
# 6 06.02.2016 PORT             1
# 7 07.02.2016 PORT             1
# 8 08.02.2016 PORT             3
# 9 09.02.2016 PORT             3
#10 10.02.2016 SAILING          3
#11 11.02.2016 SAILING          3
#12 12.02.2016 SAILING          3
#13 13.02.2016 SAILING          3
#14 14.02.2016 PORT             3
#15 15.02.2016 PORT             5
#16 16.02.2016 SAILING          5
0 голосов
/ 27 сентября 2018

Вот решение с использованием data.table::rleid и dplyr.При этом используется кодирование длины серии, чтобы сначала создать переменную группировки на основе Sailing.Port (ID1).Для каждой группы установите индикатор на 1, если это вторая половина «ПОРТА», и 0 в противном случае (ID2).Используя этот индикатор, замените ID1 другой переменной группировки rle и сгенерируйте TRACKID с cumsum:

library(dplyr)
library(data.table)

df %>%
  group_by(ID1 = rleid(Sailing.Port)) %>%
  mutate(ID2 = if_else((row_number() > n()/2) & Sailing.Port == "PORT", 1, 0))  %>%
  ungroup() %>%
  group_by(ID1 = rleid(ID2)) %>%
  mutate(ID3 = if_else(ID2 == 1 & row_number() == 1, 1, 0)) %>%
  ungroup() %>%
  mutate(TRACKID = cumsum(ID3)+1) %>%
  select(-ID1, -ID2, -ID3)

Выход:

# A tibble: 16 x 3
   Date       Sailing.Port TRACKID
   <fct>      <fct>          <dbl>
 1 01.02.2016 SAILING            1
 2 02.02.2016 SAILING            1
 3 03.02.2016 SAILING            1
 4 04.02.2016 SAILING            1
 5 05.02.2016 SAILING            1
 6 06.02.2016 PORT               1
 7 07.02.2016 PORT               1
 8 08.02.2016 PORT               2
 9 09.02.2016 PORT               2
10 10.02.2016 SAILING            2
11 11.02.2016 SAILING            2
12 12.02.2016 SAILING            2
13 13.02.2016 SAILING            2
14 14.02.2016 PORT               2
15 15.02.2016 PORT               3
16 16.02.2016 SAILING            3

Данные:

df <- structure(list(Date = structure(1:16, .Label = c("01.02.2016", 
"02.02.2016", "03.02.2016", "04.02.2016", "05.02.2016", "06.02.2016", 
"07.02.2016", "08.02.2016", "09.02.2016", "10.02.2016", "11.02.2016", 
"12.02.2016", "13.02.2016", "14.02.2016", "15.02.2016", "16.02.2016"
), class = "factor"), Sailing.Port = structure(c(2L, 2L, 2L, 
2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, 2L), .Label = c("PORT", 
"SAILING"), class = "factor")), .Names = c("Date", "Sailing.Port"
), class = "data.frame", row.names = c(NA, -16L))
0 голосов
/ 27 сентября 2018

Я бы перебрал новый вектор.Примерно так.

temp.id<-1
for(n in 2:N){
 if(status[n]=="port" & status[n-1]=="sailing"){
  temp.id<-temp.id+1
 }
 leg[n]<-temp.id
}

Это только первый день в порту.Чтобы найти середину, вам понадобится второй цикл внутри.Примерно так.

temp.days<-0
for(m in (n+1):N){
 temp.days<-temp.days+1
 if(status[m]=="sailing"){break}
}

Затем вам нужно сделать некоторую арифметику и выяснить, как обрабатывать нечетное количество дней.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...