我有一个这样的数据框:
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行。
答案 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)
以下是使用Reduce
和mapply
的方法:
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