Как выбрать первое значение в группе - без фильтрации? - PullRequest
1 голос
/ 26 февраля 2020

Я пытаюсь найти внезапное уменьшение значения (столбец v44) во многих небольших группах (из file_id и type) в кадре данных / tibble (dat).

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

РЕДАКТИРОВАТЬ: Хорошо, хорошо, я переписал ниже, чтобы небольшое представление.

library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
library(tibble)
library(ggplot2)

min <- 3000
max <- 50000
fac <- 1.5

dat <- tribble( ~ file_id, ~ type, ~ cycle, ~ v44,
               "hey", "std", 0, 50300,
               "hey", "std", 1, 40000,
               "hey", "std", 2, 35000,
               "hey", "std", 3, 32000,
               "hey", "std", 4, 31000,
               "hey", "std", 5, 30000,
               "hey", "std", 6, 29500,
               "hey", "smp", 1, 40100,
               "hey", "smp", 2, 35100,
               "hey", "smp", 3, 32100,
               "hey", "smp", 4, 5000,
               "hey", "smp", 5, 20,
               "hey", "smp", 6, 10,
               "hi", "std", 0, 49000,
               "hi", "std", 1, 39700,
               "hi", "std", 2, 32000,
               "hi", "std", 3, 30000,
               "hi", "std", 4, 29500,
               "hi", "std", 5, 29400,
               "hi", "std", 6, 29200,
               "hi", "smp", 1, 49100,
               "hi", "smp", 2, 39600,
               "hi", "smp", 3, 31100,
               "hi", "smp", 4, 30000,
               "hi", "smp", 5, 29600,
               "hi", "smp", 6, 29400)

dat %>%
  ggplot(aes(x = cycle, y = v44, colour = type)) +
  geom_line(aes(group = paste(file_id, type))) +
  facet_grid(rows = vars(type))


dat %>%
    group_by(file_id, type) %>%
    mutate(v44_low = v44 <= min,  # creates a flag
           v44_high = v44 >= max,
           v44_diff = lead(v44) - v44) %>%
    mutate(v44_drop = v44_diff < fac * first(filter(., !v44_low, !v44_high)$v44_diff)) %>%
    ungroup(file_id, type)
#> # A tibble: 26 x 8
#>    file_id type  cycle   v44 v44_low v44_high v44_diff v44_drop
#>    <chr>   <chr> <dbl> <dbl> <lgl>   <lgl>       <dbl> <lgl>   
#>  1 hey     std       0 50300 FALSE   TRUE       -10300 TRUE    
#>  2 hey     std       1 40000 FALSE   FALSE       -5000 FALSE   
#>  3 hey     std       2 35000 FALSE   FALSE       -3000 FALSE   
#>  4 hey     std       3 32000 FALSE   FALSE       -1000 FALSE   
#>  5 hey     std       4 31000 FALSE   FALSE       -1000 FALSE   
#>  6 hey     std       5 30000 FALSE   FALSE        -500 FALSE   
#>  7 hey     std       6 29500 FALSE   FALSE          NA NA      
#>  8 hey     smp       1 40100 FALSE   FALSE       -5000 FALSE   
#>  9 hey     smp       2 35100 FALSE   FALSE       -3000 FALSE   
#> 10 hey     smp       3 32100 FALSE   FALSE      -27100 TRUE    
#> # … with 16 more rows

но это оказалось очень-очень медленно, так как существует много групп.

См. https://github.com/tidyverse/dplyr/issues/3294 для объяснения того, почему фильтрация во многих группах медленная.

Я знаю, как переписать это в более быструю версию, но она все равно создаст копию:

out <- dat %>%
  group_by(file_id, type) %>%
  mutate(v44_low = v44 <= min,
         v44_high = v44 >= max,
         v44_diff = lead(v44) - v44) %>%
  filter(!v44_low, !v44_high) %>%
  mutate(v44_drop = v44_diff < fac * first(.$v44_diff)) %>%
  select(file_id, type, cycle, v44_drop)

out <- dat %>%
    left_join(out, by = c("file_id", "type", "cycle")) %>%
    ungroup(file_id, type)
out
#> # A tibble: 26 x 5
#>    file_id type  cycle   v44 v44_drop
#>    <chr>   <chr> <dbl> <dbl> <lgl>   
#>  1 hey     std       0 50300 NA      
#>  2 hey     std       1 40000 FALSE   
#>  3 hey     std       2 35000 FALSE   
#>  4 hey     std       3 32000 FALSE   
#>  5 hey     std       4 31000 FALSE   
#>  6 hey     std       5 30000 FALSE   
#>  7 hey     std       6 29500 NA      
#>  8 hey     smp       1 40100 FALSE   
#>  9 hey     smp       2 35100 FALSE   
#> 10 hey     smp       3 32100 TRUE    
#> # … with 16 more rows

Создано в 2020-02-26 с помощью пакета Представить (v0.3.0)

Это потому, что я хочу сохранить строки с высоким / низким значения, но я не хочу, чтобы они использовались для расчета разницы.

Можно ли переписать это, чтобы оно было быстрее? Где мне не нужно фильтровать внутри групп, и мне не нужно создавать копию для слияния обратно в конечный результат?

Ответы [ 2 ]

1 голос
/ 26 февраля 2020

Когда вы делаете group_by, first работает на уровне группы. Так, например, мы используем простой тест:

dat %>%
group_by(file_id, type) %>%
mutate(is_first = v44 == first(v44))

# A tibble: 26 x 5
# Groups:   file_id, type [4]
   file_id type  cycle   v44 is_first
   <chr>   <chr> <dbl> <dbl> <lgl>   
 1 hey     std       0 50300 TRUE    
 2 hey     std       1 40000 FALSE   
 3 hey     std       2 35000 FALSE   
 4 hey     std       3 32000 FALSE   
 5 hey     std       4 31000 FALSE   
 6 hey     std       5 30000 FALSE   
 7 hey     std       6 29500 FALSE   
 8 hey     smp       1 40100 TRUE    
 9 hey     smp       2 35100 FALSE   
10 hey     smp       3 32100 FALSE 

Но если вы звоните. $ V44, вы делаете это для всего data.frame, вне группы:

dat %>%
group_by(file_id, type) %>%
mutate(is_first = v44 == first(.$v44))

   file_id type  cycle   v44 is_first
   <chr>   <chr> <dbl> <dbl> <lgl>   
 1 hey     std       0 50300 TRUE    
 2 hey     std       1 40000 FALSE   
 3 hey     std       2 35000 FALSE   
 4 hey     std       3 32000 FALSE   
 5 hey     std       4 31000 FALSE   
 6 hey     std       5 30000 FALSE   
 7 hey     std       6 29500 FALSE   
 8 hey     smp       1 40100 FALSE   
 9 hey     smp       2 35100 FALSE   
10 hey     smp       3 32100 FALSE

Вы можете видеть для строки 8, первое значение не показывает TRUE. Поэтому, если вы определяете различия только внутри группы, не используйте. $.

Из описанной проблемы вам нужно работать только с вектором. Если вы используете фильтр, вы работаете со всем фреймом данных, поэтому я предлагаю использовать [который работает с вектором. В приведенном ниже примере я также заменил различия для значений вне min и max на 0:

test = dat %>%
  group_by(file_id, type) %>%
  mutate(v44_diff = lead(v44) - v44) %>%
  mutate(v44_diff = replace(v44_diff,v44 < min | v44 > max,0)) %>%
  mutate(v44_drop = v44_diff < fac*first(v44_diff[v44_diff!=0])) %>%
  ungroup(file_id, type)
0 голосов
/ 26 февраля 2020

Попробуйте следующие общие шаги c: -

Предположим, имя переменной, в которой вы сохраняете данные, - my_data

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

my_new_data <- as.data.frame (my_data, c (my_data! = Head (my_data) | my_data! = Tail (my_data))) </p>

Попробуйте это ...

...