计算数据集中的数字序列

时间:2019-01-05 10:59:08

标签: r dataframe matrix

我正在使用调查数据,我想计算一个数字序列在同一位置(所有数字)出现的次数。

作为一个例子,我定义了一个数字序列,依次为2,3或2,3,4,2-。

例如

    v1 v2 v3 v4 v5
    2   3  4  2  6
    3   2  4  6  7     
    2   3  4  2  3

例如,我想接收输出

             position           freq
 (2,3)       (v1, v2)            2
 (3,4)       (v2, v3)            2
 (2,3,4,2)   (v1,v2,v3,v4)       2
 (2,3,4)     (v1, v2, v3)        1
 (2,3,4,2,6) (v1,v2,v3, v4, v5)  1
 (3,4,2)     (v2,v3,v4)          2
 (3,4,2,6)   (v2, v3, v4, v5)    1
 (4,2)       (v3, v4)            2
 (4,2,6)     (v3, v4, v5)        1
 (2,6)       (v4, v5)            1

 and so on

....

我该如何编写函数以返回所需的输出?

非常感谢

1 个答案:

答案 0 :(得分:0)

如果我理解正确,那么问题包括两个部分:

  1. 确定5列v1v5(称为 position )与2、3、4或5个连续的 列的所有组合,即(v1, v2)(v2, v3),...,(v4, v5)(v1, v2, v3),...,(v2, v3, v4, v5)(v1, v2, v3, v4, v5)
  2. 在上述个位置查找值,并计算在不同位置出现的不同值序列的次数。

这可以通过将交叉连接聚合结合在非等价连接中来解决:

library(data.table)
# read data
ds <- fread("
v1 v2 v3 v4 v5
2   3  4  2  6
3   2  4  6  7
2   3  4  2  3
")
library(data.table)
cols <- names(ds)
n <- length(cols)
# create all possible combinations of consecutive positions by
# cross joining of row id x sequence length x start position
vi <- CJ(rn = 1:nrow(ds), len = 2:5, start = 1:n)[
  # compute end positions
  , end := start + len - 1L][
    # remove impossible end positions
    end <= n]
# coerce to data.table, append row id, reshape from wide to long format
dl <- melt(setDT(ds)[, rn := .I], id.vars = "rn", variable.name = "pos", 
           variable.factor = TRUE)[
             # use factor levels =  column number
             , pos := as.integer(pos)][]
# non-equi inner join and aggregate over join parameters 
result <- dl[vi, on = .(rn, pos >= start, pos <= end), 
   .(rn, values = toString(value), position = toString(cols[x.pos])), 
   by = .EACHI, nomatch = 0L][
     # count by values and positions
     , .(freq = .N), by = .(values, position)]
# reorder result
result[order(nchar(values), values)]
           values           position freq
 1:          2, 3             v1, v2    2
 2:          2, 3             v4, a5    1
 3:          2, 4             v2, v3    1
 4:          2, 6             v4, a5    1
 5:          3, 2             v1, v2    1
 6:          3, 4             v2, v3    2
 7:          4, 2             v3, v4    2
 8:          4, 6             v3, v4    1
 9:          6, 7             v4, a5    1
10:       2, 3, 4         v1, v2, v3    2
11:       2, 4, 6         v2, v3, v4    1
12:       3, 2, 4         v1, v2, v3    1
13:       3, 4, 2         v2, v3, v4    2
14:       4, 2, 3         v3, v4, a5    1
15:       4, 2, 6         v3, v4, a5    1
16:       4, 6, 7         v3, v4, a5    1
17:    2, 3, 4, 2     v1, v2, v3, v4    2
18:    2, 4, 6, 7     v2, v3, v4, a5    1
19:    3, 2, 4, 6     v1, v2, v3, v4    1
20:    3, 4, 2, 3     v2, v3, v4, a5    1
21:    3, 4, 2, 6     v2, v3, v4, a5    1
22: 2, 3, 4, 2, 3 v1, v2, v3, v4, a5    1
23: 2, 3, 4, 2, 6 v1, v2, v3, v4, a5    1
24: 3, 2, 4, 6, 7 v1, v2, v3, v4, a5    1
           values           position freq

说明

首先,我们需要确定5个 position v1v5与2、3、4或5个连续的所有可能组合列。

这是通过交叉连接可能长度的向量(即列数)和可能的起始位置的向量来实现的。然后,计算终点位置。不可能的终端位置(即不存在的列)将被删除。

CJ(len = 2:5, start = 1:n)[
  # compute end positions
  , end := start + len - 1L][
    # remove impossible end positions
    end <= n]

    len start end
 1:   2     1   2
 2:   2     2   3
 3:   2     3   4
 4:   2     4   5
 5:   3     1   3
 6:   3     2   4
 7:   3     3   5
 8:   4     1   4
 9:   4     2   5
10:   5     1   5

在随后的联接中,我们需要为ds的每一行查找以上位置。因此,行号可以已经包含在交叉联接中。

vi <- CJ(rn = 1:nrow(ds), len = 2:5, start = 1:n)[
  , end := start + len - 1L][
    end <= n]

然后,ds使用melt()从宽格式改成长格式,结果是

dl
    rn pos value
 1:  1   1     2
 2:  2   1     3
 3:  3   1     2
 4:  1   2     3
 5:  2   2     2
 6:  3   2     3
 7:  1   3     4
 8:  2   3     4
 9:  3   3     4
10:  1   4     2
11:  2   4     6
12:  3   4     2
13:  1   5     6
14:  2   5     7
15:  3   5     3

列名将转换为整数位置,并且在重塑之前已添加行ID。

现在,所有准备工作都已完成,我们可以执行计算的中心部分,即非等价内部联接。这样,我们在dl中找到与rn匹配的条目,并且实际位置pos在可能组合的给定范围[start, end]内。参数by = .EACHI请求立即合并联接参数。 toString()用作聚合函数。所以,

dl[vi, on = .(rn, pos >= start, pos <= end), 
   .(rn, values = toString(value), position = toString(cols[x.pos])), 
   by = .EACHI, nomatch = 0L]

收益

    rn pos pos rn        values           position
 1:  1   1   2  1          2, 3             v1, v2
 2:  1   2   3  1          3, 4             v2, v3
 3:  1   3   4  1          4, 2             v3, v4
 4:  1   4   5  1          2, 6             v4, v5
 5:  1   1   3  1       2, 3, 4         v1, v2, v3
 6:  1   2   4  1       3, 4, 2         v2, v3, v4
 7:  1   3   5  1       4, 2, 6         v3, v4, v5
 8:  1   1   4  1    2, 3, 4, 2     v1, v2, v3, v4
 9:  1   2   5  1    3, 4, 2, 6     v2, v3, v4, v5
10:  1   1   5  1 2, 3, 4, 2, 6 v1, v2, v3, v4, v5
11:  2   1   2  1          3, 2             v1, v2
12:  2   2   3  1          2, 4             v2, v3
13:  2   3   4  1          4, 6             v3, v4
14:  2   4   5  1          6, 7             v4, v5
15:  2   1   3  1       3, 2, 4         v1, v2, v3
16:  2   2   4  1       2, 4, 6         v2, v3, v4
17:  2   3   5  1       4, 6, 7         v3, v4, v5
18:  2   1   4  1    3, 2, 4, 6     v1, v2, v3, v4
19:  2   2   5  1    2, 4, 6, 7     v2, v3, v4, v5
20:  2   1   5  1 3, 2, 4, 6, 7 v1, v2, v3, v4, v5
21:  3   1   2  1          2, 3             v1, v2
22:  3   2   3  1          3, 4             v2, v3
23:  3   3   4  1          4, 2             v3, v4
24:  3   4   5  1          2, 3             v4, v5
25:  3   1   3  1       2, 3, 4         v1, v2, v3
26:  3   2   4  1       3, 4, 2         v2, v3, v4
27:  3   3   5  1       4, 2, 3         v3, v4, v5
28:  3   1   4  1    2, 3, 4, 2     v1, v2, v3, v4
29:  3   2   5  1    3, 4, 2, 3     v2, v3, v4, v5
30:  3   1   5  1 2, 3, 4, 2, 3 v1, v2, v3, v4, v5
    rn pos pos rn        values           position

请注意,整数pos的值将转换回原始列名。

在随后的聚合步骤中,确定唯一valuesposition对的出现次数。请注意,这两个操作是 chained 链接的,它们是data.table语法的 piping

最后,在打印时方便地对行进行重新排序。

在非等额联接中使用列号范围可确保仅根据请求考虑连续的列。