理解`Reduce`函数

39

我对R语言中Reduce函数有疑问。我已经阅读了它的文档,但仍然有些困惑。所以,我有5个基因名称的向量。例如:

v1 <- c("geneA","geneB",""...)
v2 <- c("geneA","geneC",""...)
v3 <- c("geneD","geneE",""...)
v4 <- c("geneA","geneE",""...)
v5 <- c("geneB","geneC",""...)

我想找出至少出现在两个向量中的基因。有些人建议:

Reduce(intersect,list(a,b,c,d,e))

如果有人能够解释一下这个语句是如何工作的,我将不胜感激,因为我已经见过Reduce在其他场景中使用。


3
您的问题是否真的是“如何找到至少在两个向量中存在哪些基因/元素?”如果是,那么 Reduce() 不会有用,虽然它可以很容易地回答“哪些基因存在于 所有 向量中?” - Josh O'Brien
3个回答

41

Reduce接受一个二元函数和一组数据项,并以递归方式连续将函数应用于列表元素。例如:

Reduce(intersect,list(a,b,c))

就是

intersect((intersect(a,b),c)

然而,我认为这种结构在这里并不能帮助您,因为它只会返回所有向量共有的元素。

要计算基因出现在多少个向量中,您可以执行以下操作:

vlist <- list(v1,v2,v3,v4,v5)
addmargins(table(gene=unlist(vlist), vec=rep(paste0("v",1:5),times=sapply(vlist,length))),2,list(Count=function(x) sum(x[x>0])))
       vec
gene    v1 v2 v3 v4 v5 Count
  geneA  1  1  0  1  0     3
  geneB  1  0  0  0  1     2
  geneC  0  1  0  0  1     2
  geneD  0  0  1  0  0     1
  geneE  0  0  1  1  0     2

非常感谢您的输入。我以前从未使用过table和addmargins函数。如果您不介意,我想问一下它们的用法。 - Johnathan
表格:所以gene是可以用作因子(即分类数据)的对象,vec是维度的名称(即“v1”,“v2”),对吗?我对times的含义感到困惑。它返回长度向量。至于addmargins,它是一个函数,用于扩展表格以添加边际总计(即感兴趣的类别中案例的总计数),对吗? “2”表示添加一个列,该列将保存行边际总计,对吗?最后,最后一个参数是包含函数的列表。感谢您的时间和帮助! - Johnathan
1
@Johnathan 是的,你说得对。times是传递给rep的参数,它确定每个元素被重复的次数 - 这是为了确保基因映射到正确的变量上。 - James
谢谢您的输入。我已经阅读了R的文档。请重复x中的值(例如v1...)。我仍然对长度感到困惑。显然,它是一个向量,如果长度为length(x),则给出重复每个元素的次数。例如,如果v2的长度为2,那么每个元素不应该重复两次吗?我不明白这如何确保将基因映射到正确的变量。对于混淆,我很抱歉。我相信这是一些显而易见的东西。谢谢! - Johnathan
参数类型颠倒的情况非常奇怪。如果它与约定“apply(list, function)”保持一致,会感觉更好。 - Vlady Veselinov

29

一个很好的方式来看到Reduce()在做什么是运行它时使用参数accumulate=TRUE。当accumulate=TRUE时,它会返回一个向量或列表,其中每个元素显示处理列表x中前n个元素后的状态。以下是一些示例:

Reduce(`*`, x=list(5,4,3,2), accumulate=TRUE)
# [1]   5  20  60 120

i2 <- seq(0,100,by=2)
i3 <- seq(0,100,by=3)
i5 <- seq(0,100,by=5)
Reduce(intersect, x=list(i2,i3,i5), accumulate=TRUE)
# [[1]]
#  [1]   0   2   4   6   8  10  12  14  16  18  20  22  24  26  28  30  32  34  36
# [20]  38  40  42  44  46  48  50  52  54  56  58  60  62  64  66  68  70  72  74
# [39]  76  78  80  82  84  86  88  90  92  94  96  98 100
# 
# [[2]]
#  [1]  0  6 12 18 24 30 36 42 48 54 60 66 72 78 84 90 96
# 
# [[3]]
# [1]  0 30 60 90

它能用于大于比较吗?显然,我尝试了一下,得到的是序列中的第一个数字,后面跟着11111或00000。对于类似Reduce('<',c(3,4,7,2,6,8,9), accumulate=T)这样的东西,我的期望值应该是3 3 2 2 2 2。使用Reduce可以实现这个吗?稍微解释一下,我假设Reduce从左边开始每次取两个向量元素。由于我的函数是“小于”,它会比较前两个元素并返回较小的数字,然后将其与第三个元素进行比较并返回较小的数字... - user3507767
只是想澄清一下,我知道我可以使用cummin()来获得我想要的结果,只是在这里尝试理解Reduce()。 - user3507767

8
假设在这个答案的最后给出了输入值,那么下面的表达式
Reduce(intersect,list(a,b,c,d,e))
## character(0)

给出所有向量中存在的基因,而不是至少存在于两个向量中的基因。这意味着:

intersect(intersect(intersect(intersect(a, b), c), d), e)
## character(0)

如果我们想要至少在两个向量中的基因:
L <- list(a, b, c, d, e)
u <- unlist(lapply(L, unique)) # or:  Reduce(c, lapply(L, unique))

tab <- table(u)
names(tab[tab > 1])
## [1] "geneA" "geneB" "geneC" "geneE"

或者

sort(unique(u[duplicated(u)]))
## [1] "geneA" "geneB" "geneC" "geneE"

注意:我们使用了:

a <- c("geneA","geneB")
b <- c("geneA","geneC")
c <- c("geneD","geneE")
d <- c("geneA","geneE")
e <- c("geneB","geneC")

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