Из какой строки переменная data.frame имеет постоянное значение

#r #time-series #dplyr

#r #временные ряды #dplyr

Вопрос:

Я хотел бы вычислить среднее значение переменной в data.frame в R из строки, другая переменная которой начинает иметь постоянное значение. Обычно я использую dplyr для задач такого типа базы данных, но я не понимаю, как это сделать, вот пример:

 s<-"no Spc PSize
2                0           6493
2                0           9281
2               12          26183
2               12          36180
2               12          37806
2               12          37765
3               12          36015
3               12          26661
3                0          14031
3                0           5564
3                1          17701
3                1          20808
3                1          31511
3                1          44746
3                1          50534
3                1          54858
3                1          58160
3                1          60326"

d<-read.delim(textConnection(s),sep="",header=T)

mean(d[1:10,3])
sd(d[1:10,3])
  

Из строки 11 переменная spc имеет постоянное значение, так что это то место, где я хочу разделить data.frame

 mean(d[11:18,3])
sd(d[11:18,3])
  

Я могу вычислить это вручную, но идея не в этом…

Ответ №1:

Вариант 1: Использование rleid из data.table пакета:

 d %>% 
  group_by(rlid = rleid(Spc)) %>% 
  summarise(mean_size = mean(PSize), sd_size = sd(PSize)) %>% 
  slice(n())
  

дает:

 # A tibble: 1 × 3
   rlid mean_size  sd_size
  <int>     <dbl>    <dbl>
1     4   42330.5 16866.59
  

Вариант 2: Использование rle :

 startrow <- sum(head(rle(d$Spc)$lengths, -1))   1
d %>%
  slice(startrow:n()) %>% 
  summarise(mean_size = mean(PSize), sd_size = sd(PSize))
  

дает:

   mean_size  sd_size
1   42330.5 16866.59
  

Вариант 3: Если вы хотите вычислить для двух групп (последней и других), вам следует использовать group_by вместо filter и создать новый вектор группировки ( rep_vec ) с rle :

 rep_vec <- c(sum(head(rle(d$Spc)$lengths, -1)), tail(rle(d$Spc)$lengths, 1))

d %>%
  group_by(grp = rep(c('others','last_group'), rep_vec)) %>% 
  summarise(mean_size = mean(PSize), sd_size = sd(PSize))
  

что дает:

          grp mean_size  sd_size
       (chr)     (dbl)    (dbl)
1 last_group   42330.5 16866.59
2     others   23597.9 13521.32
  

Если вы хотите включить строки, вы можете изменить код на:

 d %>%
  mutate(rn = row_number()) %>% 
  group_by(grp = rep(c('others','last_group'), rep_vec)) %>% 
  summarise(mean_size = mean(PSize), sd_size = sd(PSize), rows = paste0(range(rn), collapse=':'))
  

что дает:

          grp mean_size  sd_size  rows
       <chr>     <dbl>    <dbl> <chr>
1 last_group   42330.5 16866.59 11:18
2     others   23597.9 13521.32  1:10
  

Ответ №2:

Вы можете сделать это, добавив столбец, который проверяет, соответствует ли запись указанному выше значению, затем используйте cumsum , чтобы найти места, где изменяется количество. Я group_by сделал это и вычислил нужные вам сводки — я также добавил вывод, какие строки были включены, чтобы продемонстрировать, откуда он был взят.

 d %>%
  mutate(
    row = 1:n()
    , isDiff = Spc != lag(Spc, default = Spc[1])
    , whichGroup = cumsum(isDiff)) %>%
  group_by(whichGroup, Spc) %>%
  summarise(mean = mean(PSize)
            , sd = sd(PSize)
            , whichRows = paste(range(row), collapse = ":"))
  

Дает:

   whichGroup   Spc    mean        sd whichRows
       <int> <int>   <dbl>     <dbl>     <chr>
1          0     0  7887.0  1971.414       1:2
2          1    12 33435.0  5486.794       3:8
3          2     0  9797.5  5987.073      9:10
4          3     1 42330.5 16866.591     11:18
  

Если вам нужна только последняя группа, о которой я не могу сказать из вашего сообщения, делаете вы это или нет, вы могли бы вместо этого использовать filter , вот так:

 d %>%
  mutate(
    row = 1:n()
    , isDiff = Spc != lag(Spc, default = Spc[1])
    , whichGroup = cumsum(isDiff)) %>%
  filter(whichGroup == max(whichGroup)) %>%
  summarise(Spc = Spc[1]
            , mean = mean(PSize)
            , sd = sd(PSize)
            , whichRows = paste(range(row), collapse = ":"))
  

Что дает:

   Spc    mean       sd whichRows
1   1 42330.5 16866.59     11:18
  

Судя по комментарию, вам, похоже, нужна последняя группа против в остальном вы можете получить это с помощью такого подхода:

 d %>%
  mutate(
    row = 1:n()
    , isDiff = Spc != lag(Spc, default = Spc[1])
    , whichGroup = cumsum(isDiff)) %>%
  group_by(isLast = whichGroup == max(whichGroup)) %>%
  summarise(mean = mean(PSize)
            , sd = sd(PSize)
            , whichRows = paste(range(row), collapse = ":"))
  

что дает:

   isLast    mean       sd whichRows
   <lgl>   <dbl>    <dbl>     <chr>
1  FALSE 23597.9 13521.32      1:10
2   TRUE 42330.5 16866.59     11:18
  

Комментарии:

1. Я хотел бы рассчитать две группы, последнюю и остальные.

2. @Leosar — смотрите последнюю правку для версии, которая сравнивает последнюю группу со всеми остальными

3. Мне кажется, что этот подход более гибкий, чем @Procrastinatus-максимальный

4. @Leosar Я думаю, что подход Марка хороший, но он не более гибкий, imo (также не менее гибкий).

Ответ №3:

Итак, вы хотите найти индекс, в котором средний вектор начинает быть постоянным? Вы можете взять diff() вашего вектора и посмотреть, в первый раз это отличается от нуля. Например,

 vec <- c(1,2,3,4,5,5,5,6,6,6)
diff(vec)
differences <- rev(diff(vec))

# distance from the end of first non-zero
min.dist <- min(which(differences != 0))

# take difference
length(vec) - min.dist   1
  

Это последнее значение должно дать вам индекс, с которого вектор начинает быть постоянным.