【问题标题】:R - A loop comparing elements in common between two hierarchical listsR - 一个循环比较两个分层列表之间的共同元素
【发布时间】:2019-05-08 11:31:02
【问题描述】:

一段时间以来,我一直在尝试构建一个矩阵,该矩阵由两个分层列表之间的共同元素计数填充。

这是一些虚拟数据:

site<-c('A','A','A','A','A','A','A','A','A','B','B','B','B','B','B')
group<-c('A1','A1','A2','A2','A2','A3','A3','A3','A3', 
'B1','B1','B2','B2','B2','B2')
element<-c("red","orange","blue","black","white", "black","cream","yellow","purple","red","orange","blue","white","gray","salmon")
d<-cbind(site,group,element)

我创建了一个列表结构,假设由于每个列表中的 os 元素数量不同,它会是程序性的。另外,因为我不希望在组之间进行所有可能的比较,而只希望在站点之间进行比较。

#first level list - by site
sitelist<-split(nodmod, list(nodmod$site),drop = TRUE)
#list by group 
nestedlist <- lapply(sitelist, function(x) split(x, x[['mod']], drop = TRUE))

我的目的是创建一个表格或矩阵,其中包含两个站点的组之间共有的元素数量(我的原始数据有其他站点)。像这样:

    A1  A2  A3
B1  2   0   0
B2  0   2   0

这个问题的嵌套性质对我来说是一个挑战。我对列表不太熟悉,因为我主要使用数据框解决了问题。我的尝试归结为这一点。我觉得它已经接近了,但是对于循环的正确语法有很多缺点。

t <- outer(1:length(d$A),
         1:length(d$B),
         FUN=function(i,j){
           sapply(1:length(i),
                  FUN=function(x) 
                    length(intersect(d$A[[i]]$element, d$B[[j]]$element)) )
         })

任何帮助将不胜感激。如果解决了类似的问题,我们深表歉意。我已经搜索了互联网,但没有找到它,或者没有理解将其转移到我的解决方案。

【问题讨论】:

  • 第 10 行有 site = Agroup = B1。对吗?
  • 我的错误,现在正在编辑。
  • 你想要做的是一个共现矩阵。您可以从 data.frame 结构开始,使用data.frame() 代替cbind(),然后使用crossprod(table(d[,c(3,2)]))。看看这个question

标签: r list loops sapply


【解决方案1】:

考虑矩阵乘法x %*% y(参见?matmult),通过在每个对应单元格中分配一个唯一的值创建一个唯一的元素值的辅助矩阵。然后将矩阵乘法作为自身的转置运行,然后是行和列的子集:

# EMPTY MATRIX
helper_mat <- matrix(0, nrow=length(unique(element)), ncol=length(unique(group)),
                     dimnames=list(unique(element), unique(group)))

# ASSIGN 1's AT SELECT LOCATIONS
for(i in seq_along(site)) {
  helper_mat[element[i], group[i]] <- 1
}

helper_mat
#        A1 A2 A3 B1 B2
# red     1  0  0  1  0
# orange  1  0  0  1  0
# blue    0  1  0  0  1
# black   0  1  1  0  0
# white   0  1  0  0  1
# cream   0  0  1  0  0
# yellow  0  0  1  0  0
# purple  0  0  1  0  0
# gray    0  0  0  0  1
# salmon  0  0  0  0  1

# MATRIX MULTIPLICATION WITH SUBSET
final_mat <- t(helper_mat) %*% helper_mat
final_mat <- final_mat[grep("B", rownames(final_mat)), grep("A", colnames(final_mat))]

final_mat
#    A1 A2 A3
# B1  2  0  0
# B2  0  2  0

感谢@Lamia,使版本更短:

helper_mat <- table(element, group)

final_mat <- t(helper_mat) %*% helper_mat # ALTERNATIVELY: crossprod(helper_mat)

final_mat <- final_mat[grep("B", rownames(final_mat)), grep("A", colnames(final_mat))]

final_mat
#      group
# group A1 A2 A3
#    B1  2  0  0
#    B2  0  2  0

【讨论】:

  • 这可以使用crossprod(table(d$element,d$group))进行简化
  • 绝对是@Lamia,table 是我唯一需要的,crossprod matmult 的变体。
  • 这很棒。谢谢你。如何调整此代码以创建输出作为每个交叉点的元素列表?比如B1/A1和B2/A2中包含的颜色?
  • 该请求需要提出一个新问题,因为您正在大幅更改所需的输出。
【解决方案2】:

@Parfait 使用矩阵乘法的类似方法。您可能需要尝试数据生成以将其扩展到您的应用程序:

site<-c('A','A','A','A','A','A','A','A','A','B','B','B','B','B','B')
group<-c('A1','A1','A2','A2','A2','A3','A3','A3','A3', 
         'B1','B1','B2','B2','B2','B2')
element<-c("red","orange","blue","black","white", "black","cream","yellow","purple","red","orange","blue","white","gray","salmon")

d<-data.frame(group, el = as.factor(element), stringsAsFactors = FALSE)


As <- d[group %in% paste0("A", 1:3), ]
Bs <- d[group %in% paste0("B", 1:2), ]

A_mat <- as.matrix(table(As))
B_mat <- as.matrix(table(Bs))

结果:

> A_mat
         el
group black blue cream gray orange purple red salmon white yellow
   A1     0    0     0    0      1      0   1      0     0      0
   A2     1    1     0    0      0      0   0      0     1      0
   A3     1    0     1    0      0      1   0      0     0      1


> B_mat
         el
group black blue cream gray orange purple red salmon white yellow
   B1     0    0     0    0      1      0   1      0     0      0
   B2     0    1     0    1      0      0   0      1     1      0


> B_mat %*% t(A_mat)
     group
group A1 A2 A3
   B1  2  0  0
   B2  0  2  0

【讨论】:

    【解决方案3】:
    # example dataset
    site<-c('A','A','A','A','A','A','A','A','A','B','B','B','B','B','B')
    group<-c('A1','A1','A2','A2','A2','A3','A3','A3','A3', 
             'B1','B1','B2','B2','B2','B2')
    element<-c("red","orange","blue","black","white", "black","cream","yellow","purple","red","orange","blue","white","gray","salmon")
    d<-cbind(site,group,element)
    
    library(tidyverse)
    
    # save as dataframe
    d = data.frame(d)
    
    expand.grid(groupA = unique(d$group[d$site=="A"]),
                groupB = unique(d$group[d$site=="B"])) %>%               # get all combinations of A and B columns
      rowwise() %>%                                                      # for each row
      mutate(counts = length(intersect(d$element[d$group==groupA], 
                                       d$element[d$group==groupB]))) %>% # count common elements
      spread(groupA, counts) %>%                                         # reshape data
      data.frame() %>%                                                   
      column_to_rownames("groupB")
    
    #    A1 A2 A3
    # B1  2  0  0
    # B2  0  2  0
    

    您可以使用将(自动)应用于每一行的矢量化函数来代替rowwise,如下所示:

    # create a function and vectorise it
    CountCommonElements = function(x, y) length(intersect(d$element[d$group==x], d$element[d$group==y]))
    CountCommonElements = Vectorize(CountCommonElements)
    
    expand.grid(groupA = unique(d$group[d$site=="A"]),
                groupB = unique(d$group[d$site=="B"])) %>%                                                              
      mutate(counts = CountCommonElements(groupA, groupB)) %>% 
      spread(groupA, counts) %>%                                       
      data.frame() %>%                                                   
      column_to_rownames("groupB")
    
    #    A1 A2 A3
    # B1  2  0  0
    # B2  0  2  0
    

    【讨论】:

    • 不幸的是,这个包存在一些问题:'mutate_impl(.data, dots) 中的错误:评估错误:as_dictionary() 自 rlang 0.3.0 起已失效。请改用as_data_pronoun()。它似乎与 mutate 函数有关,针对这个问题提出的解决方案超出了我的 R 技能。
    • 可能是您正在使用的软件包或 R 版本。你知道它们是否是最新版本吗?
    • 它是 R-3.4.0。这似乎是与 Windows 版本相关的问题。现在正在更新,但我听说有人试图降级。
    • 我已经安装了最新版本,它工作正常。谢谢!
    • 顺便说一句,我仍然很好奇如何使用 base R 来解决这个问题,但是对于我目前所需要的,这绝对可以!
    猜你喜欢
    • 2011-02-21
    • 1970-01-01
    • 2019-05-08
    • 1970-01-01
    • 1970-01-01
    • 2021-01-01
    • 1970-01-01
    • 2019-05-05
    • 2015-12-16
    相关资源
    最近更新 更多