R как отфильтровать временной ряд измерений на основе предыдущих значений

Я пытаюсь отфильтровать демографические данные кораллов во временном ряду. У меня есть набор кораллов, которые измеряют каждые 3 месяца. Что я хочу сделать, так это а.) Фильтровать все кораллы, которые в какой-то момент имели максимальный диаметр в пределах указанного диапазона размеров (диаметр 8-12 мм), б.) Удалить кораллы, которые ранее были больше, чем диапазон размеров, и в. .) удалите измерения кораллов, которые попали в размерный диапазон ПОСЛЕ того, как он вырос за пределы или за пределы диапазона размеров, включив для каждого коралла только ПЕРВОЕ измерение, в котором он вырос в размерный диапазон (8-12 мм), и последующее измерение в следующем. Шаг времени.

Я создал образец базы данных и желаемую базу данных, чтобы конкретно проиллюстрировать то, что я ищу. В образец базы данных я также включил все критерии, перечисленные для каждого коралла ниже, в разделе примечаний рядом с первой записью для каждого коралла для вашей справки. Вот 8 кораллов, которые я включил в базу данных, и словами, что я хочу с ними сделать:

Коралл №1 следует полностью удалить из базы данных, так как он не соответствует желаемому диапазону размеров 8–12 мм.

Коралл № 2 должен быть удален из базы данных, потому что он начинался выше желаемого диапазона размеров, затем уменьшался ниже него, а затем превратился в него. Мне нужны только кораллы, которые выросли до нужного размера, но не уменьшились в размерах заранее.

Коралл № 3 - это пример коралла, который вырос до размера (8–12 мм) и выше без усадки, и это коралл, который я хочу оставить, потому что он вырос до этого размера. Однако я хочу включить только ПЕРВУЮ меру внутри диапазона размеров (9 мм в данном случае в TimeStep 3) и текущее измерение (12 мм в данном случае для TimeStep 4).

Коралл № 4 - это пример коралла, размер которого вырос и остался выше допустимого диапазона, поэтому его необходимо удалить.

Коралл № 5 - это пример коралла, который начинался ниже диапазона, вырос в него, а затем снова сжался до этого диапазона (TimeStep 4). Для этого сценария я хочу включить только первый раз, когда диаметр попал в диапазон (TimeStep 2) и текущее измерение (TimeStep 3), а не второй раз, когда он попал в диапазон. Это потому, что первый раз - естественный рост, тогда как второй раз - усадка и ее результирующее восстановление (которое я хочу исключить или отфильтровать).

Коралл № 6 - это пример коралла, который начинался в диапазоне размеров для TimeStep 1, а затем вырастал из него на следующем TimeStep и продолжал расти после. Я хочу сохранить только измерения в TimeStep 1 и 2 (первое измерение внутри диапазона и продолжающееся измерение)

Коралл № 7 является примером коралла, который начинался в диапазоне размеров в TimeStep 1, а затем оставался в диапазоне для TimeStep 2. В этом случае мне нужно только первое измерение в диапазоне размеров (TimeStep 1) и последующее измерение ( TimeStep 2)

Коралл № 8 является примером коралла, который вырос до диапазона размеров в TimeStep 3, оставался в диапазоне (10 => 9) в TimeStep 4, затем сжался ниже желаемого диапазона, а затем для TimeStep 6 снова вырос до диапазона. Для этой колонии я снова хочу, чтобы ПЕРВОЕ измерение внутри диапазона (10 мм на TimeStep 3) и продолжающееся измерение в TimeStep 4 было включено для этого коралла.

Коралл № 9 - это пример коралла, который вырос до размера, указанного на временном шаге 3 (9 мм), но не был обнаружен на следующем временном шаге (NF для столбца кода состояния с измерением как NA). Я хочу сохранить такие кораллы в наборе данных, чтобы рассчитать выживаемость.

В общем, мне нужен код, который фильтрует эту базу данных таким образом, чтобы если коралл в какой-то момент имел диаметр в диапазоне размеров 8-12 см, но ранее был больше этого диапазона, никогда не был в диапазоне или ниже диапазона или начинался ниже диапазона и никогда не попадали в нее, они полностью удаляются из базы данных. Кроме того, я хочу сохранить все кораллы, которые выросли до диапазона, а затем сжались до него, в базе данных, удаляя их во второй раз, когда они попадали в диапазон. Это можно сделать, удалив все измерения, ЗА ИСКЛЮЧЕНИЕМ первого временного шага, в котором коралл вырос до определенного диапазона размеров, и следующего измерения временного шага.

ПРИМЕР БАЗЫ ДАННЫХ

data <- structure(list(Site = c("WAI", "WAI", "WAI", "WAI", "WAI", "WAI", 
"WAI", "WAI", "WAI", "WAI", "WAI", "WAI", "WAI", "WAI", "WAI", 
"WAI", "WAI", "WAI", "WAI", "WAI", "WAI", "WAI", "WAI", "WAI", 
"WAI", "WAI", "WAI", "WAI", "WAI", "WAI", "WAI", "WAI", "WAI", 
"WAI", "WAI", "WAI", "WAI", "WAI", "WAI", "WAI", "WAI", "WAI", 
"WAI", "WAI", "WAI", "WAI", "WAI", "WAI", "WAI", "WAI", "WAI", 
"WAI"), `Module #` = c(116, 116, 116, 116, 116, 116, 116, 115, 
115, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 
116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 
116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 
116, 116, 116, 116, 116), Side = c("N", "N", "N", "N", "N", "N", 
"N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", 
"N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", 
"N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", 
"N", "N", "N", "N", "N", "N", "N"), TimeStep = c(1, 2, 3, 4, 
5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 
2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 
5, 6, 1, 2, 3, 4), Settlement_Area = c(0.75902336, 0.75902336, 
0.75902336, 0.75902336, 0.75902336, 0.75902336, 0.75902336, 0.75902336, 
0.75902336, 0.75902336, 0.75902336, 0.75902336, 0.75902336, 0.75902336, 
0.75902336, 0.75902336, 0.75902336, 0.75902336, 0.75902336, 0.75902336, 
0.75902336, 0.75902336, 0.75902336, 0.75902336, 0.75902336, 0.75902336, 
0.75902336, 0.75902336, 0.75902336, 0.75902336, 0.75902336, 0.75902336, 
0.75902336, 0.75902336, 0.75902336, 0.75902336, 0.75902336, 0.75902336, 
0.75902336, 0.75902336, 0.75902336, 0.75902336, 0.75902336, 0.75902336, 
0.75902336, 0.75902336, 0.75902336, 0.75902336, 0.75902336, 0.75902336, 
0.75902336, 0.75902336), `Colony #` = c(1, 1, 1, 1, 1, 1, 2, 
2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 
5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 
9, 9, 9), Location = c("C1", "C1", "C1", "C1", "C1", "C1", "B4", 
"B4", "B4", "B4", "B4", "B4", "A1", "A1", "A1", "A1", "A1", "A1", 
"B3", "B3", "B3", "B3", "B3", "B3", "D1", "D1", "D1", "D1", "D1", 
"D1", "A2", "A2", "A2", "A2", "A2", "A2", "A4", "A4", "A4", "A4", 
"A4", "A4", "B3", "B3", "B3", "B3", "B3", "B3", "A3", "A3", "A3", 
"A3"), `Taxonomic Code` = c("PC", "PC", "PC", "PC", "PC", "PC", 
"PC", "PC", "PC", "PC", "PC", "PC", "PC", "PC", "PC", "PC", "PC", 
"PC", "PC", "PC", "PC", "PC", "PC", "PC", "PC", "PC", "PC", "PC", 
"PC", "PC", "PC", "PC", "PC", "PC", "PC", "PC", "PC", "PC", "PC", 
"PC", "PC", "PC", "PC", "PC", "PC", "PC", "PC", "PC", "PC", "PC", 
"PC", "PC"), `Cover Code` = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, NA), 
    `Max Diameter (cm)` = c(5, 7, 13, 15, 16, 19, 15, 7, 9, 11, 
    14, 18, 3, 6, 9, 12, 15, 20, 13, 16, 18, 21, 23, 26, 6, 9, 
    14, 12, 15, 18, 11, 14, 17, 17, 21, 24, 9, 11, 14, 16, 20, 
    22, 3, 6, 10, 9, 7, 10, 4, 6, 9, NA), `Status Code` = c(NA, 
    NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 
    NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 
    NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 
    NA, NA, NA, NA, NA, "NF"), Notes = c("coral # 1 should be deleted from the database because it skipped over the desired size range of 8-12 mm", 
    NA, NA, NA, NA, NA, "coral # 2 should be deleted from the database because it started above the desired size range then shrank back into it.  I only want corals that have grown into the size range", 
    NA, NA, NA, NA, NA, "Colony # 3 is an example of a coral that grew to the size range (8-12 mm) and beyond without shrinking and this is a coral that I want to keep because it grew to the size range.  However, I want to only include the FIRST measure inside the size range (9 mm in this case) and the proceeding measurement (12 mm)", 
    NA, NA, NA, NA, NA, "Colony # 4 is an example of a coral that started off above the size range and therefore needs to be removed.", 
    NA, NA, NA, NA, NA, "Colony # 5 is an example of a coral that started below the range, grew into it, then later shrank back into the range (TimeStep 4). For this scenario, I want to only include the first time the diameter fell into the range (TimeStep 2) and the proceeding measurement, not the second time it fell into the range. This is because the first time is natural growth whereas the second time is shrinkage and its resulting recovery (which I want to exclude or filter out).", 
    NA, NA, NA, NA, NA, "Colony # 6 is an example of a coral that started in the size range for TimeStep 1 and then grew out of it in the next TimeStep and continued to grow after. I want to maintain only the measurements in TimeStep 1 and 2 (the first measure inside the range and the proceeding measurement)", 
    NA, NA, NA, NA, NA, "Colony # 7 is an example of a coral that started in the size range in TimeStep 1 and then remained in the range for TimeStep 2. In this case I only want the first measurement in the size range (TimeStep 1) and the subsequent measurement (TimeStep 2)", 
    NA, NA, NA, NA, NA, "Colony # 8 is an example of a coral that grew to the size range in TimeStep 3, stayed in the range (10 => 9) in TimeStep 4, then shrank below the desired range then for TimeStep 6 grew back to the range. For this colony, again I want the FIRST measurement inside the range (10 mm at TimeStep 3) and the proceeding measurement in TimeStep 4 included for this coral", 
    NA, NA, NA, NA, NA, NA, NA, NA, NA)), class = c("spec_tbl_df", 
"tbl_df", "tbl", "data.frame"), row.names = c(NA, -52L), spec = structure(list(
    cols = list(Site = structure(list(), class = c("collector_character", 
    "collector")), `Module #` = structure(list(), class = c("collector_double", 
    "collector")), Side = structure(list(), class = c("collector_character", 
    "collector")), TimeStep = structure(list(), class = c("collector_double", 
    "collector")), Settlement_Area = structure(list(), class = c("collector_double", 
    "collector")), `Colony #` = structure(list(), class = c("collector_double", 
    "collector")), Location = structure(list(), class = c("collector_character", 
    "collector")), `Taxonomic Code` = structure(list(), class = c("collector_character", 
    "collector")), `Cover Code` = structure(list(), class = c("collector_double", 
    "collector")), `Max Diameter (cm)` = structure(list(), class = c("collector_double", 
    "collector")), `Status Code` = structure(list(), class = c("collector_character", 
    "collector")), Notes = structure(list(), class = c("collector_character", 
    "collector"))), default = structure(list(), class = c("collector_guess", 
    "collector")), skip = 1), class = "col_spec"))

ЖЕЛАТЕЛЬНАЯ БАЗА ДАННЫХ

data_final <- structure(list(Site = c("WAI", "WAI", "WAI", "WAI", "WAI", "WAI", 
"WAI", "WAI", "WAI", "WAI", "WAI", "WAI"), `Module #` = c(116, 
116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116), Side = c("N", 
"N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N"), TimeStep = c(3, 
4, 2, 3, 1, 2, 1, 2, 3, 4, 3, 4), Settlement_Area = c(0.75902336, 
0.75902336, 0.75902336, 0.75902336, 0.75902336, 0.75902336, 0.75902336, 
0.75902336, 0.75902336, 0.75902336, 0.75902336, 0.75902336), 
    `Colony #` = c(3, 3, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9), Location = c("A1", 
    "A1", "D1", "D1", "A2", "A2", "A4", "A4", "B3", "B3", "B2", 
    "B2"), `Taxonomic Code` = c("PC", "PC", "PC", "PC", "PC", 
    "PC", "PC", "PC", "PC", "PC", "PC", "PC"), `Cover Code` = c(1, 
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, NA), `Max Diameter (cm)` = c(9, 
    12, 9, 14, 11, 14, 9, 11, 10, 9, 9, NA), `Status Code` = c(NA, 
    NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, "NF")), class = c("spec_tbl_df", 
"tbl_df", "tbl", "data.frame"), row.names = c(NA, -12L), spec = structure(list(
    cols = list(Site = structure(list(), class = c("collector_character", 
    "collector")), `Module #` = structure(list(), class = c("collector_double", 
    "collector")), Side = structure(list(), class = c("collector_character", 
    "collector")), TimeStep = structure(list(), class = c("collector_double", 
    "collector")), Settlement_Area = structure(list(), class = c("collector_double", 
    "collector")), `Colony #` = structure(list(), class = c("collector_double", 
    "collector")), Location = structure(list(), class = c("collector_character", 
    "collector")), `Taxonomic Code` = structure(list(), class = c("collector_character", 
    "collector")), `Cover Code` = structure(list(), class = c("collector_double", 
    "collector")), `Max Diameter (cm)` = structure(list(), class = c("collector_double", 
    "collector")), `Status Code` = structure(list(), class = c("collector_character", 
    "collector"))), default = structure(list(), class = c("collector_guess", 
    "collector")), skip = 1), class = "col_spec"))

До сих пор мне удавалось получать кораллы, которые никогда не были в диапазоне размеров, путем создания вектора уникальных номеров колоний, которые попадали в диапазон от 8 до 12 мм:

size_vect <- seq(from = 8, to = 12, by = 1)
# a vector containing the range of diameter measurements we want to filter for

ID_vect <- data %>% group_by(`Colony #`) %>% 
filter(`Max Diameter (cm)` > min(size_vect) & `Max Diameter (cm)` < max(size_vect)) %>% 
# select all measures where the coral fell within the size range
distinct(`Colony #`) %>% 
# remove duplicate colony numbers
pull(`Colony #`)
# make the column `Colony #` in the dataframe ID_vect into a vector

Затем я отфильтровал всю базу данных образцов, чтобы включить только колонии кораллов из ID_vect:

data_new <- data %>% group_by(`Colony #`) %>%
filter(`Colony #` %in% ID_vect) 
# filter for all corals that contain the same colony number as those in the ID_vect

Я не знаю, как теперь фильтровать базу данных на основе следующего условия: если коралл попадал в диапазон размеров в какой-то момент, но предыдущее измерение было БОЛЬШЕ, чем максимальное значение желаемого диапазона размеров (12 мм), этот коралл должен удалить полностью. Например, Коралл № 2 должен быть удален, потому что до того, как значение попало в диапазон в TimeStep 3, оно составляло 15 мм в TimeStep 1, что превышает диапазон.

Кроме того, я не знаю, как учесть, не было ли измерения в следующем измерении TimeStep, например, с Coral # 9, где оно было измерено как 9 мм в TimeStep 3 и не было обнаружено (NF в коде состояния) в TimeStep 4. Мне нужно сохранить измерение TimeStep 4 для расчета выживаемости. Я не знаю, как закодировать этот условный фильтр, и здесь мне нужна помощь. Любой совет по коду приветствуется, спасибо!


person Eric Dilley    schedule 12.04.2020    source источник


Ответы (1)


Мы можем использовать кодирование длин серий, чтобы не отставать от переходов из диапазона в диапазон. С data.table::rleid это намного проще, поэтому я рекомендую это использовать.

Вот пример RLE в действии на коралле 8.

 `Colony #` `Max Diameter (cm)` InRange RLE
          8                   3   FALSE   1
          8                   6   FALSE   1
          8                  10    TRUE   2
          8                   9    TRUE   2
          8                   7   FALSE   3
          8                  10    TRUE   4

После кодирования RLE мы фильтруем строки с минимальным RLE в диапазоне, который ниже минимального RLE выше диапазона. Если такие строки существуют, мы ищем первую временную точку, которая находится в диапазоне, а также фильтруем следующую временную точку.

library(dplyr)
library(data.table)
data %>% 
  select(-Notes) %>%
  mutate(InRange = case_when(`Max Diameter (cm)` >= 8 & `Max Diameter (cm)` <= 12 ~ TRUE,
                             TRUE ~ FALSE)) %>% 
  mutate(AboveRange = case_when(`Max Diameter (cm)` > 12 ~ TRUE,
                                TRUE ~ FALSE)) %>% 
  group_by(`Colony #`) %>%
  mutate(RLE = data.table::rleid(InRange)) %>% 
  mutate(MinIn = min(RLE[InRange]), MinAbove = min(RLE[AboveRange]), MinInTime = min(TimeStep[InRange])) %>%
  filter(MinIn < MinAbove & (TimeStep == MinInTime | (TimeStep == MinInTime + 1))) %>% 
  select(-InRange,-AboveRange,-RLE,-MinIn,-MinAbove,-MinInTime)
## A tibble: 12 x 11
## Groups:   Colony # [6]
#   Site  `Module #` Side  TimeStep Settlement_Area `Colony #` Location `Taxonomic Code` `Cover Code` `Max Diameter (cm)` `Status Code`
#   <chr>      <dbl> <chr>    <dbl>           <dbl>      <dbl> <chr>    <chr>                   <dbl>               <dbl> <chr>        
# 1 WAI          116 N            3           0.759          3 A1       PC                          1                   9 NA           
# 2 WAI          116 N            4           0.759          3 A1       PC                          1                  12 NA           
# 3 WAI          116 N            2           0.759          5 D1       PC                          1                   9 NA           
# 4 WAI          116 N            3           0.759          5 D1       PC                          1                  14 NA           
# 5 WAI          116 N            1           0.759          6 A2       PC                          1                  11 NA           
# 6 WAI          116 N            2           0.759          6 A2       PC                          1                  14 NA           
# 7 WAI          116 N            1           0.759          7 A4       PC                          1                   9 NA           
# 8 WAI          116 N            2           0.759          7 A4       PC                          1                  11 NA           
# 9 WAI          116 N            3           0.759          8 B3       PC                          1                  10 NA           
#10 WAI          116 N            4           0.759          8 B3       PC                          1                   9 NA           
#11 WAI          116 N            3           0.759          9 A3       PC                          1                   9 NA           
#12 WAI          116 N            4           0.759          9 A3       PC                         NA                  NA NF  
person Ian Campbell    schedule 12.04.2020
comment
Ян Кэмпбелл: У меня к вам еще один вопрос. Я изменил свою базу данных образцов, чтобы включить коралл (Коралл № 9), который попал в диапазон размеров и затем не был обнаружен (NF в коде состояния) при следующем измерении TimeStep. Я хочу включить этот коралл в базу данных, чтобы определить, не дожил ли коралл до следующего временного шага. Не могли бы мне помочь, изменив предоставленный для этого код? Приносим извинения за дальнейшие действия, это очень сложный набор данных, с которым я имею дело. - person Eric Dilley; 12.04.2020
comment
Я добавил оператор is.na (MinIn), чтобы сохранить измерения от Coral # 9 с помощью этого кода: - person Eric Dilley; 12.04.2020
comment
Вы случайно не знаете, что мне нужно сделать в последнюю очередь, чтобы включить только первую меру, которая была в диапазоне (9 мм на TimeStep 3), и отсутствие меры в текущей записи (коралл не найден или код состояния = NF для TimeStep 4)? - person Eric Dilley; 12.04.2020
comment
Йен Кэмпбелл: у меня проблема в том, что с вашим кодом кораллы, которые не были обнаружены и не были измерены, исключаются из базы данных с помощью предоставленного кода. Чтобы учесть выживаемость кораллов, мне нужно иметь возможность условно вести ряды, которые имеют Status Code NF или D. Это потому, что иногда мне удавалось измерить мертвый коралл, а в других случаях я не мог его найти. Вот почему мне нужен код для работы а.) Кораллов, которые умерли и не были измерены, а б.) Кораллов, которые умерли, но были измерены, когда были найдены мертвыми. - person Eric Dilley; 14.04.2020
comment
Я обновил свой ответ, включив в него коралл 9 и убрав строки с кодами состояния. В итоге я использовал case_when, который намного надежнее, чем if_else, но его немного сложнее понять непосвященным. Если ответ работает для вас, подумайте о том, чтобы принять его и / или проголосовать за него. - person Ian Campbell; 14.04.2020
comment
Ян Кэмпбелл. Я снова запустил ваш код и понял, что вы удаляете NF в столбце кода состояния для Coral # 9 (TimeStep 5). Для Коралла № 9 я хочу только первый раз, когда коралл достигнет диапазона размеров (TimeStep 3), и следующую запись, где измерения не проводились и код состояния = NF. - person Eric Dilley; 14.04.2020
comment
Вы можете отказаться от фильтрации по статусу кода, удалив is.na('Status Code') из строки фильтрации. - person Ian Campbell; 14.04.2020
comment
Ян Кэмпбелл, по какой-то причине я все еще получаю тот же результат, независимо от того, включаю ли я оператор is.na (Status Code) в конец вашего кода. Оба случая по-прежнему приводят к одному и тому же результату, когда Коралл № 9 отсутствует в отфильтрованном наборе данных. - person Eric Dilley; 14.04.2020
comment
Я переназначил data из вашего вопроса и запустил код, представленный выше. Вот результат. - person Ian Campbell; 14.04.2020
comment
Ян Кэмпбелл, спасибо за ваше продолжение! Я каким-то образом работал с базой данных, которая добавляла значение, превышающее диапазон (13 мм). Еще раз большое спасибо за ваши ответы на этот вопрос, и я отмечу это как ответ. Надеюсь, это последний вопрос, который у меня есть, но если нет, я напишу еще раз в этом посте. Спасибо еще раз! - person Eric Dilley; 14.04.2020