【问题标题】:Subset a Data Frame based on Multiple Conditions根据多个条件对数据框进行子集化
【发布时间】:2022-06-18 00:03:20
【问题描述】:

我有以下数据框(我的真实数据框有更多的行和列,但表面上是这样构造的):

Root_R1 = c(1,2,3,4,5)
Root_R2 = c(1,0,3,0,0)
Root_R3 = c(1,0,3,0,0)
Shoot_R1 = c(1,0,3,4,5)
Shoot_R2 = c(0,0,31,4,5)
Shoot_R3 = c(0,0,0,0,0)
data.frame(Root_R1, Root_R2, Root_R3, Shoot_R1, Shoot_R2, Shoot_R3)

Root_R1 Root_R2 Root_R3 Shoot_R1 Shoot_R2 Shoot_R3
1    Root    Root    Root    Shoot    Shoot    Shoot
2       1       1       1        1        0        0
3       2       0       0        0        0        0
4       3       3       3        3       31        0
5       4       0       0        4        4        0
6       5       0       0        5        5        0

我想要做的是过滤此数据框并找到与组织类型(根、芽等)关联的至少两列的值大于 0 的所有行。因此,对于与“Roots”(第 1、2、3 列)关联的列,应返回第 1 - 3 行,而与“Shoots”关联的列将返回第 4 - 6 行。我认为ifelse 代码会起作用,但这似乎效率低下。来自dplyrfilter 会更合适吗?

【问题讨论】:

  • 在向量中混合字符串和数字是个坏主意,它们都会变成字符。
  • 明白。如果我制作了相同的数据框但听取了您的建议(从我的 df 中删除“Root”和“Shoot”并使用“Root_R1”作为列标题,您建议的以下解决方案会改变吗?
  • 是的,它将不再需要第一条指令 (df1 <- df1[-1,])。在df1[]<-lapply 循环中也不强制转换为整数。这个循环不会有什么坏处,但也不需要。
  • 明白了。假设我想更改截止的数值,我会更改这部分代码(sum(x > 0L)),如果我想更改符合截止的行数,我会更改:@987654328 @?
  • 是的,就是这样。或者写一个函数。我将编辑我的答案来预测这些情况。

标签: r if-statement filter subset


【解决方案1】:

这是一个基本的 R 解决方案。 grep 告诉 "Root" 列来自 "Shoot" 列。然后apply 循环返回逻辑(行)索引,which 负责对 data.frame 进行子设置。

Root_R1 = c("Root",1,2,3,4,5)
Root_R2 = c("Root",1,0,3,0,0)
Root_R3 = c("Root",1,0,3,0,0)
Shoot_R1 = c("Shoot",1,0,3,4,5)
Shoot_R2 = c("Shoot",0,0,31,4,5)
Shoot_R3 = c("Shoot",0,0,0,0,0)
df1 <- data.frame(Root_R1, Root_R2, Root_R3, Shoot_R1, Shoot_R2, Shoot_R3)

df1 <- df1[-1,]
df1[] <- lapply(df1, as.integer)

root <- grep("Root", names(df1))
shoot <- grep("Shoot", names(df1))
ok_root <- which(apply(df1[root], 1, \(x) sum(x > 0L) >= 2L))
ok_shoot <- which(apply(df1[shoot], 1, \(x) sum(x > 0L) >= 2L))

df1[ok_root, ]
#>   Root_R1 Root_R2 Root_R3 Shoot_R1 Shoot_R2 Shoot_R3
#> 2       1       1       1        1        0        0
#> 4       3       3       3        3       31        0
df1[ok_shoot, ]
#>   Root_R1 Root_R2 Root_R3 Shoot_R1 Shoot_R2 Shoot_R3
#> 4       3       3       3        3       31        0
#> 5       4       0       0        4        4        0
#> 6       5       0       0        5        5        0

reprex package (v2.0.1) 于 2022-06-09 创建


编辑

关注question in comments

假设我想更改截止的数值,我会更改这部分代码 (sum(x &gt; 0L)),如果我想更改符合截止的行数,我会改变这个:&gt;= 2L?

这是一个解决问题的函数。

special_subset <- function(x, colpattern, cutoff = 0L, numrows = 2L) {
  i_cols <- grep(colpattern, names(x))
  ok <- which(apply(x[i_cols], 1, \(y) sum(y > cutoff) >= numrows))
  x[ok, ]
}

special_subset(df1, "Root")
#>   Root_R1 Root_R2 Root_R3 Shoot_R1 Shoot_R2 Shoot_R3
#> 2       1       1       1        1        0        0
#> 4       3       3       3        3       31        0

special_subset(df1, "Shoot", cutoff = 1)
#>   Root_R1 Root_R2 Root_R3 Shoot_R1 Shoot_R2 Shoot_R3
#> 4       3       3       3        3       31        0
#> 5       4       0       0        4        4        0
#> 6       5       0       0        5        5        0

reprex package (v2.0.1) 于 2022-06-09 创建


编辑 2

要将多个colpattern 传递给函数,请检查其参数长度,如果它大于一个,则将其折叠成具有替代项的模式("|" 元字符)。

special_subset <- function(x, colpattern, cutoff = 0L, numrows = 2L) {
  if(length(colpattern) > 1) {
    colpattern <- paste(colpattern, collapse = "|")
  }
  i_cols <- grep(colpattern, names(x))
  ok <- which(apply(x[i_cols], 1, \(y) sum(y > cutoff) >= numrows))
  x[ok, ]
}

special_subset(df1, c("Root", "Shoot"))
#>   Root_R1 Root_R2 Root_R3 Shoot_R1 Shoot_R2 Shoot_R3
#> 2       1       1       1        1        0        0
#> 4       3       3       3        3       31        0
#> 5       4       0       0        4        4        0
#> 6       5       0       0        5        5        0

tissue_type <- c("Root", "Shoot")
special_subset(df1, tissue_type)
#>   Root_R1 Root_R2 Root_R3 Shoot_R1 Shoot_R2 Shoot_R3
#> 2       1       1       1        1        0        0
#> 4       3       3       3        3       31        0
#> 5       4       0       0        4        4        0
#> 6       5       0       0        5        5        0

reprex package (v2.0.1) 于 2022-06-17 创建

【讨论】:

  • 谢谢。我假设lapply 函数对列表中的每个数据框执行相同的操作,如下所示:ok_list &lt;- which(lapply(list, 1, \(x) sum(x &gt; 0L) &gt;= 2L))?
  • @PatrickThomas 不,lapply 没有 MARGIN 参数,它必须是 apply。试试看,1 报错。
  • 明白。我想在具有多个组的数据框上运行此功能,并且我正在尝试考虑最有效的方法。如果我将我所有的组织类型放在一个标有colpattern 的表中,其中有一列标题为colpattern$tissue_type 并运行以下内容,那么for 循环是否可行:special_subset &lt;- function(x, colpattern, cutoff = 0L, numrows = 2L) { for (i in seq_along(colpattern$tissue_type)) { i_cols &lt;- grep(colpattern, names(x)) ok &lt;- which(apply(x[i_cols], 1, \(y) sum(y &gt; cutoff) &gt;= numrows)) x[ok, ] }
  • 我尝试将我的大 df 的所有类别放在一个名为 tissuetype &lt;- c("Roor", "Shoot") 的向量中,并起草了以下代码以同时在多个列上运行 special_subset &lt;- {function(x,z, cutoff = 0L, numrows = 2L) { for (i in seq_along(z)) { i_cols &lt;- grep(colpattern, names(x)) ok &lt;- which(apply(x[i_cols], 1, \(y) sum(y &gt; cutoff) &gt;= numrows)) x[ok, ]} } } 但收到以下错误:Warning messages: 1: In grep(colpattern, names(x)) : argument 'pattern' has length &gt; 1 and only the first element will be used .
  • @PatrickThomas 在grep(tissue_type, names(x)) 之前尝试tissue_type&lt;- paste(colpattern$tissue_type, collapse="|")
猜你喜欢
  • 1970-01-01
  • 2020-04-15
  • 1970-01-01
  • 2013-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多