Быстрое извлечение строк data.frame с другим значением в предыдущей строке

Меня интересует извлечение информации из data.frame, в котором значение данной записи во фрейме данных отличается от предыдущего. Вот пример кадра:

df <- data.frame(name = rep("a",5), val1 = c(1,1,2,2,2), val2 = c(10,11,12,13,14))

> df
  name val1 val2
1    a    1   10
2    a    1   11
3    a    2   12
4    a    2   13
5    a    2   14

Меня интересует извлечение информации в строках 2 и 3, где val1 изменяется с 1 на 2. Я могу сделать это, повторяя каждую запись во фрейме данных, отслеживая предыдущий val1, чтобы при его изменении я мог вытащить две соответствующие строки, но мне интересно, есть ли более быстрый или эффективный способ сделать это, когда фрейм данных очень большой. R обычно имеет более быстрые способы извлечения информации, чем итерация, но я не уверен, что здесь использовать.


person oregano    schedule 07.02.2017    source источник


Ответы (4)


Один из способов — использовать diff и логическое подмножество:

keepers <- diff(df$val1)
df[as.logical(c(0, keepers) + c(keepers, 0)),]
  name val1 val2
2    a    1   11
3    a    2   12

diff фиксирует изменение значения. Нули добавляются и добавляются для захвата первой строки после изменения и последней строки перед изменением, и эти векторы складываются вместе, в результате чего получается вектор из 1 и 0. as.logical превращает это в логический вектор, который используется для подмножества строк.

person lmo    schedule 07.02.2017

С diff вы можете воспользоваться тем фактом, что diff дает вам на одно значение меньше, чем количество строк. С:

which(diff(df$val1)==1) + 0:1
# instead of '==1' you can also use '>0' or '!=0': which(diff(df$val1)!=0) + 0:1

вы получаете индекс строки до и строки после изменения:

> which(diff(df$val1)==1) + 0:1
[1] 2 3

Следовательно:

df[which(diff(df$val1)==1) + 0:1, ]

приводит к желаемому результату:

  name val1 val2
2    a    1   11
3    a    2   12

Еще один хороший R-трик сравнивает результат head и tail:

which(head(df$val1,-1) != tail(df$val1,-1)) + 0:1

это приводит к тому же индексу (конечно!).

person h3rm4n    schedule 07.02.2017
comment
Будет ли diff быстрее, потому что это одна операция? - person oregano; 08.02.2017
comment
@орегано не уверен, тебе придется это проверить - person h3rm4n; 08.02.2017

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

library(data.table)
zz=data.table(df)
yy=zz[, .SD[c(1,.N)], by=val1]
yy[2:(nrow(yy)-1)]
person Chirayu Chamoli    schedule 07.02.2017

Мы можем использовать lag из dplyr, чтобы получить индекс строк, которые не похожи на предыдущий, а затем также выбрать одну строку выше, вычитая 1 из этого индекса.

library(dplyr)
i1 <- which(df$val1 != lag(df$val1))
df[sort(c(i1, i1-1)),]

#name val1 val2
#2    a    1   11
#3    a    2   12

Другой пример,

df <- data.frame(name=rep("a", 7),val1=c(1,1,2,2,2,3, 3), val2=c(10,11,12,13,14, 15, 16))
df
df
#  name val1 val2
#1    a    1   10
#2    a    1   11
#3    a    2   12
#4    a    2   13
#5    a    2   14
#6    a    3   15
#7    a    3   16

i1 <- which(df$val1 != lag(df$val1))
df[sort(c(i1, i1-1)),]

#  name val1 val2
#2    a    1   11
#3    a    2   12
#5    a    2   14
#6    a    3   15
person Ronak Shah    schedule 07.02.2017