Определите, когда столбцы значения data.frame меняют значение и возвращают индексы изменения

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

x<-data.frame(cnt=1:10, code=rep('ELEMENT 1',10), val0=rep(5,10), val1=rep(6,10),val2=rep(3,10))
x[4,]$val0=6
  • Столбец cnt - это уникальный идентификатор (может быть дата или столбец времени, для простоты здесь int)
  • Кодовый столбец похож на код для набора строк (представьте несколько таких групп, но с разными кодами). Код и cnt являются ключами в моей таблице данных.
  • Столбцы val0, val1, val2 являются чем-то вроде результатов.

Приведенный выше формат данных должен быть прочитан как: Оценки для "ELEMENT 1" начались как 5,6,3, оставались как есть до 4-й итерации, когда они изменились на 6,6,3, а затем изменились на 5,6,3.

Мой вопрос: есть ли способ получить строку 1-го, 4-го и 5-го данных .frame? Есть ли способ обнаружить, когда столбцы меняются? (Есть 12 столбцов кстати)

Я попытался использовать дублированный data.table(который отлично работал в большинстве случаев), но в этом случае он удалит все дубликаты и оставит только строки 1 и 4 (удаление 5-го).

Есть ли у вас предложения? Я бы предпочел не использовать цикл for, поскольку прибл. 2M.

Ответ 1

В data.table версии 1.8.10 (стабильная версия в CRAN) существует (n) (неэкспортированная) функция, называемая duplist, которая выполняет именно это. И он также написан на C и поэтому ужасно быстр.

require(data.table) # 1.8.10
data.table:::duplist(x[, 3:5]) 
# [1] 1 4 5

Если вы используете версию разработки data.table (1.8.11), тогда существует более эффективная версия (с точки зрения памяти), переименованная в uniqlist, которая выполняет точно такую ​​же работу. Вероятно, это должно быть экспортировано для следующего выпуска. Кажется, что он поднялся на SO более одного раза. Посмотрим.

require(data.table) # 1.8.11
data.table:::uniqlist(x[, 3:5])
# [1] 1 4 5

Ответ 2

Полностью нечитаемый, но:

c(1,which(rowSums(sapply(x[,grep('val',names(x))],diff))!=0)+1)
# [1] 1 4 5

В принципе, запустите diff в каждой строке, чтобы найти все изменения. Если изменение происходит в любом столбце, то в строке произошла смена.

Кроме того, без sapply:

c(1,which(rowSums(diff(as.matrix(x[,grep('val',names(x))])))!=0)+1)