1

好的,所以我在 R 中有一个这样的数据框

ID <- c(1, 2, 3)
c1 <- c( 1, 1, NA)
c2 <- c(NA, NA, 5)
c3 <- c(NA, NA, NA)
c4 <- c(2, NA, 5)
c5 <- c(5, 7, 3)

df <- data.frame(ID, c1, c2, c3, c4, c5)

所以,这就是我要找的

1. Treat every row as a vector
2. Be able to remove all NAs in every row/vector
3. In a given row there can't be repeated values (expect for ID vs a number in other cell)
4. I'm looking to "cut" this row/vector.  I don't need 5 values just 2.

我这样做是为了 MAP@k 指标,所以数字的顺序(左边的比下一个更重要)儿子保持顺序很重要。

这是我正在寻找的输出

ID <- c(1, 2, 3)
c1 <- c(1, 1, 5)
c2 <- c(2, 7, 3)

df2 <- data.frame(ID, c1, c2)

谢谢您的帮助

4

3 回答 3

2

我们遍历 'df' 的行(使用applywith MARGINas 1),删除NA元素 ( !is.na(x)) 并获取unique值。然后,如果元素的长度不同,则输出将是list('lst')。我们使用第一列'ID'lengths来获取length每个list元素的, get the最小of it, based on it we subset the列表cbind`。elements and

 lst <- apply(df[-1], 1, function(x) unique(x[!is.na(x)]))
 dfN <- cbind(df[1], do.call(rbind,lapply(lst, function(x) x[seq(min(lengths(lst)))])))
 colnames(dfN)[-1] <- paste0("c", colnames(dfN)[-1])
 dfN
 #  ID c1 c2
 #1  1  1  2
 #2  2  1  7
 #3  3  5  3

注意:如果每行length中的unique元素相同(删除 后NA),输出将为matrix. 只需转置输出并cbind使用第一列。


或者另一种选择data.table应该非常有效。

library(data.table)
dM <- melt(setDT(df), id.var="ID", na.rm=TRUE)[, 
          .(value = unique(value), n = seq(uniqueN(value))), ID]
dcast(dM[dM[, n1 := min(tabulate(ID))][, .I[1:.N <=n1] , ID]$V1],
           ID~paste0("c", n), value.var="value")
#  ID c1 c2
#1:  1  1  2
#2:  2  1  7
#3:  3  5  3
于 2016-05-17T04:07:56.013 回答
1

丑陋但应该是高效的(在大约 20 秒内咀嚼 3M 记录,在 < 2 秒内咀嚼 300K):

sel <- !is.na(df[-1])
tmp <- unique(data.frame(ID=df$ID[row(df[-1])[sel]], c=df[-1][sel]))
tmp$time <- ave(tmp$ID, tmp$ID, FUN=seq_along)

reshape(tmp[tmp$time <= 2,], idvar="ID", direction="wide", sep="")

#  ID c1 c2
#1  1  1  2
#2  2  1  7
#3  3  5  3
于 2016-05-17T05:00:57.457 回答
1

基于 akrun data.table 的想法,我将 data.table 的代码翻译成 dplyr/tidyr(我更容易阅读,仅此而已)。这是代码

library(dplyr)
library(tidyr)

df_tidy <- df %>%
gather(importance, val, c1:c5) %>% 
na.omit %>% 
arrange(ID, importance) %>%
group_by(ID) %>%
distinct(ID, val) %>%
mutate(place = seq_len(n())) %>%
filter(place <= 2) %>%
mutate(place = paste("c", place, sep="")) %>%
select(-importance) %>%
spread(place, val)

谢谢 akrun 和 thelatemail !

于 2016-05-17T11:53:46.160 回答