在 for 循环中使用 if 条件对数据帧进行子集

Subset a dataframe using if-conditions inside a for loop

提问人:V Fishlock 提问时间:1/25/2023 最后编辑:V Fishlock 更新时间:3/20/2023 访问量:62

问:

我正在尝试使用这种基本结构来解决数据重塑问题;

for(i in 1:5) {                           # Head of for-loop
  if(i < 4) {                             # First if-condition 
    if(i %in% seq(2, 10, 2)) {            # Second if-condition 
      print(i)                            # Some output
    }
  }
}

免责声明,尽管我在这段代码中讨论的是“日期”,但它们是儒略日期系统,因此它们不是 POSIXct 格式,而是表现为整数。

我想使用值列表(“dates”)在满足 2 个条件的列表“bydates”中查找事例,并将它们写入新的 df。“bydates”有 2275 个观测值,涉及 4 个变量;NatalName、JStart、JEnd、FAM(格式为 chr、num、num、chr)。

对于“日期”中的每个值 (i) 我想评估 JStart 是否< i,以及 JEnd 是否> i,以及是否满足这两个条件以写入格式为 i、NatalNAme、FAM 的列表 df。

这是我的尝试之一,我一直在回想(我也尝试了函数,ifelse 和 if_else,但没有成功)。

lists <- c() # create a blank variable to store the result

for(i in dates) 
        {if(bydates$Jstart <= i) {
                if(JEnd > i) {
                        lists <- as.df(i, bydates$FAM, bydates$NatalName)
        }
}
}

这将返回 “Error in if (bydates$Jstart <= i) { : the condition has length > 1”

我认为这意味着我的“bydates”df 中的多个值符合条件,这是正确的,但这是否意味着我应该循环使用“bydates”?我花了一个多星期的时间研究这个问题,但我仍然被困住了。我也很困惑为什么我没有收到通常报告的“条件长度为 >1,并且只会使用第一个元素”错误。

任何帮助非常感谢。

编辑:根据@Stefan的要求,使用dput的数据片段

> dput(dates[1:4])
c(744, 864, 984, 1224)
> dput(head(bydates))
structure(list(NatalName = c("AAN12", "AAN18", "AAN20", "ABI96", 
"ABR12", "ABR17"), Jstart = c(1113, 1178, 1203, 914, 1105, 1175
), JEnd = c(1158, 1180, -23053, 915, -23053, -23053), FAM = c("AA", 
"AA", "AA", "AA", "AA", "AA")), row.names = c(NA, -6L), class = c("tbl_df", 
"tbl", "data.frame"))
R for 循环 数据操作

评论

0赞 D.J 1/25/2023
您可能想看看如何在同一个 .也许这个答案会有所帮助。if()if()
0赞 Godrim 1/25/2023
您很可能遇到了对向量不起作用的问题,请尝试将它们替换为 。有关详细信息,请参阅此处ififelse()
1赞 Roland 1/25/2023
如果没有看到实际的代码和数据,很难确定,但我怀疑你不需要循环,也不需要.您可能只需要使用逻辑向量对数据进行子集化。if

答:

0赞 stefan 1/25/2023 #1

问题是没有矢量化。它旨在用于控制脚本的流程,即如果满足一个条件,则执行 A.换句话说,要求条件返回长度为 1 的逻辑向量,而条件返回长度为 1 >向量,即长度等于数据集中的行数。因此,您会收到一个错误。ififbydates$Jstart <= i

虽然有矢量化,但总体上不需要 an。您要实现的是数据集的子集,这可以通过使用例如ifelseif

bydates[bydates$Jstart <= i & bydates$JEnd > i,]

此外,由于您需要一个数据集列表,您可以考虑使用而不是循环:lapplyfor

基于一些虚假示例数据使用(和用于子集)的最小可重现示例可能如下所示:lapplysubset

bydates <- data.frame(
  JStart = seq(as.Date("2022-01-03"), as.Date("2022-01-12"), by = "day"),
  JEnd = seq(as.Date("2022-01-01"), as.Date("2022-01-10"), by = "day"),
  FAM = letters[1:10],
  NatalName = LETTERS[1:10]
)

dates <- c(as.Date("2022-01-05"), as.Date("2022-01-10"))

lapply(dates, function(i) {
  bydates$i <- i
  subset(bydates, JStart >= i & JEnd < i, c(i, FAM, NatalName))
})
#> [[1]]
#>            i FAM NatalName
#> 3 2022-01-05   c         C
#> 4 2022-01-05   d         D
#> 
#> [[2]]
#>            i FAM NatalName
#> 8 2022-01-10   h         H
#> 9 2022-01-10   i         I

评论

0赞 V Fishlock 1/26/2023
谢谢@stefan,这很有道理,我确实明白你的意思。但是,我不确定这是否有效;在我的数据上使用您的代码,我得到;错误 : !不能将列子集超过末尾。i 位置 744 不存在。i 只有4列。运行以查看错误发生的位置。警告消息:在日期$i <- i:强制LHS到列表正如我在原始帖子中所说,这里的日期实际上是儒略格式,744是列表中的第一个值。即使我添加日期<- as.list(dates),我仍然收到错误减去最后一个警告x[r, vars, drop = drop]rlang::last_error()
0赞 stefan 1/26/2023
您能否在控制台中提供一段 and via dput(head(bydates))' 并将输出作为编辑复制到您的帖子中,并以类似方式使用例如.有关详细信息,请参阅如何制作出色的 R 可重现示例bydatesdatesdput(), e.g. type dput(dates[1:5])dput()
0赞 V Fishlock 3/20/2023 #2

在朋友的帮助下,我用咕噜咕噜想出了一个解决方案;

    library(purrr)

    setdates<-function(y){
    
    bydates %>%
            filter(Jstart<= y & JEnd >y) %>%
            mutate(Date = y) %>%
            select(NatalName, FAM, Date) %>%
            arrange(FAM)}

    famdat<-map_df(dates, setdates)

我的问题是,在设置处理数据操作的函数之前,我试图集成日期。以防这对其他人有帮助......