重叠前一行

时间:2017-08-24 12:47:58

标签: r

我有一个这样的数据框:

set.seed(123) 
a <- c("A", "B", "C", "D", "E", "F", "G", "H", "I")
df <- data.frame(
   V1 = sample(a,4, replace=TRUE),
   V2 = sample(a,4, replace=TRUE),
   V3 = sample(a,4, replace=TRUE),
   V4 = sample(a,4, replace=TRUE)
)

看起来像

  V1 V2 V3 V4
1  C  I  E  G
2  H  A  E  F
3  D  E  I  A
4  H  I  E  I

我想计算一行中与前一行相比的唯一值的数量,因此结果如下:

  V1 V2 V3 V4 V5
1  C  I  E  G 4
2  H  A  E  F 3
3  D  E  I  A 2
4  H  I  E  I 1

第1行的V5等于4,因为它是第1行,并且都是唯一的

第2行的V5等于3,因为H,A和F不在第1行

第3行的V5等于2,因为1)D和I不在第2行.2)D和A不在第1行。

第4行的V5等于1,因为1)H不在第1行,2)我不在第2行,3)H不在第4行。

如果第4行是HIEA,那么第4行的V5仍然是1,因为它只有1个值不在第3行,即使它有2个值不在第2行而2个值不在第1行。

3 个答案:

答案 0 :(得分:2)

这是基础R中的多步骤方法。

# Create a list of the elements by row, using mike H's method
myList <-  strsplit(Reduce(paste0, df), "")
# previous method, could create new object first t(df) if large df
# myList <-  split(t(df), col(t(df)))

# get pairwise combinations of rows
combos <- t(combn(nrow(df):1, 2))[choose(nrow(df), 2):1,]

# get desired values, sapply runs through pairs of rows, tapply calculates min with row
df$cnts <- c(length(unique(myList[[1]])), # value for first row
             tapply(sapply(1:nrow(combos), # sapply through pairs, taking set diffs
                           function(x) length(setdiff(myList[[combos[x,1]]],
                                                      myList[[combos[x,2]]]))),
                     combos[,1], min)) # split set diff lengths by row, get min length

返回

df
  V1 V2 V3 V4 cnts
1  C  I  E  G    4
2  H  A  E  F    3
3  D  E  I  A    2
4  H  I  E  I    1

答案 1 :(得分:1)

对于此类任务,存储行/数据集,如&#34; df&#34;以制表格式有助于解决问题:

tab = table(as.matrix(df), row(df)) > 0
#> tab
#   
#        1     2     3     4
#  A FALSE  TRUE  TRUE FALSE
#  C  TRUE FALSE FALSE FALSE
#  D FALSE FALSE  TRUE FALSE
#  E  TRUE  TRUE  TRUE  TRUE
#  F FALSE  TRUE FALSE FALSE
#  G  TRUE FALSE FALSE FALSE
#  H FALSE  TRUE FALSE  TRUE
#  I  TRUE FALSE  TRUE  TRUE

crossprod可用于检索(以非常有效的方式)属于一行但不属于任何其他项目的项目数量:

ct = crossprod(tab, !tab)
#> ct
#   
#    1 2 3 4
#  1 0 3 2 2
#  2 3 0 2 2
#  3 2 2 0 2
#  4 1 1 1 0

上面我们可以看到,例如,第4行包含第1行不包含的1个元素,而第1行包含2个不在第4行中的元素,等等。

因为在这里我们只关心每一行的前一行,特别是每组一对一比较的最小值,得到结果的想法是:

ct[upper.tri(ct, TRUE)] = Inf  ## to ignore 'upper.tri' values in 'max.col'

j_min = max.col(-ct, "first")  ## row-index of the minimum difference per row
c(sum(tab[, 1]), 
  ct[cbind(2:nrow(df), j_min[-1])])
#[1] 4 3 2 1

答案 2 :(得分:0)

以下是使用Reducemapply的方法:

df$cols_paste <- strsplit(Reduce(paste0, df), split = "")
df$V5 <- lapply(1:length(df$cols_paste), function(x){ 
                                if(x==1) compare = NA
                                else compare = df$cols_paste[seq(1:(x-1))]
                                min(mapply(function(x, y) length(setdiff(x,y)), df$cols_paste[x],  compare))
                                         })

df[,setdiff(names(df), "cols_paste")]
  V1 V2 V3 V4 V5
1  C  I  E  G  4
2  H  A  E  F  3
3  D  E  I  A  2
4  H  I  E  I  1