根据每年不同的最大值和最小值筛选数据库

Filter database based on max and min that varies each year

提问人:Teresa 提问时间:11/9/2023 更新时间:11/9/2023 访问量:84

问:

我有一个数据库,如下所示:

    Year ID Date Occupy
    2010 1  10   Yes
    2010 2  11   No
    2010 3  12   Yes
    2010 4  9    No
    2010 5  15   No
    2011 7  7    Yes
    2011 8  9    Yes
    2011 9  10   Yes
    2011 11 12   No

我正在尝试创建一个代码,该代码首先检查哪些日期是每年第一个和最后一个被占用的日期(此处日期是每月的某一天)。2010 年应为 10 日和 12 日,2011 年应为 7 日和 10 日。然后,代码应过滤掉日期小于或大于这些第一个和最后一个占用日期的行。

结果应该是:

    Year ID Date Occupy
    2010 1  10   Yes
    2010 2  11   No
    2010 3  12   Yes
    2011 7  7    Yes
    2011 8  9    Yes
    2011 9  10   Yes
  

我尝试使用以下两个单独的部分来执行这两个步骤:

    lapply(function(x) c(min(x), max(x)))

然后用润滑剂进行分组和过滤,但一切都崩溃了或不符合我想要的效果。

R 滤波器 组 - 按 最大 最小值

评论


答:

2赞 Wimpel 11/9/2023 #1

一种单行方法(可能不是最优雅的方式,但我想不出另一种方法;-))data.table

library(data.table)
mydata <- fread("    Year ID Date Occupy
                 2010 1  10   Yes
                 2010 2  11   No
                 2010 3  12   Yes
                 2010 4  9    No
                 2010 5  15   No
                 2011 7  7    Yes
                 2011 8  9    Yes
                 2011 9  10   Yes
                 2011 11 12   No")

# or setDT(mydata)
mydata[mydata[Occupy == "Yes", .(min = min(Date), max = max(Date)), Year], 
   .(Year = x.Year, ID = x.ID, Date = x.Date, Occupy = x.Occupy), 
   on = .(Year, Date >= min, Date <= max)]

#    Year ID Date Occupy
# 1: 2010  1   10    Yes
# 2: 2010  2   11     No
# 3: 2010  3   12    Yes
# 4: 2011  7    7    Yes
# 5: 2011  8    9    Yes
# 6: 2011  9   10    Yes

其他方法,相同的结果,更短的代码

mydata[ID %in% mydata[mydata[Occupy == "Yes", .(min = min(Date), max = max(Date)), Year], 
                      ID, on = .(Year, Date >= min, Date <= max)], ]

评论

1赞 thelatemail 11/9/2023
我喜欢这个逻辑 - 避免位的另一种变体 -x.variabledat[dat[dat[Occupy=="Yes", .(mnd=min(Date), mxd=max(Date)), by=Year], on=.(Year,Date>=mnd,Date<=mxd), which=TRUE]]
2赞 dufei 11/9/2023 #2

如果您更喜欢语法:dplyr

library(dplyr)

df <- tribble(
        ~Year, ~ID, ~Date, ~Occupy,
         2010,   1,    10,   "Yes",
         2010,   2,    11,    "No",
         2010,   3,    12,   "Yes",
         2010,   4,     9,    "No",
         2010,   5,    15,    "No",
         2011,   7,     7,   "Yes",
         2011,   8,     9,   "Yes",
         2011,   9,    10,   "Yes",
         2011,  11,    12,    "No"
        )

df |> 
  mutate(
    min_occupied = min(Date[Occupy == "Yes"]),
    max_occupied = max(Date[Occupy == "Yes"]),
    .by = Year
  ) |> 
  filter(between(Date, min_occupied, max_occupied))
#> # A tibble: 6 × 6
#>    Year    ID  Date Occupy min_occupied max_occupied
#>   <dbl> <dbl> <dbl> <chr>         <dbl>        <dbl>
#> 1  2010     1    10 Yes              10           12
#> 2  2010     2    11 No               10           12
#> 3  2010     3    12 Yes              10           12
#> 4  2011     7     7 Yes               7           10
#> 5  2011     8     9 Yes               7           10
#> 6  2011     9    10 Yes               7           10

创建于 2023-11-08 使用 reprex v2.0.2

评论

0赞 Teresa 11/9/2023
嗨@dufei,感谢您的建议。但是,由于某种原因,此代码不起作用:我复制并粘贴了完全相同的代码(没有更改昏迷),并得到了不同的结果。它生成了两个年份(7 和 12)的最小和最大日期相同的小插曲,因此只删除了 2010 年的最后一行(日期=15)。这可能是包/R 版本问题吗?
0赞 Teresa 11/9/2023
我更新了我的 Rstudio 和 dplyr,但仍然有问题。然后我把“.by”改成了“group_by”,把“|>”改成了“%>%”。现在,您的整个代码都可以在 reprex 上运行。但是,当我使用我的数据运行您的整个代码时,R 会抛出一个错误并说“由错误引起:! 必须是长度 1“ 实际上,关于 between() 函数的 R 文档提到 left 和 right”必须是标量”。所以我对为什么这段代码与 reprex 一起运行而不是使用我的实际数据感到困惑。有什么想法吗?between()left
0赞 dufei 11/9/2023
嗨,特蕾莎,你有没有确定你正在使用的来自包装? 接受向量作为边界值。我还建议您首先更新R安装(我使用的是4.3.1版)以消除与等相关的错误。可以使用以下命令检查 R 版本。希望这有帮助!between()dplyrdplyr::between()|>R.Version()
0赞 Teresa 11/9/2023
匪夷所思!更新 R 并指定 dplyr::between 代码后,代码有效!谢谢!
0赞 Friede 11/9/2023 #3

使用易于阅读的语法的不常见方法:{dplyr}

library(dplyr)
data1 <- 
  data |>
  filter(Occupy == "No") |>
  mutate(minD = NA, 
         maxD = NA)
data2 <- 
  data |>
  filter(Occupy == "Yes") |>
  mutate(minD = min(Date), 
         maxD = max(Date), .by = Year)

rbind(data1, data2) |>
  mutate(
    flag = ifelse(
      Occupy == "No" & Date %in% c(min(minD, na.rm = TRUE):max(maxD, na.rm = TRUE)),
      1L, 0L), .by = Year) |>
  filter(Occupy == "Yes" | flag == 1L) |>
  group_by(Year, Date) |>
  arrange(Date, .by_group = TRUE) |>
  select(-c(minD, maxD, flag))

这给了

#> # A tibble: 6 × 4
#> # Groups:   Year, Date [6]
#>    Year    ID  Date Occupy
#>   <int> <int> <int> <chr> 
#> 1  2010     1    10 Yes   
#> 2  2010     2    11 No    
#> 3  2010     3    12 Yes   
#> 4  2011     7     7 Yes   
#> 5  2011     8     9 Yes   
#> 6  2011     9    10 Yes

使用 和 的 R 基础版本。ave()by()

# (1) 
data <- within(data, {
  # I think ave's FUN argument does not accept more than one function 
  minD <- ave(Date, list(Year, Occupy), FUN = min)
  maxD <- ave(Date, list(Year, Occupy), FUN = max)
  }) 
# (2)
data[data$Occupy == "No", c("minD", "maxD")] <- NA 
# (3)
by(data,
   INDICES = data$Year, 
   FUN = \(x) {
       x$flag <- ifelse(x$Occupy == "No" & x$Date 
                        %in% c(min(x$minD, na.rm = TRUE):max(x$maxD, na.rm = TRUE)), 
                        1L, 0L)
       return(x)}) |>
  `names<-`(NULL) |>
  { \(x) do.call("rbind", x) }() |>
  { \(x) x[x$Occupy == "Yes" | x$flag == 1L, 1L:4L] }()

知道如何将 (1)、(2) 和 (3) 连接在一起会很有趣。