У меня есть набор данных с идентификатором и скоростью.

id <- c(1,1,1,1,2,2,2,2,3,3,3)

speed <- c(40,30,50,40,45,50,30,55,50,50,60)

i <- cbind(id, speed)

limit <- 35

Скажем, если «скорость» пересекает «предел», он будет считаться как 1. И вы будете считать снова, только если скорость снизится и пересекает «предел».

Я хочу, чтобы данные были похожи.

 id | Speed Viol.
 ----------
 1  |   2
 ---------
 2  |   2
 ---------
 3  |   1
 ---------

Здесь id (count).

 id1 (1) 40 (2) 50,40

 id2 (1) 45,50 (2) 55

 id3 (1) 50,50,60

Как это сделать, не используя if().

1
Chaitu 1 Сен 2017 в 14:41

4 ответа

Лучший ответ

Вот метод tapply, предложенный в комментариях и исходных векторах.

tapply(speed, id, FUN=function(x) sum(c(x[1] > limit, diff(x > limit)) > 0))
1 2 3 
2 2 1 

tapply применяет функцию к каждой группе, здесь, по идентификатору. Функция проверяет, является ли первый элемент идентификатора старше 35, и затем объединяет это с выходными данными diff, аргумент которого проверяет, являются ли последующие наблюдения больше 35. Таким образом, diff проверяет, является ли идентификатор возвращается к отметке выше 35 после падения ниже этого уровня. Отрицательные значения в результирующем векторе преобразуются в FALSE (0) с > 0, и эти результаты суммируются.

tapply возвращает именованный вектор, с которым довольно приятно работать. Однако, если вам нужен data.frame, вы можете использовать aggregate вместо этого, как предлагает d.b:

aggregate(speed, list(id=id), FUN=function(x) sum(c(x[1] > limit, diff(x > limit)) > 0))
  id x
1  1 2
2  2 2
3  3 1
5
d.b 1 Сен 2017 в 14:56

Вот dplyr решение. Я группирую по id, а затем проверяю, превышает ли скорость скорость в каждой строке, но не в предыдущей записи. (Я получаю предыдущую строку, используя lag). Если это так, то выдает TRUE. Или, если это первая строка для id (т. Е. row_number()==1) и она превышает лимит, это также дает TRUE. Затем я суммирую все TRUE значения для каждого id, используя summarise.

id <- c(1,1,1,1,2,2,2,2,3,3,3)   
speed <- c(40,30,50,40,45,50,30,55,50,50,60)
i <- data.frame(id, speed)
limit <- 35

library(dplyr)

i %>% 
  group_by(id) %>% 
  mutate(viol=(speed>limit&lag(speed)<limit)|(row_number()==1&speed>limit)) %>% 
  summarise(sum(viol))

# A tibble: 3 x 2
     id `sum(viol)`
  <dbl>       <int>
1     1           2
2     2           2
3     3           1
2
Lyngbakr 1 Сен 2017 в 12:10

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

library(data.table)

setDT(i)[, id1 := rleid(speed > limit), by = id][
          speed > limit, .(violations = uniqueN(id1)), by = id][]

Который дает,

   id violations
1:  1          2
2:  2          2
3:  3          1
1
Sotos 1 Сен 2017 в 12:00
aggregate(speed~id, data.frame(i), function(x) sum(rle(x>limit)$values))
#  id speed
#1  1     2
#2  2     2
#3  3     1

Основная идея заключается в том, что x > limit будет проверять случаи, когда ограничение скорости нарушается, и rle(x) группирует эти случаи по последовательным нарушениям или последовательным нарушениям. Тогда все, что вам нужно сделать, это подсчитать группы последовательных нарушений (когда rle(x>limit)$values равен TRUE).

0
d.b 1 Сен 2017 в 15:13