【问题标题】:Can AWK array be used to get largest cluster in correlation matrix?可以使用 AWK 数组来获得相关矩阵中的最大簇吗?
【发布时间】:2021-12-23 16:00:42
【问题描述】:

我有一个描述项目 A-K 之间相关性的矩阵,其中 1 = 相关,0 = 不相关。

有没有一种简单的方法可以从数据中提取最大的集群?换句话说,具有最相关元素的集群。以下是一些示例数据:

#  A B C D E F G H I J K
A  1 1 1 1 1 1 1 1 1 1 1
B  1 1 1 1 1 1 1 1 1 1 1
C  1 1 1 1 1 1 1 1 1 1 1
D  1 1 1 1 0 1 0 0 0 1 1
E  1 1 1 0 1 0 0 1 1 0 0
F  1 1 1 1 0 1 0 0 1 0 1
G  1 1 1 0 0 0 1 0 0 0 0
H  1 1 1 0 1 0 0 1 1 1 1
I  1 1 1 0 1 1 0 1 1 0 0
J  1 1 1 1 0 0 0 1 0 1 0
K  1 1 1 1 0 1 0 1 0 0 1

通过眼睛交换几列/行,预期结果将是矩阵的左上角,这是一个大小为 6 的集群,包含:{A, B, C, D, F, K}

我知道 awk 对这个应用程序来说并不是最友好的,但我热衷于使用 awk,因为它将集成到更大的 awk 脚本中。话虽如此,我对这门语言并不是完全不动摇。

不知道从哪里开始,但这是我在 python 中所想的更复杂的版本: https://stats.stackexchange.com/questions/138325/clustering-a-correlation-matrix

【问题讨论】:

  • 我确信它可以完成,但 Awk 对于这个特定任务来说是一个尴尬的工具选择。您搜索过什么,尝试过什么,以及为什么您对 Awk 解决方案特别感兴趣?
  • 不知道你所说的重叠是什么意思;你能链接到相关的解释吗?你的意思是那些零个数最少的那些?
  • 感谢@tripleee 的回复!我已编辑问题以提供更多详细信息。
  • 你能详细说明一下行/列交换规则吗?我仍然不明白为什么 D row/col 可以连续 6 个 1s 和 H row/col 不能。
  • 感谢@tshiono,我目前正在任意进行行/列交换。在这种情况下,{ABCHEI} 是另一组 6 是正确的。最终,我可能会为每个元素使用实数(而不仅仅是 0 和 1),并且在具有相同数量的多个集群的情况下成员,其元素总和最小的集群将是最佳集群。

标签: matrix awk


【解决方案1】:

假设:

  • 所有矩阵都是symmetric(即正方形;等于它的转置;matrix[x,y]=matrix[y,x])
  • matrix[x,x]=1 为所有 x
  • 所有矩阵条目都是0 或1
  • 对单元素集群不感兴趣
  • 对同一簇的排列不感兴趣(即A,B 与B,A 相同)
  • 因为我们不必担心排列,所以我们可以专注于按照它们在矩阵中出现的顺序处理元素(例如,我们处理 A,B,C 并忽略 A,C,B、B,A,C 的等价物, B,C,A, C,A,B 和 C,B,A);这使我们能够专注于处理矩阵的上/右半部分(在恒等/对角线上方),并按从左到右的顺序;这将大大减少我们需要评估的排列数量
  • 如问题所示,组成一个簇的元素可以在矩阵中向上/向左移动,以便用1's 填充矩阵的顶部/左侧(这在处理过程中发挥作用,对于每个新的元素,我们只需要测试添加到矩阵顶部/左侧部分的新列/行的等效项)

关于最后一个假设......假设我们有集群A,D,我们现在要测试A,D,F;我们只需要测试新的列/行条目 (?):

Current Cluster     New Cluster ?

  A D                 A D F
A 1 1               A 1 1 ?     # if matrix is symmetric then only need to test
D 1 1               D 1 1 ?     # the new column *OR* the new row, not both;
                    F ? ? 1     # bottom/right == 1 == matrix[F,F] per earlier assumption

一个使用递归函数和两个GNU awk's 功能的想法:a) 数组数组(又称多维数组)和 b) PROCINFO["sorted_in"]将集群自定义排序到标准输出:

awk '

######
# load matrix into memory

FNR==1 { n=(NF-1)                                            # number of elements to be processed
         for (i=2;i<=NF;i++)
             label[i-1]=$i                                   # save labels
         next
       }
       { for (i=2;i<=NF;i++)
             m[FNR-1][i-1]=$i                                # populate matrix array m[row#][column#]
       }

######
# define our recursive function

function find_cluster(cluster, i, clstrcount, stackseq,    j, k, corrcount) {

    # cluster       : current working cluster (eg, "A,B,C")
    # i             : index of latest element (eg, for "A,B,C" => latest element is "C" so i = 3
    # clstrcount    : number of elements in current cluster
    # stackseq      : sequence number of stack[] array
    #               : stack[] contains list of indexes for current cluster (for "A,B,C" stack = "1,2,3")
    # j,k,corrcount : declaring additional variables as "local" to this invocation of the function

    clstrcount++                                             # number of elements to be processed at this call/level

    for (j=i+1;j<=n;j++) {                                   # process all elements/indexes greater than i

        corrcount=1                                          # reset correlation count; always start with 1 since m[j][j]=1

        # check the new column/row added to the top/left of the matrix to see if it extends the current cluster (ie, all entries are "1")

        for (k in stack) {                                   # loop through element/indexes in stack
            if (m[stack[k]][j])                              # check column entries
               corrcount++
            if (m[j][stack[k]])                              # check row entries; not necessary if matrix is symmetric but we will add it here to show the m[][] references
               corrcount++
        }

        if (corrcount == (stackseq*2 +1) ) {                 # if we have all "1"s we have a new cluster of size clstrcount

           stack[++stackseq]=j                               # "push" current element/index on stack; increment stack seq/index
           cluster=cluster "," label[j]                      # add current element/label to cluster

           max= (clstrcount>max) ? clstrcount : max          # new max(cluster count) ?
           clusters[clstrcount][++clsterseq]=cluster         # add new cluster to our master list: clusters[cluster_count][seq]

           find_cluster(cluster, j, clstrcount, stackseq)    # recursive call to check for next element(s)

           delete stack[stackseq--]                          # back from recursive call so "pop" curent element (j) from stack
           gsub(/[,][^,]+$/,"",cluster)                      # remove current element/label from cluster to make way for next element/label to be tested
        }
    }
}

######
# start looking for clusters of size 2+

END    { max=2                                               # not interested in clusters of "1"
         for (i=1;i<n;i++) {                                 # loop through list of elements
             clstrcount=1                                    # init cluster count = 1
             clstrseq=0                                      # init clusters[...][seq] sequence seed
             cluster=label[i]                                # reset cluster to current element/label
             stackseq=1                                      # reset stack[seq] sequence seed
             stack[stackseq]=i                               # "push" current element on stack

             find_cluster(cluster, i, clstrcount, stackseq)  # start recursive calls looking for next element in cluster
         }

######
# for now just display clusters with size > 2; adjust this next line to add/remove cluster sizes from stdout

         if (max>2)                                          # print list of clusters with length > 2
            for (i=max;i>2;i--) {                            # print from largest to smallest and ...
                PROCINFO["sorted_in"]="@val_str_asc"         # in alphabetical order
                printf "####### clusters of size %s:\n", i
                for (j in clusters[i])                       # loop through all entries for clusters of size "i"
                    print clusters[i][j]
            }
       }
' matrix.dat

注意:当前版本(诚然)有点冗长......我正在研究细节时记下第一次解决方案的结果;通过进一步分析,可以减少代码;话虽如此,在这个 11 元素矩阵中找到所有 2+ 大小的簇所花费的时间并不算太糟糕:

real    0m0.084s
user    0m0.031s
sys     0m0.046s

这会生成:

####### clusters of size 6:
A,B,C,D,F,K
A,B,C,E,H,I
####### clusters of size 5:
A,B,C,D,F
A,B,C,D,J
A,B,C,D,K
A,B,C,E,H
A,B,C,E,I
A,B,C,F,I
A,B,C,F,K
A,B,C,H,I
A,B,C,H,J
A,B,C,H,K
A,B,D,F,K
A,B,E,H,I
A,C,D,F,K
A,C,E,H,I
B,C,D,F,K
B,C,E,H,I
####### clusters of size 4:
A,B,C,D
A,B,C,E
A,B,C,F
A,B,C,G
A,B,C,H
A,B,C,I
A,B,C,J
A,B,C,K
A,B,D,F
A,B,D,J
A,B,D,K
A,B,E,H
A,B,E,I
A,B,F,I
A,B,F,K
A,B,H,I
A,B,H,J
A,B,H,K
A,C,D,F
A,C,D,J
A,C,D,K
A,C,E,H
A,C,E,I
A,C,F,I
A,C,F,K
A,C,H,I
A,C,H,J
A,C,H,K
A,D,F,K
A,E,H,I
B,C,D,F
B,C,D,J
B,C,D,K
B,C,E,H
B,C,E,I
B,C,F,I
B,C,F,K
B,C,H,I
B,C,H,J
B,C,H,K
B,D,F,K
B,E,H,I
C,D,F,K
C,E,H,I
####### clusters of size 3:
A,B,C
A,B,D
A,B,E
A,B,F
A,B,G
A,B,H
A,B,I
A,B,J
A,B,K
A,C,D
A,C,E
A,C,F
A,C,G
A,C,H
A,C,I
A,C,J
A,C,K
A,D,F
A,D,J
A,D,K
A,E,H
A,E,I
A,F,I
A,F,K
A,H,I
A,H,J
A,H,K
B,C,D
B,C,E
B,C,F
B,C,G
B,C,H
B,C,I
B,C,J
B,C,K
B,D,F
B,D,J
B,D,K
B,E,H
B,E,I
B,F,I
B,F,K
B,H,I
B,H,J
B,H,K
C,D,F
C,D,J
C,D,K
C,E,H
C,E,I
C,F,I
C,F,K
C,H,I
C,H,J
C,H,K
D,F,K
E,H,I

【讨论】:

  • 这真是太棒了@markp-fuso!效果很好!非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-16
  • 2019-04-22
  • 2011-02-08
  • 2010-10-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多