Назначение значений в последовательности в зависимости от предыдущего значения строки в R
Я задал аналогичный вопрос вроде здесь, и упомянутое там решение отлично работает с проблемой, заявленной там, но эта версия немного сложнее и сложнее.
У меня есть таблица данных, подобная этой.
ID1 member
1 a parent
2 a child
3 a parent
4 a child
5 a child
6 b parent
7 b parent
8 b child
9 c child
10 c child
11 c parent
12 c child
И я хочу назначить последовательность, подобную приведенной ниже, в столбце ID1 и member.
ID1 member sequence
1 a parent 1
2 a child 2
3 a parent 1
4 a child 2
5 a child 3
6 b parent 1
7 b parent 1
8 b child 2
9 c child 2 *
10 c child 3
11 c parent 1
12 c child 2
то есть.
> dt$sequence = 1, wherever dt$member == "parent"
> dt$sequence = previous_row_value + 1, wherever dt$member=="child"
Но иногда бывает так, что новый ID1 может не начинаться с member = "parent". Если он начинается с "child" (как в примере со звездообразной строкой), мы должны начать последовательность с 2.
На данный момент я делаю это с использованием циклов, как показано ниже.
dt_sequence <- dt[ ,sequencing(.SD), by="ID1"]
sequencing <- function(dt){
for(i in 1:nrow(dt)){
if(i == 1){
if(dt[i,member] %in% "child")
dt$sequence[i] = 2
else
dt$sequence[i] = 1
}
else{
if(dt[i,member] %in% "child")
dt$sequence[i] = as.numeric(dt$sequence[i-1]) + 1
else
dt$sequence[i] = 1
}
}
return(dt)
}
Я запустил этот код в таблице данных из 4e5 строк, и потребовалось много времени для завершения (около 20 минут).
Может ли кто-нибудь предложить более быстрый способ сделать это.
Ответы
Ответ 1
DF <- read.table(text=" ID1 member
1 a parent
2 a child
3 a parent
4 a child
5 a child
6 b parent
7 b parent
8 b child
9 c child
10 c child
11 c parent
12 c child", header=TRUE, stringsAsFactors=FALSE)
library(data.table)
setDT(DF)
DF[, sequence := seq_along(member) + (member[1] == "child"),
by = list(ID1, cumsum(member == "parent"))]
# ID1 member sequence
# 1: a parent 1
# 2: a child 2
# 3: a parent 1
# 4: a child 2
# 5: a child 3
# 6: b parent 1
# 7: b parent 1
# 8: b child 2
# 9: c child 2
#10: c child 3
#11: c parent 1
#12: c child 2
Ответ 2
Попробуйте это,
dt$sequence <- rep(NA, length(dt$member))
for (i in seq_along(dt$member)){
dt$sequence[i] <- ifelse(dt$member[i]=="parent", 1,
ifelse(dt$ID1[i]==dt$ID1[i-1], dt$sequence[i-1] + 1, 2)
)
}
и более простое решение dplyr
data <- dt %>%
group_by(ID1) %>%
mutate(
seq = ifelse(member=="parent", 1, 2),
sequence = ifelse(seq==1, 1, lag(seq, default = 1) + 1)
)
Если каждая группа ID1
содержит хотя бы один parent
, гораздо более легкое решение будет упорядочивать данные в группе = ID1, так что parent
всегда будет сверху:
dt %>%
group_by(ID1) %>%
arrange(desc(member))
Ответ 3
Хороший вопрос. Итак, вот мое решение:
Данные
dd <- structure(list(ID1 = structure(c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 3L),
.Label = c("a", "b", "c"), class = "factor"),
member = structure(c(2L, 1L, 2L, 1L, 1L, 2L, 2L, 1L, 1L, 1L, 2L, 1L),
.Label = c("child", "parent"),
class = "factor")),
.Names = c("ID1", "member"),
row.names = c("1", "2", "3", "4", "5", "6", "7", "8",
"9", "10", "11", "12"), class = "data.frame")
код
Сначала установите все элементы с parent
в 1:
parent <- dd$member == "parent"
dd$sequence <- 0
dd$sequence[parent] <- 1
Теперь установите все child
elemetns без родительского имени в 2:
dd$sequence <- ave(dd$sequence, dd$ID1,
FUN = function(.) {
ret <- .
ret[1] <- if (ret[1] == 0) 2 else ret[1]
ret}
)
Теперь мы хотим получить длину каждой последовательности 0's
и позицию каждого 0
:
rl <- rle(dd$sequence)
rl.wh <- which(rl$values == 0)
Наконец, мы можем сгенерировать последовательности:
dd$sequence[dd$sequence == 0] <- unlist(mapply(function(x, r)
seq(x + 1, length.out = r, by = 1), rl$values[rl.wh - 1], rl$length[rl.wh]))