基于条件在R中筛选子集列表

3

I have a data frame which looks like:

df = read.table(text="S00001    S00002  S00003  S00004  S00005  S00006  
GG  AA  GG  AA  GG  AG  
CC  TT  TT  TC  TC  TT  
TT  CC  CC  TT  TT  TT  
AA  AA  GG  AA  AG  AA  
TT  CC  CC  TT  TC  TT  
GG  GG  GG  AA  GG  GG", header=T, stringsAsFactors=F)

我想要统计每一行中具有相同字母的字符数量(例如:"AA"、"CC"、"GG"或"TT")。我的做法是使用table()函数来计算所有元素,并根据列表名称是否为"homo"生成另一个列表。我尝试对这些列表进行子集操作,但未成功。以下是我的脚本:

A <- apply(df,1, function(x) table(x))
B <- apply(df,1, function(x) (names(table(x)) %in% c("AA","CC","GG","TT")))
A[B] ## this didn't work

我希望会生成一个数据框架:

我期待会生成一个数据框架:

2 3
1 3
2 4
4 1
2 3
1 5

非常感谢任何帮助。


你是否总是事先知道 c("AA","CC","GG","TT") 是你唯一的双字母选项? - David Arenburg
3个回答

4
尝试使用mapply。它会按顺序对列表中的每个元素进行评估。标题名称是自动生成的,您可以根据需要更改它们:
t(mapply('[', A, B))
     AA GG
[1,]  2  3
[2,]  1  3
[3,]  2  4
[4,]  4  1
[5,]  2  3
[6,]  1  5

正如CathG所提到的,您可以通过以下方式避免计算B

t(sapply(A, function(x){x[grepl("([A-Z])\\1", names(x))]}))

t(sapply(A, function(x){x[names(x) %in% c("AA","CC","GG","TT")]})) 将会得到相同的结果,而无需计算 B - Cath
@CathG 我注意到了。我添加了这个方法来展示 OP 如何在两个列表上使用一个函数。 - Pierre L
t(sapply(A, function(x){x[grepl("([A-Z])\\1", names(x))]})) - Cath
1
我将为后人添加 :) - Pierre L
@CathG 这是个不错的。 - Pierre L

3
我不喜欢apply,因为矩阵转换,特别是apply(df, 1,...)需要按行操作。
相反,我建议使用辅助函数,结合sapplyrowSums(将在sapply矩阵输出上操作)。
f <- function(x, y) rowSums(sapply(x, `%in%`, y))

那么您可以这样做(不需要计算 AB
cbind(f(df, c("AA", "CC")), 
      f(df, c("GG", "TT")))
#      [,1] [,2]
# [1,]    2    3
# [2,]    1    3
# [3,]    2    4
# [4,]    4    1
# [5,]    2    3
# [6,]    1    5

或者(取决于你想要什么)
f(df, c("AA", "CC", "GG", "TT"))
# [1] 5 4 6 5 5 6

我的真实数据不仅仅是每行的("A", "G")或者("C", "T")组合。它们可以是四个字母中的任意两个组合,格式为两个唯一的双字母,也可能是一个混合字母。例如,在某些行中是("AA", "CC"和/或"AC")。因此,你的脚本输出结果对于("A","C")或者("G","T")并不是我期望的。 - user3354212
3
这个解决方案将根据您的问题描述和您尝试的解决方案正确运行,例如尝试 f(df, "TC")。如果您有一些特殊情况没有提到,并且正在寻找一个正则表达式的解决方案,您应该在说明中提到并展示这些情况的期望结果。请不要因为您未能提供MWE而对我进行负面评价,毕竟我是免费帮助您的。 - David Arenburg
抱歉我的评论让你不高兴了。你的脚本在这个例子中运行得很好。非常抱歉我没有在示例中包含我所有可能的数据组合。如果我的 "df" 是这样的:df = read.table(text="AA AA CC AA AC AA
TT GG GG TT TG TT", header=F, stringsAsFactors=F),期望的结果是 (4,1), (3,3)。谢谢你的时间。
- user3354212
1
你有一个只有一行的数据集吗? - David Arenburg
"AA AA CC AA AC AA" "TT GG GG TT TG TT" - user3354212
然后你可以执行 cbind(f(df, c("AA", "GG")), f(df, c("CC", "TT"))),尽管我不太明白你所需的两列输出并不是很有意义,因为你并不真正知道每个值的含义。 - David Arenburg

3
我们可以通过单个apply来实现这一点。
t(apply(df, 1, function(x) {tbl <- table(x)
        tbl[names(tbl) %in% c("AA", "CC", "GG", "TT")]}))
#      [,1] [,2]
#[1,]    2    3
#[2,]    1    3
#[3,]    2    4
#[4,]    4    1
#[5,]    2    3
#[6,]    1    5

我遇到了真实数据的问题。我想在原始数据框中添加两列,以保存您脚本的输出结果。我使用了 df[c("A","B")] <- t(apply(df, 1, function(x) {tbl <- table(x) tbl[names(tbl) %in% c("AA", "CC", "GG", "TT")]})) 但是出现了错误 "Error in [<-.data.frame(*tmp*, c("A", "B"), value = list(c(8L, 635L : replacement element 1 has 2 rows, need 705"。我的数据结构与此处示例相同。当我刚刚检查了您的脚本输出时,结果看起来像:' X7 X9 X11
[1,] Integer,2 Integer,2 Integer,2'。
- user3354212
一个可能的原因是在一个特定行中可能没有 "AA"(或 "CC"、"GG"、"TT"),而在另一行中存在,从而导致输出元素数量不平衡。我们可以使用 v1 <- c("AA", "CC", "GG", "TT"); t(apply(df, 1, function(x) {table(factor(x[x%in%v1], levels=v1))})) - akrun
1
我找到了问题所在。我的真实数据中有几行只有一个双字母(即只有一个元素,而不是两个不同的双字母元素)。因此,我必须首先通过 lapply(B, function(x) length(x) > 1) 删除这些行,然后再应用您的脚本。那应该可以解决问题。 - user3354212

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接