R - Создание уникального идентификатора последовательности для строк во фрейме данных на основе значения в предыдущей строке - PullRequest
0 голосов
/ 24 мая 2019

Я работаю с набором данных о поездках, который предоставляет информацию для отдельных сегментов поездки каждого человека, включая расстояние и назначение сегмента (например, чтобы идти на работу, ходить по магазинам, возвращаться домой).Это организовано в датафрейме.Я хочу сгруппировать эти сегменты в то, что я называю «цепочечными» или «зацикленными» поездками, увеличивая число цепей / петель каждый раз, когда в предыдущем сегменте происходит возврат домой.

В r я попытался создать цикл for с содержащимся в нем ifelse, который ссылается на значения переменных из предыдущей итерации, а затем заполняет новый столбец, который я уже создал, с нулевыми значениями.(Я знаю, это звучит немного глупо).По сути, я пытался выработать что-то похожее на то, что я смог сделать в Excel, где я создал серию вложенных if.(= IF (DF2 = DF1, IF (DG1 = 11, DI1 + 1, DI1), 1). Это формула из третьей строки, которая относится к значениям в непосредственно предшествующей строке.

Этоэто вывод кода dput для данных:

structure(list(h_id = c(1000002L, 1000002L, 1000002L, 1000002L, 
1000013L, 1000013L, 1000013L, 1000013L, 1000013L, 1000013L, 1000013L, 1000013L), p_ID = c(10000022L, 10000022L, 10000022L, 
10000022L, 10000131L, 10000131L, 10000132L,10000132L, 10000132L,10000132L,10000132L,10000132L), t_pur = c(6L, 11L, 7L, 11L, 
5L, 11L, 1L, 2L, 2L, 11L, 6L, 11L), t_distance = c(753.154936, 753.154936, 4681.630497, 
4681.630497, 616.0517311, 616.0517311, 9626,7984, 641.3675, 15076.6182, 21407.5585, 24273.3116, 24273.3116), X = c(1L, 1L, 2L, 2L, 
1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L), Conc = c("10000022-1", "10000022-1", "10000022-2", "10000022-2", 
"10000131-1", "10000131-1", "10000132-1", "10000132-1", "10000132-1", "10000132-1", "10000132-2", "10000132-2" ), t_mode1 = c(1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 
1L), has_work = c(0, 0, 0, 0, 0, 0,1, 0, 0, 0, 0, 0), newcol = c(1, 1, 1, 1, 1, 
1, 1, 1, 1, 1,1, 1)), .Names = c("h_id", "p_ID", "t_pur", "t_distance", "X", "Conc", 
"t_mode1", "has_work", "newcol"), row.names = 3:14, class = "data.frame")
for (i in 1:nrow(loops4)) {
  ifelse(i == 1, loops4$newcol[i] <- 1,
         ifelse(loops4$p_ID[i-1]==loops4$p_ID,
                ifelse(loops4$t_pur[i-1]==11,
                       loops4$newcol[i] <- loops4$newcol[i-1]+1, 
                       loops4$newcol[i-1]), loops4$newcol[i] <- 1)) 
}

Здесь, loopps - это мой фрейм данных. newcol - это тот, который я создал для хранения идентификатора цикла. p_ID - уникальный идентификатор, связанный с каждым человеком,t_pur является целью сегмента поездки, а «11» является значением, которое соответствует цели «возвращения домой» (я хочу начинать новый идентификатор цикла каждый раз, когда предыдущий сегмент возвращался домой). После этого я могуобъединить уникальный идентификатор лица и идентификатор цикла для создания уникального идентификатора для каждого цикла.Первый ifelse только для первой записи, чтобы назначить 1 идентификатору цикла, так как не было бы никакого предшествующего значения для цикла, чтобы обратиться к

Я ожидал, что цикл будет проходить через каждую строку в кадре данных, сначала проверяя, ссылается ли запись на того же человека, что и в предыдущей записи.следует проверить, была ли цель поездки из предыдущего ряда «11» или нет.Если это так, он должен добавить 1 к предыдущему идентификатору, чтобы обозначить новый цикл.Если ему не предшествует цель отключения "11", ему следует назначить тот же идентификатор цикла, что и в предыдущей строке, и перейти к следующей строке.Когда он запускается, во-первых, кажется, что он занимает огромное количество времени, а во-вторых, он заполняет все на 1, а не увеличивает и перезапускает, как я ожидал.

Я ожидал такой кадр данных.X - это правильно рассчитанное значение из Excel.newcol - это столбец, в котором я пытался вычислить значение r в newcol.Значения newcol должны совпадать с X, но это не так.(Я обновил таблицу ниже, чтобы отразить в newcol то, что я надеялся увидеть в выходных данных).

   h_id     p_ID t_pur t_distance X       Conc t_mode1 has_work newcol
1000002 10000022     6      753.2 1 10000022-1       1        0      1
1000002 10000022    11      753.2 1 10000022-1       1        0      1
1000002 10000022     7     4681.6 2 10000022-2       1        0      2
1000002 10000022    11     4681.6 2 10000022-2       1        0      2
1000013 10000131     5      616.1 1 10000131-1       1        0      1
1000013 10000131    11      616.1 1 10000131-1       1        0      1
1000013 10000132     1     9626.8 1 10000132-1       1        1      1
1000013 10000132     2      641.4 1 10000132-1       1        0      1
1000013 10000132     2    15076.6 1 10000132-1       1        0      1
1000013 10000132    11    21407.6 1 10000132-1       1        0      1
1000013 10000132     6    24273.3 2 10000132-2       1        0      2
1000013 10000132    11    24273.3 2 10000132-2       1        0      2

ОБНОВЛЕНИЕ:

Я вернулся и немного подумал о назначении внутри конструкции ifelse, основываясь на комментарии ниже, и понял, что это не имеет особого смысла.Поэтому я попытался переписать код следующим образом:

for (i in 1:nrow(loops4)) {
  loops4$newcol[i] <- ifelse(i == 1, 1, ifelse (loops4$p_ID[i-1]==loops4$p_ID[i], ifelse(loops4$t_pur[i-1]==11, loops4$newcol[i-1]+1, loops$newcol[i-1], 1)))
}

Но я получил те же неожиданные результаты.

ОБНОВЛЕННОЕ ОБНОВЛЕНИЕ:

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

structure(list(h_id = c(1000002L, 1000002L, 1000002L, 1000002L, 
1000013L, 1000013L, 1000013L, 1000013L, 1000013L, 1000013L, 1000013L, 
1000013L), p_ID = c(10000022L, 10000022L, 10000022L, 10000022L, 
10000131L, 10000131L, 10000132L, 10000132L, 10000132L, 10000132L, 
10000132L, 10000132L), t_pur = c(6L, 11L, 7L, 11L, 5L, 11L, 1L, 
2L, 2L, 11L, 6L, 11L), t_distance = c(753.154936, 753.154936, 
4681.630497, 4681.630497, 616.0517311, 616.0517311, 9626.798385, 
641.3674532, 15076.61817, 21407.55851, 24273.31161, 24273.31161
), X = c(1L, 1L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L), Conc = c("10000022-1", 
"10000022-1", "10000022-2", "10000022-2", "10000131-1", "10000131-1", 
"10000132-1", "10000132-1", "10000132-1", "10000132-1", "10000132-2", 
"10000132-2"), t_mode1 = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L), has_work = c(0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0), 
    newcol = c(1L, 1L, 2L, 2L, 1L, 1L, 0L, 0L, 1L, 1L, 2L, 2L
    )), .Names = c("h_id", "p_ID", "t_pur", "t_distance", "X", 
"Conc", "t_mode1", "has_work", "newcol"), class = c("tbl_df", 
"tbl", "data.frame"), row.names = c(NA, -12L))

И я попробовал подход dplyr, предложенный ниже.

loops_good <- loops5 %>%
  group_by(h_id) %>% group_by (p_ID) %>%
  mutate(newcol = cumsum(lead(t_pur, default = 0) == 11)) %>%
  ungroup()

Полученные результаты - почти то, на что я надеялся.Но не совсем.Все строки с 7 по 10 должны быть сгруппированы вместе с одним и тем же идентификатором в newcol, потому что нет промежуточной "11".Последовательность t_pur равна 1, 2, 2, 11. Но в приведенном ниже выводе значения newcol представлены как 0, 0, 1, 1.

По сути, я пытаюсь связать отдельные сегменты вобщие поездки, начинающиеся каждый раз, когда есть возвращение домой, обозначенное как t_pur == "11".Иногда это просто назад-назад (два сегмента).Но иногда, как в строках 7-10, это 4 сегмента.

Фактический вывод:

      h_id     p_ID t_pur t_distance     X Conc       t_mode1 has_work newcol
     <int>    <int> <int>      <dbl> <int> <chr>        <int>    <dbl>  <int>
 1 1000002 10000022     6       753.     1 10000022-1       1        0      1
 2 1000002 10000022    11       753.     1 10000022-1       1        0      1
 3 1000002 10000022     7      4682.     2 10000022-2       1        0      2
 4 1000002 10000022    11      4682.     2 10000022-2       1        0      2
 5 1000013 10000131     5       616.     1 10000131-1       1        0      1
 6 1000013 10000131    11       616.     1 10000131-1       1        0      1
 7 1000013 10000132     1      9627.     1 10000132-1       1        1      0
 8 1000013 10000132     2       641.     1 10000132-1       1        0      0
 9 1000013 10000132     2     15077.     1 10000132-1       1        0      1
10 1000013 10000132    11     21408.     1 10000132-1       1        0      1
11 1000013 10000132     6     24273.     2 10000132-2       1        0      2
12 1000013 10000132    11     24273.     2 10000132-2       1        0      2

Надеялись на вывод:

      h_id     p_ID t_pur t_distance     X Conc       t_mode1 has_work newcol
     <int>    <int> <int>      <dbl> <int> <chr>        <int>    <dbl>  <int>
 1 1000002 10000022     6       753.     1 10000022-1       1        0      1
 2 1000002 10000022    11       753.     1 10000022-1       1        0      1
 3 1000002 10000022     7      4682.     2 10000022-2       1        0      2
 4 1000002 10000022    11      4682.     2 10000022-2       1        0      2
 5 1000013 10000131     5       616.     1 10000131-1       1        0      1
 6 1000013 10000131    11       616.     1 10000131-1       1        0      1
 7 1000013 10000132     1      9627.     1 10000132-1       1        1      1
 8 1000013 10000132     2       641.     1 10000132-1       1        0      1
 9 1000013 10000132     2     15077.     1 10000132-1       1        0      1
10 1000013 10000132    11     21408.     1 10000132-1       1        0      1
11 1000013 10000132     6     24273.     2 10000132-2       1        0      2
12 1000013 10000132    11     24273.     2 10000132-2       1        0      2

1 Ответ

0 голосов
/ 24 мая 2019

Я думаю, я понимаю, что вы хотите ... вот удар.

Пояснение:

  • когда вы говорите "сначала проверяет, ссылалась ли запись на того же человека" , это говорит мне, что вы должны группировать по этой переменной, т.е. dplyr::group_by, data.table by=, и база R's by()
  • используя простой lead или shift, мы включаем логику значения строки next , чтобы присвоить его this row; b / c обе эти функции не знают, что делать при просмотре последней строки для конкретного человека, мы должны предоставить значение по умолчанию с default=, fill= или вручную с c(...[-1], 0)

Отредактировано для обновленной логики и понимания.


dplyr

library(dplyr)
x %>%
  group_by(p_ID) %>%
  mutate(newcol = cumsum(lag(t_pur == 11, default = TRUE))) %>%
  ungroup()
# # A tibble: 12 x 9
#       h_id     p_ID t_pur t_distance     X Conc       t_mode1 has_work newcol
#      <int>    <int> <int>      <dbl> <int> <chr>        <int>    <dbl>  <int>
#  1 1000002 10000022     6       753.     1 10000022-1       1        0      1
#  2 1000002 10000022    11       753.     1 10000022-1       1        0      1
#  3 1000002 10000022     7      4682.     2 10000022-2       1        0      2
#  4 1000002 10000022    11      4682.     2 10000022-2       1        0      2
#  5 1000013 10000131     5       616.     1 10000131-1       1        0      1
#  6 1000013 10000131    11       616.     1 10000131-1       1        0      1
#  7 1000013 10000132     1      9627.     1 10000132-1       1        1      1
#  8 1000013 10000132     2       641.     1 10000132-1       1        0      1
#  9 1000013 10000132     2     15077.     1 10000132-1       1        0      1
# 10 1000013 10000132    11     21408.     1 10000132-1       1        0      1
# 11 1000013 10000132     6     24273.     2 10000132-2       1        0      2
# 12 1000013 10000132    11     24273.     2 10000132-2       1        0      2

data.table

library(data.table)
xDT <- x
xDT$newcol <- NULL
setDT(xDT)
xDT[, newcol := cumsum(shift(t_pur == 11, type = "lag", fill = TRUE)), by = "p_ID"]

База R

do.call(rbind.data.frame,
        c(by(x, x$p_ID, function(z)
          within(z, { newcol = cumsum(c(TRUE, head(z$t_pur, n=-1) == 11)) } )),
          stringsAsFactors = FALSE))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...