Переименовать последовательность элементов, если она встречается более одного раза (R, dplyr) - PullRequest
3 голосов
/ 31 января 2020

У меня есть набор данных, df

 ID         Date
 A          9/9/2019 5:00:01
 A          9/9/2019 5:00:02
 A          9/9/2019 5:00:03
 B          9/9/2019 6:00:01
 B          9/9/2019 6:00:03
 B          9/9/2019 6:00:04
 A          9/9/2019 6:00:05
 A          9/9/2019 6:00:06
 A          9/9/2019 6:00:07
 c          9/9/2019 6:00:08
 c          9/9/2019 6:00:09
 A          9/9/2019 6:00:10
 A          9/9/2019 6:00:11

Я хотел бы

 ID         Date
 A          9/9/2019 5:00:01
 A          9/9/2019 5:00:02
 A          9/9/2019 5:00:03
 B          9/9/2019 6:00:01
 B          9/9/2019 6:00:03
 B          9/9/2019 6:00:04
 list1      9/9/2019 6:00:05
 list1      9/9/2019 6:00:06
 list1      9/9/2019 6:00:07
 c          9/9/2019 6:00:08
 c          9/9/2019 6:00:09
 list2      9/9/2019 6:00:10
 list2      9/9/2019 6:00:11

Я хотел бы переименовать последовательность элементов, если она встречается в моем наборе данных более одного раза

dput:

structure(list(ID = structure(c(1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 
1L, 3L, 3L, 1L, 1L), .Label = c("A", "B", "c"), class =   "factor"), 
Date = structure(1:13, .Label = c("9/9/2019 12:00:00 AM", 
"9/9/2019 12:00:01 AM", "9/9/2019 12:00:02 AM", "9/9/2019    12:00:03 AM", 
"9/9/2019 12:00:04 AM", "9/9/2019 12:00:05 AM", "9/9/2019   12:00:06 AM", 
"9/9/2019 12:00:07 AM", "9/9/2019 12:00:08 AM", "9/9/2019 12:00:09 AM", 
"9/9/2019 12:00:11 AM", "9/9/2019 12:00:12 AM", "9/9/2019 12:00:13 AM"
), class = "factor")), class = "data.frame", row.names = c(NA, 
-13L))

Это то, что я пробовал:

c(letters, do.call(paste0, expand.grid(letters, 1:1000)))
setDT(df[, grp := rleid(ID)][ItemSubject == "", 
ItemSubject := nm1[.GRP], grp][, grp := NULL][]

Я использовал эту команду ранее, но не уверен, что интегрировать это конкретная команда в коде.

Ответы [ 3 ]

3 голосов
/ 31 января 2020

Я думаю, что следующий подход является относительно чистым:

library(data.table)
setDT(DT)

DT[ , run_id := rleid(ID)]
DT[DT[ , .SD[1L], by = run_id][duplicated(ID), ID := paste0('list', .I)],
   on = 'run_id', ID := i.ID][]
#         ID                    Date run_id
#     <fctr>                  <fctr>  <int>
#  1:      A    9/9/2019 12:00:00 AM      1
#  2:      A    9/9/2019 12:00:01 AM      1
#  3:      A    9/9/2019 12:00:02 AM      1
#  4:      B 9/9/2019    12:00:03 AM      2
#  5:      B    9/9/2019 12:00:04 AM      2
#  6:      B    9/9/2019 12:00:05 AM      2
#  7:  list1  9/9/2019   12:00:06 AM      3
#  8:  list1    9/9/2019 12:00:07 AM      3
#  9:  list1    9/9/2019 12:00:08 AM      3
# 10:      c    9/9/2019 12:00:09 AM      4
# 11:      c    9/9/2019 12:00:11 AM      4
# 12:  list2    9/9/2019 12:00:12 AM      5
# 13:  list2    9/9/2019 12:00:13 AM      5

Данные в основном упорядочены по строкам - ряд A строк 1-3 отличается от серия A строк 7-9.

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

Теперь мы можем подойти к вашей проблеме. Как вы описываете, мы хотим заменять столбец ID всякий раз, когда run_id увеличивается, но ID повторяется.

Я выбрал подход и попытался использовать duplicated для обнаружения повторов. Проблема в том, что в исходной таблице есть повторы A (например, строки 2 и 3), которые не являются повторениями в том смысле, который мы имеем в виду. Мы хотим сначала сократить таблицу до одной строки за run_id, чтобы дубликаты означали дубликаты по run_id s . Это то, что делает часть DT[ , .SD[1L], by = run_id]. Обратите внимание, что, строго говоря, мы могли бы сделать DT[ , .(ID = ID[1L]), by = run_id], поскольку нам не нужен столбец Date.

Теперь мы можем использовать duplicated(ID), чтобы идентифицировать строки, которые повторяются; так как второй аргумент (обычно называемый j) оценивается после фильтра duplicated(ID), остается только две строки, поэтому .I идеально совпадает со счетчиком, который вы хотели в замена.

Как только это будет сделано, мы можем заменить целевые значения ID в исходной таблице на , объединяя и перезаписывая (иначе update-on-join ).

2 голосов
/ 03 февраля 2020

Вот еще один вариант data.table:

DT[, nid := rleid(ID)][, 
    ri := rleid(nid), ID][
        ri > 1L, ID := paste0("list", ri - 1L), ID]

вывод:

       ID                    Date nid ri
 1:     A    9/9/2019 12:00:00 AM   0  1
 2:     A    9/9/2019 12:00:01 AM   0  1
 3:     A    9/9/2019 12:00:02 AM   0  1
 4:     B 9/9/2019    12:00:03 AM   1  1
 5:     B    9/9/2019 12:00:04 AM   1  1
 6:     B    9/9/2019 12:00:05 AM   1  1
 7: list1  9/9/2019   12:00:06 AM   2  2
 8: list1    9/9/2019 12:00:07 AM   2  2
 9: list1    9/9/2019 12:00:08 AM   2  2
10:     c    9/9/2019 12:00:09 AM   3  1
11:     c    9/9/2019 12:00:11 AM   3  1
12: list2    9/9/2019 12:00:12 AM   4  3
13: list2    9/9/2019 12:00:13 AM   4  3
2 голосов
/ 31 января 2020

В base R мы можем сделать это с rle

df1$ID <- inverse.rle(within.list(rle(as.character(df1$ID)),  {
           i1 <- duplicated(values)
           values[i1] <- paste0("list", seq_len(sum(i1)))
            }))
df1
#      ID                    Date
#1      A    9/9/2019 12:00:00 AM
#2      A    9/9/2019 12:00:01 AM
#3      A    9/9/2019 12:00:02 AM
#4      B 9/9/2019    12:00:03 AM
#5      B    9/9/2019 12:00:04 AM
#6      B    9/9/2019 12:00:05 AM
#7  list1  9/9/2019   12:00:06 AM
#8  list1    9/9/2019 12:00:07 AM
#9  list1    9/9/2019 12:00:08 AM
#10     c    9/9/2019 12:00:09 AM
#11     c    9/9/2019 12:00:11 AM
#12 list2    9/9/2019 12:00:12 AM
#13 list2    9/9/2019 12:00:13 AM
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...