Удалите NA в ряду и переместите ячейку справа, где NA был расположен в R, также уникальные значения
Итак, у меня есть фрейм данных в 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)
Спасибо за помощь
3 ответа
Мы перебираем строки 'df' (используя apply
с MARGIN
как 1), удалите NA
элементы (!is.na(x)
) и получить unique
ценности. Тогда, если длина элементов не одинакова, на выходе будет list
('ЛСТ'). Мы используем lengths
чтобы получить length
каждого list
элемент, get the
минof it, based on it we subset the
списокelements and
cbind` с первым столбцом 'ID'.
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
Ужасно, но должно быть эффективно (пережевано 3M записей за 20 секунд и 300K за < 2 секунды):
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
Основываясь на идее 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!