提问人:Teresa 提问时间:11/9/2023 更新时间:11/9/2023 访问量:84
根据每年不同的最大值和最小值筛选数据库
Filter database based on max and min that varies each year
问:
我有一个数据库,如下所示:
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)))
然后用润滑剂进行分组和过滤,但一切都崩溃了或不符合我想要的效果。
答:
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.variable
dat[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()
dplyr
dplyr::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) 连接在一起会很有趣。
评论