0
votes

I have a time series dataset with a number of different groups (specified by row value in a group column) and variable columns.

I want to select the row with the min value for a given variable column for each group, and then select the 3 rows for dates "above" and "below" (before and after) the min value as well as the max value itself.

The final result should be 7 rows for each group, with the min value being the 4th row. Ideally I'd like to do this in dplyr...

Dput:

structure(list(Country = c("ARG", "ARG", "ARG", "ARG", "ARG", 
"ARG", "ARG", "ARG", "ARG", "ARG", "ARG", "ARG", "ARG", "ARG", 
"ARG", "ARG", "ARG", "ARG", "ARG", "ARG", "ARG", "ARG", "ARG", 
"ARG", "ARG", "ARG", "ARG", "ARG", "ARG", "ARG", "ARG", "ARG", 
"ARG", "ARG", "ARG", "ARG", "ARG", "ARG", "ARG", "ARG", "ARG", 
"ARG", "ARG", "ARG", "ARG", "ARG", "ARG", "ARG", "ARG", "ARG", 
"ARG", "ARG", "ARG", "ARG", "ARG", "AUS", "AUS", "AUS", "AUS", 
"AUS", "AUS", "AUS", "AUS", "AUS", "AUS", "AUS", "AUS", "AUS", 
"AUS", "AUS", "AUS", "AUS", "AUS", "AUS", "AUS", "AUS", "AUS", 
"AUS", "AUS", "AUS", "AUS", "AUS", "AUS", "AUS", "AUS", "AUS", 
"AUS", "AUS", "AUS", "AUS", "AUS", "AUS", "AUS", "AUS", "AUS", 
"AUS", "AUS", "AUS", "AUS", "AUS", "AUS", "AUS", "AUS", "AUS", 
"AUS", "AUS", "AUS", "AUS", "AUS", "AUS"), Date = structure(c(18307, 
18308, 18309, 18310, 18311, 18312, 18313, 18314, 18315, 18316, 
18317, 18318, 18319, 18320, 18321, 18322, 18323, 18324, 18325, 
18326, 18327, 18328, 18329, 18330, 18331, 18332, 18333, 18334, 
18335, 18336, 18337, 18338, 18339, 18340, 18341, 18342, 18343, 
18344, 18345, 18346, 18347, 18348, 18349, 18350, 18351, 18352, 
18353, 18354, 18355, 18356, 18357, 18358, 18359, 18360, 18361, 
18307, 18308, 18309, 18310, 18311, 18312, 18313, 18314, 18315, 
18316, 18317, 18318, 18319, 18320, 18321, 18322, 18323, 18324, 
18325, 18326, 18327, 18328, 18329, 18330, 18331, 18332, 18333, 
18334, 18335, 18336, 18337, 18338, 18339, 18340, 18341, 18342, 
18343, 18344, 18345, 18346, 18347, 18348, 18349, 18350, 18351, 
18352, 18353, 18354, 18355, 18356, 18357, 18358, 18359, 18360, 
18361), class = "Date"), MobDecline = c(1.33333333333333, -4, 
-1.66666666666667, 7, 6.66666666666667, 5.33333333333333, 7, 
4, 5.66666666666667, -30.3333333333333, -32.6666666666667, 7.66666666666667, 
6.33333333333333, 8, 4.33333333333333, -1.33333333333333, 8, 
8, 8.33333333333333, 9.66666666666667, 12, 5.33333333333333, 
1.33333333333333, 9.66666666666667, 11.6666666666667, -2.33333333333333, 
8, 6.66666666666667, -14, -18.6666666666667, -14.3333333333333, 
-28.3333333333333, -32.3333333333333, -33.6666666666667, -70.3333333333333, 
-71.6666666666667, -75.3333333333333, -84, -84, -75.6666666666667, 
-76, -74.3333333333333, -72, -74.3333333333333, -75.3333333333333, 
-81, -72.6666666666667, -72.3333333333333, -70, -67.3333333333333, 
-70.3333333333333, -68.3333333333333, -68.3333333333333, -67.6666666666667, 
-71, 3.33333333333333, 1.66666666666667, 7.66666666666667, 6, 
6.66666666666667, 7.33333333333333, 10.3333333333333, 5.33333333333333, 
1.66666666666667, 8, 7.33333333333333, 7.66666666666667, 8, 11.6666666666667, 
7, 3, 5.33333333333333, 7.33333333333333, 7, 5.66666666666667, 
9.33333333333333, 2.66666666666667, 0.333333333333333, -8.66666666666667, 
5.66666666666667, 6.33333333333333, 6, 6.66666666666667, -2.66666666666667, 
-5.33333333333333, 1.33333333333333, -4.66666666666667, -7.66666666666667, 
-10.6666666666667, -11.3333333333333, -16.3333333333333, -21.3333333333333, 
-19.6666666666667, -31.3333333333333, -34.6666666666667, -38, 
-38, -40.3333333333333, -45.3333333333333, -43.3333333333333, 
-45.3333333333333, -45.6666666666667, -47.6666666666667, -45.6666666666667, 
-46, -47.3333333333333, -45, -46, -45, -42.3333333333333)), row.names = c(NA, 
-110L), groups = structure(list(Country = c("ARG", "AUS"), .rows = list(
    1:55, 56:110)), row.names = c(NA, -2L), class = c("tbl_df", 
"tbl", "data.frame"), .drop = TRUE), class = c("grouped_df", 
"tbl_df", "tbl", "data.frame"))

Desired Result:

Country Date         MobDecline
    ARG 2020-03-19  -33.6666667
    ARG 2020-03-20  -70.3333333
    ARG 2020-03-21  -71.6666667
    ARG 2020-03-22  -75.3333333
    ARG 2020-03-23  -84.0000000
    ARG 2020-03-24  -84.0000000
    ARG 2020-03-25  -75.6666667
    AUS 2020-03-30  -43.3333333
    AUS 2020-03-31  -45.3333333
    AUS 2020-04-01  -45.6666667
    AUS 2020-04-02  -47.6666667
    AUS 2020-04-03  -45.6666667
    AUS 2020-04-04  -46.0000000
    AUS 2020-04-05  -47.3333333
1
Please provide sample data (standard dataset or dput(...) or data.frame(...)) and your intended output.r2evans
Great, thanks for including the sample data. Have you tried anything, or is this your first attempt and you have no code to start from?r2evans
Have not tried anything, but don't know where to start. Guessing it uses something like dplyr's group_by, then slice(which.min))?TiberiusGracchus2020

1 Answers

1
votes

I think your intended output is incorrect: ARG's "max" (absolutely value!) value is on 2020-03-23 (and -24), yet you show four rows before it and insufficient rows after it.

Try this:

dat %>%
  group_by(Country) %>%
  mutate(most = row_number() == which.max(abs(MobDecline))) %>%
  filter(zoo::rollapply(most, width = 7, FUN = any, fill = FALSE))
# # A tibble: 14 x 4
# # Groups:   Country [2]
#    Country Date       MobDecline most 
#    <chr>   <date>          <dbl> <lgl>
#  1 ARG     2020-03-20      -70.3 FALSE
#  2 ARG     2020-03-21      -71.7 FALSE
#  3 ARG     2020-03-22      -75.3 FALSE
#  4 ARG     2020-03-23      -84   TRUE 
#  5 ARG     2020-03-24      -84   FALSE
#  6 ARG     2020-03-25      -75.7 FALSE
#  7 ARG     2020-03-26      -76   FALSE
#  8 AUS     2020-03-30      -43.3 FALSE
#  9 AUS     2020-03-31      -45.3 FALSE
# 10 AUS     2020-04-01      -45.7 FALSE
# 11 AUS     2020-04-02      -47.7 TRUE 
# 12 AUS     2020-04-03      -45.7 FALSE
# 13 AUS     2020-04-04      -46   FALSE
# 14 AUS     2020-04-05      -47.3 FALSE

(and most can be removed, keeping it here for demonstration).

The use of zoo::rollapply is a much shorter and flexible version than one based on repeated lead and/or lag (which is otherwise one way to approach this).

Now, this is using abs(which.max(...)), which both assumes max absolute value (you did say max, after all) and will return at most one entry, even when tied. If you need +/- 3 rows to include this (so one more row included here), then we can try to use ==, but it will at times fail (R FAQ 7.31), so I'll introduce a "tolerance":

dat %>%
  group_by(Country) %>%
  mutate(most = MobDecline <= (min(MobDecline) + tol)) %>%
  filter(zoo::rollapply(most, width = 7, FUN = any, fill = FALSE))
# # A tibble: 15 x 4
# # Groups:   Country [2]
#    Country Date       MobDecline most 
#    <chr>   <date>          <dbl> <lgl>
#  1 ARG     2020-03-20      -70.3 FALSE
#  2 ARG     2020-03-21      -71.7 FALSE
#  3 ARG     2020-03-22      -75.3 FALSE
#  4 ARG     2020-03-23      -84   TRUE 
#  5 ARG     2020-03-24      -84   TRUE 
#  6 ARG     2020-03-25      -75.7 FALSE
#  7 ARG     2020-03-26      -76   FALSE
#  8 ARG     2020-03-27      -74.3 FALSE
#  9 AUS     2020-03-30      -43.3 FALSE
# 10 AUS     2020-03-31      -45.3 FALSE
# 11 AUS     2020-04-01      -45.7 FALSE
# 12 AUS     2020-04-02      -47.7 TRUE 
# 13 AUS     2020-04-03      -45.7 FALSE
# 14 AUS     2020-04-04      -46   FALSE
# 15 AUS     2020-04-05      -47.3 FALSE