【问题标题】:dplyr filter not filtering entire datasetdplyr 过滤器不过滤整个数据集
【发布时间】:2021-04-14 18:35:52
【问题描述】:

我有一个名为 df 的数据框,如下所示:

Sample nr smooth channel timepoint PatientID
AA35456 24.7361 fam 1 121
AA35456 107.3580 fam 1 121
AA35456 72.0639 fam 1 121
AA35456 43.8766 vic 1 121
AA35456 2382.8700 vic 1 121
AA35444 49.6488 vic 1 121
AA35444 72.0639 fam 3 121
AA35444 43.8766 vic 3 121
AA35444 72.0639 fam 3 121
AA35444 43.8766 vic 3 121
AA35444 72.0639 fam 3 123
AA35442 43.8766 vic 3 123
AA35444 72.0639 fam 6 126
AA35442 43.8766 vic 6 126
AA35444 72.0639 fam 6 126
AA35442 43.8766 vic 6 126
AA35444 72.0639 fam 6 126
AA35442 43.8766 vic 6 126

我有另一个名为 activefilter 的数据框,看起来像这样

timepoint PatientID
3 121
6 124
3 123
6 123

数据框 df 有大约 300 万行,数据框 activefilter 中的行数取决于研究问题。 我想要做的是子集数据帧df 基于我尝试过的有源过滤器

finaldf <- df %>% filter( PatientID == activefilter$PatientID & timepoint == activefilter$timepoint)

但这并不总是有效

  1. 它有时会跳过患者 ID 和时间点。
  2. 它只获取数据框过滤值的一部分。假设我有 50 行时间点为 3 的患者 ID 121,它将只返回其中的 17 行。

这是应该在 for 循环中完成的事情吗?

【问题讨论】:

  • 能否请您dput()您的数据集。
  • 这不只是df %&gt;% inner_join(activefilter)
  • @missuse,不会在 OP 的过滤器中使用 %in% 还包括具有其他人时间点的患者吗?我认为inner_join 在这里更好,因为它明确地寻找两个匹配列之间的协调。
  • @missuse,这将包括不应被过滤的患者的时间点记录
  • @TarJae 由于帖子长度我不能

标签: r dataframe dplyr filtering


【解决方案1】:

您应该使用dplyr::inner_join 而不是dplyr::filter

如果你打电话

df %>% inner_join(activefilter)

它只会根据任何共享列给出df 中与activefilter 中的条目匹配的行

【讨论】:

  • 感谢@andrewb 这为我解决了问题
【解决方案2】:

@TarJae 和@akrun 的方法是正确的——半连接或内连接通过过滤到df 的行,其中PatientIDtimepoint 匹配来自@987654324 的行@。连接也非常快,所以我敢打赌过滤 3M 行基本上是瞬时的。


我很好奇为什么你最初的方法“有点”奏效而不是完全失败。原因(至少和我目前理解的一样)是基础 R 将“回收”向量。因此,当您指定activefilter$PatientID 时,您将获得来自activefilterPatientID 列的值向量。如果该向量比df 短,它将重复该向量以填充差异。

因此,当您使用 filter(PatientID == activefilter$PatientID...) 时,您实际上是在问“df 中的第一个 PatientID 是否与 activefilter 中的第一个 PatientID 匹配?df 中的第二个 PatientID 是否与 @ 中的第二个 PatientID 匹配987654335@?”等等。如果您的数据非常大,或者如果两个数据集具有相似的排序模式,您可能会偶然获得一些匹配项,但您不会得到所有匹配项。

作为一个更简单的示例,我们可以查看dplyr 中包含的两个数据集。我对第一个进行了排序以使其成为更好的示例:

activefilter <- band_instruments %>% arrange(plays)
df <- band_members

activefilter
# A tibble: 3 x 2
  name  plays 
  <chr> <chr> 
1 Paul  bass  
2 John  guitar
3 Keith guitar

df
# A tibble: 3 x 2
  name  band   
  <chr> <chr>  
1 Mick  Stones 
2 John  Beatles
3 Paul  Beatles

请注意,两者都有一个name 列,其中两个表对两个表(Paul 和John)是通用的,但只有一个(John)按位置匹配。如果我们复制您的原始代码,它只会捕获 John。

df %>%
 filter(name == activefilter$name)
# A tibble: 1 x 2
  name  band   
  <chr> <chr>  
1 John  Beatles

最好使用内连接或半连接来获得重叠。在您的示例中,activefilter 没有与 df 匹配的列之外的其他列,因此它们是等效的。在我的示例中,instruments 表添加了我的第一个表中不存在的另一列。 Inner_join 将抓取新列,而 semi join 将仅包含第一个表中的列。

@TarJae 有用地指出的另一个区别是 semi_join 将识别 df 中与 activefilter 中的行匹配的行,而如果 activefilter 中的任何行匹配,inner_join 将提供重复的行重复。这可能会或可能不会发生在您的原始数据中,如果是这样,我不确定您想要哪种行为,但仅供参考。

df %>%
  inner_join(activefilter)
Joining, by = "name"
# A tibble: 2 x 3
  name  band    plays 
  <chr> <chr>   <chr> 
1 John  Beatles guitar
2 Paul  Beatles bass  

df %>%
  semi_join(activefilter)
Joining, by = "name"
# A tibble: 2 x 2
  name  band   
  <chr> <chr>  
1 John  Beatles
2 Paul  Beatles

【讨论】:

  • 非常感谢您的解释,不胜感激!
  • 我认为semi_join 是这种情况下的正确答案。 semi_join 子集 df 到具有 12 行的数据框中,其中仅包含与 activefilter 数据集中匹配的行。如果您使用inner_join 而不是df 以14 行结尾。这两行是来自activefilter 数据集的重复行,因为inner_join 将所有匹配的行放在一起。
  • @TarJae 我无法重现您刚才所说的内容,我尝试使用您提供的数据框进行半连接和内连接,在这两种情况下我都得到了 12 行。 finaldf % semi_join(acitvefilter, by = "PatientID") finaldf2 % semi_join(acitvefilter, by = "PatientID")
【解决方案3】:

这是semi_join 的场景。它返回 x 中与 y 匹配的所有行。

finaldf <- df %>% 
  semi_join(activefilter, by = c("PatientID", "timepoint") 
finaldf

数据:

df <- structure(list(Samplenr = c("AA35456", "AA35456", "AA35456", 
"AA35456", "AA35456", "AA35444", "AA35444", "AA35444", "AA35444", 
"AA35444", "AA35444", "AA35442", "AA35444", "AA35442", "AA35444", 
"AA35442", "AA35444", "AA35442"), smooth = c(24.7361, 107.358, 
72.0639, 43.8766, 2382.87, 49.6488, 72.0639, 43.8766, 72.0639, 
43.8766, 72.0639, 43.8766, 72.0639, 43.8766, 72.0639, 43.8766, 
72.0639, 43.8766), channel = c("fam", "fam", "fam", "vic", "vic", 
"vic", "fam", "vic", "fam", "vic", "fam", "vic", "fam", "vic", 
"fam", "vic", "fam", "vic"), timepoint = c(1, 1, 1, 1, 1, 1, 
3, 3, 3, 3, 3, 3, 6, 6, 6, 6, 6, 6), PatientID = c(121, 121, 
121, 121, 121, 121, 121, 121, 121, 121, 123, 123, 126, 126, 126, 
126, 126, 126)), row.names = c(NA, -18L), class = c("tbl_df", 
"tbl", "data.frame"))
activefilter <- structure(list(timepoint = c(3, 6, 3, 6), PatientID = c(121, 
124, 123, 123)), row.names = c(NA, -4L), class = c("tbl_df", 
"tbl", "data.frame"))

【讨论】:

  • 这也可以,但我设法用 innerjoin 解决了它。我仍然很困惑为什么过滤器没有给出预期的结果
  • 不应该 by = c("PatientID", "timepoint") 的连接在这种情况下 semi_join 和 inner_join 都有效吗? OP 正在尝试匹配这两个变量。
猜你喜欢
  • 1970-01-01
  • 2019-05-20
  • 1970-01-01
  • 1970-01-01
  • 2019-11-05
  • 2021-03-22
  • 2017-05-06
  • 1970-01-01
  • 2019-04-04
相关资源
最近更新 更多