首页 文章

以离散属性值为条件对观察进行分组

提问于
浏览
1

每个观察我有3个离散属性 . 如果对于2个观察值,这些属性中至少有2个采用相同的值,那么我想将它们组合在一起(实际上总是最多2个属性是相同的) .

我的想法是 Build 一个分组矩阵 . 每行和每列代表一个观察 . 行和列的交叉表示两个观测值的"similarity",如果至少两个属性值相同,则应采用值 TRUE ,否则为 FALSE .

这是我所做的可重复的示例( abc 是要比较的属性):

df <- data.frame(a = c(1, 1, 2, 2, 3), 
                 b = c(1, 2, 3, 2, 4), 
                 c =c("a", "a", "d", "a", "c"))

grouping_matrix <- matrix(nrow = nrow(df), ncol = nrow(df))

for (i in 1:nrow(df)){
  for (j in 1:nrow(df)){
    if(sum(df[i, ] == df[j, ]) >= 2) {
      grouping_matrix [i, j] <- TRUE
    } else {
      grouping_matrix [i, j]  <- FALSE  
    }
  }
}

> df
  a b c
1 1 1 a
2 1 2 a
3 2 3 d
4 2 2 a
5 3 4 c
> grouping_matrix 
      [,1]  [,2]  [,3]  [,4]  [,5]
[1,]  TRUE  TRUE FALSE FALSE FALSE
[2,]  TRUE  TRUE FALSE  TRUE FALSE
[3,] FALSE FALSE  TRUE FALSE FALSE
[4,] FALSE  TRUE FALSE  TRUE FALSE
[5,] FALSE FALSE FALSE FALSE  TRUE

这很有效 . 然而,即使对于一些thousend观察,它也需要永远 . 我很确定有一些更有效的方式,例如一些 data.table 魔法 . 如果问题没有明确说明,请告诉我 .

3 回答

  • 1

    如果您遇到性能问题 avoid matrixes . 任何需要距离矩阵的东西都需要至少O(n²)的时间,并且需要永远 . 在R中,因为R解释器非常慢,所以避免任何for循环 . 像所有快速R模块一样,用C或Fortran重写代码 . 纯R是sloooow .

    但是你的方法有一个更普遍的问题 . 如果a和b应该分组,b和c应该分组,但a和c太不同了怎么办?例如

    a X Y U
    b X Y Z
    c V Y Z
    

    a和b共有X Y,b和c共有Y和Z,但a和c只有Y.

    如果可以对a和c进行分组,那么您正在寻找 transitive closure . disjoint set structure 可以帮助加速这种计算 .

  • 1

    没有双 for 循环,这样做也是如此 . 我会做一个快速的性能测试来检查它是否相当快 .

    grouping_matrix <- do.call(rbind, lapply(1:nrow(df), function(x) rowSums(df[rep(x, nrow(df)),] == df) >= 2))
    
             1   1.1   1.2   1.3   1.4
    [1,]  TRUE  TRUE FALSE FALSE FALSE
    [2,]  TRUE  TRUE FALSE  TRUE FALSE
    [3,] FALSE FALSE  TRUE FALSE FALSE
    [4,] FALSE  TRUE FALSE  TRUE FALSE
    [5,] FALSE FALSE FALSE FALSE  TRUE
    
  • 1

    或者,这可以通过重塑和自联接来解决 . 此方法不会将每一行相互比较(如嵌套的 for 循环或嵌套的 lapply() 方法),而只是查找列名和值中的匹配项 .

    library(data.table)
    mDT <- setDT(df)[, rn := .I][, melt(.SD, id.vars = "rn")]
    mDT[mDT, on = .(variable, value), allow = TRUE, nomatch = 0L][
      , .N >= 2L, by = .(rn, i.rn)][
        , dcast(.SD, rn ~ i.rn, fill = FALSE)]
    

    1 2 3 4 5
    1:1是,否则返回FALSE FALSE
    2:2是,否则为假
    3:3错误,错误,错误
    4:4否则为真假是真的
    5:5否错FALSE FALSE

    解释

    首先,通过将所有列从宽格式转换为长格式(在添加行id rn 之后)创建临时data.table mDT .

    mDT
    

    变量值
    1:1 a 1
    2:2 a 1
    3:3 a 2
    4:4 a 2
    5:5 a 3
    6:1 b 1
    7:2 b 2
    8:3 b 3
    9:4 b 2
    10:5 b 4
    11:1 c a
    12:2 c a
    13:3 c d
    14:4 c a
    15:5 c c

    然后, mDT 与列的名称和值自身连接:

    mDT[mDT, on = .(variable, value), allow = TRUE, nomatch = 0L]
    

    变量值i.rn
    1:1 a 1 1
    2:2 a 1 1
    3:1 a 1 2
    4:2 a 1 2
    5:3 a 2 3
    6:4 a 2 3
    7:3 a 2 4
    8:4 a 2 4
    9:5 a 3 5
    10:1 b 1 1
    11:2 b 2 2
    12:4 b 2 2
    13:3 b 3 3
    14:2 b 2 4
    15:4 b 2 4
    16:5 b 4 5
    17:1 c a 1
    18:2 c a 1
    19:4 c a 1
    20:1 c a 2
    21:2 c a 2
    22:4 c a 2
    23:3 c d 3
    24:1 c a 4
    25:2 c a 4
    26:4 c a 4
    27:5 c c 5
    变量值i.rn

    参数 nomatch = 0L 试图减少结果行数,因为OP已报告其 生产环境 数据集存在问题 .

    现在,对于 rni.rn 的每个组合计算匹配数,并检查该数字是否大于1:

    mDT[mDT, on = .(variable, value), allow = TRUE][
      , .N >= 2L, by = .(rn, i.rn)]
    

    我是V1
    1:1 1是的
    2:2 1是的
    3:1 2是的
    4:2 2是的
    5:3 3是的
    6:4 3错误
    7:3 4假
    8:4 4是的
    9:5 5是的
    10:4 2是的
    11:2 4是的
    12:4 1假
    13:1 4假

    最后,根据要求将该结果重新整形为类似矩阵的结构 .

相关问题