【问题标题】:Filtering a data frame in R using a for loop使用for循环过滤R中的数据框
【发布时间】:2020-01-10 22:50:56
【问题描述】:

我是 R 新手,在使用更高级的过滤时遇到了困难。我有一个包含 1500 行家庭成员的数据框,需要过滤掉至少有 1 人超过 24 岁的家庭成员。例如,在下面的样本集中,我只想保留第 3 行,4 和 5。

PersonalID     DOB         HouseholdID
1            1961-04-15      123
2            2017-01-12      123
3            2000-01-02      122
4            2001-03-05      122
5            1996-08-22      122

最初,我只是过滤以获得该年龄范围内每个人的新数据框,然后再次过滤原始数据框(一次又一次,依此类推......)与 25 岁以下的人的每个 HouseholdID 以检查是否有人否则 HouseholdID 超过 24 岁。

每当我一遍又一遍地做同样的事情时,似乎可能有一种方法可以使用函数,但我很难想出一个可行的方法。这是我目前的尝试,但我知道它有很多问题:

UNDER25df <- filter(df, DOB >= "yyyy-mm-dd")

for (UNDER25df$HouseholdID in df) {
    if (all(df$DOB >= "yyyy-mm-dd")) {
       view(filter(df, HouseholdID == "$HouseholdID"))
     }
 }

我得到的错误是:

“}”中出现意外的“}”

但我很确定我可以在 R 的 for 循环中嵌套一个 if 语句,而且我对括号的位置很小心,所以我不知道它到底指的是什么。

我不确定是否可以以这种方式遍历数据框,或者这是否有意义。我已经读过矢量化对于高级过滤通常可能更好,但尝试阅读有关它的文档并且无法真正了解如何使其跳转到这个问题。有人有我应该研究的建议或方向吗?

【问题讨论】:

  • 不知道为什么在这里需要for 循环,而比较中的值没有意义DOB &gt;= "yyyy-mm-dd"HouseholdID == "$HouseholdID" 或循环for (UNDER25df$HouseholdID in df)。可能是df %&gt;% group_by(HouseholdID) %&gt;% filter(all(as.Date(DOB) &gt;= as.Date("2010-01-01")))

标签: r dataframe


【解决方案1】:

你不需要循环。试试

library(lubridate)
library(dplyr)

set.seed(1)
df <- tibble(DOB = Sys.Date() - sample(3000:12000, 6), 
             personalID = 1:6,
             HouseholdID = c(1,1,2,2,2,3))
df$DOB

# grab householdID from all persons that are at least 24
oldies <- df[(lubridate::today() - lubridate::ymd(df$DOB)) > years(24),
             "HouseholdID", TRUE]

# base R way
oldies <- df[as.Date(df$DOB) > as.Date("1993-2-10"),
             "HouseholdID", TRUE]

# household members in a household with someone 24 or older
df %>% 
  filter(HouseholdID %in% oldies)


# household members in a household with noone 24 or older
df %>% 
  filter(!(HouseholdID %in% oldies))

【讨论】:

  • 我必须使用 lubridate 吗?我有一个我正在使用的特定时间范围,所以我知道为了我的数据的目的,25 岁​​以下是指 1993 年 10 月 2 日或之后出生的人,我创建了一个新的 df,由 93 年 10 月 2 日之前出生的人过滤,然后尝试将其与df %&gt;% filter(HouseholdID %in% oldies) 一起使用,但这给了我一个 0x106 tibble。从理论上讲,这应该提供一个家庭中 25 岁或以上的每个人的数据框吗?我可以以某种方式使用它来只保留那些不在其中一个家庭中的人吗?
  • 不,您没有使用 lubridate,但在我看来,它使代码更具可读性和更易于编码。您还可以使用基本 R 函数。关于你的第一个问题:是的,这就是目的。第二:是的,我也包括了这个
【解决方案2】:

我不确定是否要保留所有用户小于或等于 24 岁的按 ID 分组的行。如果是这样,那么也许你可以试试下面的代码

library(lubridate)
dfout <- subset(df, ave(floor(time_length(Sys.Date()-as.Date(DOB),"years"))<=24, HouseholdID, FUN = all))
  • 如果你真的想用for循环来制作,那么下面是一个例子
dfout <- data.frame()
for (id in unique(df$HouseholdID)) {
  subdf <- subset(df,HouseholdID == id)
  if (with(subdf, all(floor(time_length(Sys.Date()-as.Date(DOB),"years"))<=24))) {
    dfout <- rbind(dfout,subdf)
  }
}

上述两种方法都可以为您提供如下所示的结果

> dfout
  PersonalID        DOB HouseholdID
3          3 2000-01-02         122
4          4 2001-03-05         122
5          5 1996-08-22         122

数据

df <- structure(list(PersonalID = 1:5, DOB = c("1961-04-15", "2017-01-12", 
"2000-01-02", "2001-03-05", "1996-08-22"), HouseholdID = c(123L, 
123L, 122L, 122L, 122L)), class = "data.frame", row.names = c(NA, 
-5L))

【讨论】:

    【解决方案3】:

    我不确定你是要选择所有人都在 24 岁以上还是至少一个人在 24 岁以上的家庭。无论如何,你可以使用subsetave

    subset(df, ave(as.integer(format(Sys.Date(), "%Y")) - 
                   as.integer(format(DOB, "%Y")) >= 24, HouseholdID, FUN = any))
    

    这会选择至少一个人超过 24 岁的家庭。如果您想选择所有人都超过 24 岁的家庭,请在 FUN 参数中使用 all 而不是 any

    同样,使用dplyr,我们可以使用

    library(dplyr)
    df %>%
       group_by(HouseholdID) %>%
       filter(any(as.integer(format(Sys.Date(), "%Y")) - 
                  as.integer(format(DOB, "%Y")) >= 24))
    

    【讨论】:

      猜你喜欢
      • 2021-11-21
      • 1970-01-01
      • 2017-10-04
      • 1970-01-01
      • 1970-01-01
      • 2021-08-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多