У меня есть набор данных, который выглядит так (но с данными за больше лет):

dat <- data.frame(date = as.Date(c("2000-01-01","2000-03-31","2000-07-01","2000-09-30", 
                                   "2001-01-01","2001-03-31","2001-07-01","2001-09-30")),
                  value = c(0.8,1,0.2,0,0.7,1,0.2,0))

Я хотел бы выбрать первый случай, когда «значение»> = 0,8 для каждого года.

Итак, для приведенного выше набора данных я ожидал бы, что на выходе будет фрейм данных с двумя строками и двумя столбцами:

new_dat <- data.frame(date = as.Date(c("2000-01-01", "2001-03-31")),
                      value = c(0.8,0.7))
print(new_dat)

Я пытался сделать это с помощью dplyr:

dat_grouped <- dat %>%
  mutate(year = year(date))%>%
  group_by(year) %>%
  distinct(value >= 0.8, date = date) #wanted to keep the date column

Он дает мне значения ИСТИНА ЛОЖЬ для столбца «значение», но я не могу найти хороший способ выбрать первое ИСТИННОЕ значение. Я пробовал обернуть отдельный () с помощью first (), и я пробовал подключать к which.min (), но ни один из них не работал.

Я нашел эту запись, но надеялся на аккуратное решение. У меня также возникла проблема с адаптацией этого кода к моему набору данных. Я получаю «Ошибка в apply (x, 2, my.first): dim (X) должен иметь положительную длину»

Я также хотел бы выполнить тот же запрос, но в первый раз, когда значение <= 0,2. Но я предполагаю, что это будет тот же процесс с другим логическим запросом. Возможно, логический оператор - не лучший вариант?

Любые предложения приветствуются. Спасибо.

0
Joshua Culpepper 7 Июл 2021 в 02:01

3 ответа

Лучший ответ

Вы можете использовать slice -

library(dplyr)
library(lubridate)

dat %>%
  group_by(year = year(date)) %>%
  slice(match(TRUE, value >= 0.8)) %>%
  ungroup

#   date       value  year
#  <date>     <dbl> <int>
#1 2000-01-01   0.8  2000
#2 2001-03-31   1    2001

Если каждый год гарантированно будет иметь хотя бы одно значение, превышающее 0,8, вы также можете использовать which.max -

dat %>%
  group_by(year = year(date)) %>%
  slice(which.max(value >= 0.8)) %>%
  ungroup
2
Ronak Shah 7 Июл 2021 в 02:29

Вы можете использовать dplyr::filter, чтобы получить только значения> = 0,8, затем сгруппировать по году (что можно получить с помощью lubridate::year) и dplyr::slice_min, чтобы получить первую дату.

dat <- data.frame(date = as.Date(c("2000-01-01","2000-03-31","2000-07-01","2000-09-30", 
                                   "2001-01-01","2001-03-31","2001-07-01","2001-09-30")),
                  value = c(0.8,1,0.2,0,0.7,1,0.2,0))

library(lubridate)
#> 
#> Attaching package: 'lubridate'
#> The following objects are masked from 'package:base':
#> 
#>     date, intersect, setdiff, union
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

dat %>% 
  filter(value >= .8) %>% 
  group_by(year = year(date)) %>% 
  slice_min(date)
#> # A tibble: 2 x 3
#> # Groups:   year [2]
#>   date       value  year
#>   <date>     <dbl> <dbl>
#> 1 2000-01-01   0.8  2000
#> 2 2001-03-31   1    2001

Создано 2021-07-06 пакетом REPEX (v2.0.0)

Если ваши данные уже отсортированы по дате, вы можете пропустить filter и использовать метод ниже (или один из методов Ронака)

dat %>% 
  group_by(year = year(date)) %>% 
  slice_max(value >= 0.8, with_ties = FALSE)
#> # A tibble: 2 x 3
#> # Groups:   year [2]
#>   date       value  year
#>   <date>     <dbl> <dbl>
#> 1 2000-01-01   0.8  2000
#> 2 2001-03-31   1    2001

Создано 07.07.2021 г. пакетом REPEX (v2.0.0)

2
IceCreamToucan 7 Июл 2021 в 22:07

3 решения Base R:

# Repeatedly subsetting: data.frame => stdout(console)
subset(
  subset(
    with(
      dat,
      dat[order(date),]
    ),
    value >= 0.8
  ), 
  ave(
    substr(
      date, 
      1, 
      4
    ), 
    substr(
      date, 
      1, 
      4
    ), 
    FUN = seq.int
  ) == 1
)

# All in one base R using `Split-Apply-Combine`: 
# data.frame => stdout(console)
data.frame(
  do.call(
    rbind, 
    lapply(
      with(
        dat, 
        split(
          dat, 
          substr(date, 1, 4)
        )
      ),
      function(x){
        head(
          subset(
            with(x, x[order(date),]),
            value >= 0.8
          ), 
          1
        )  
      }
    )
  ),
  row.names = NULL
)

# In stages Base R: 
# Subset out values not meeting the threshold value
# criteria: above_threshold_df => data.frame
above_threshold_df <- subset(
  with(dat, dat[order(date),]), 
  value >= 0.8
)

# Extract the year from the date variable: 
# grp => integer vector 
grp <- with(above_threshold_df, substr(date, 1, 4))

# Use the group vector to extract the first entry from 
# each year that meets the threshold: 
# res => data.frame
res <- subset(
  above_threshold_df,
  ave(
    grp, 
    grp, 
    FUN = seq.int
  ) == 1
)
0
hello_friend 7 Июл 2021 в 00:01