Эффективно повторять data.table в списке, последовательно заменяя столбцы с такими же именами из другого data.table в цикле - PullRequest
1 голос
/ 06 апреля 2019

У меня есть два data.table s:

x <- data.table(a = c(1, 2, 3, 4, 1), b = c(2, 3, 4, 1, 2), c = c(3, 4, 1, 2, 3))
y <- data.table(a = c(1, 0, 0, 0, 1), b = c(0, 1, 0, 0, 0), c = c(0, 0, 0, 0, 1))

Я пытаюсь создать список из y с длиной числа его столбцов, где каждый следующий столбец заменяется назначения этого же столбца в x.Желаемый результат должен выглядеть следующим образом:

[[1]]
   a b c
1: 1 0 0
2: 2 1 0
3: 3 0 0
4: 4 0 0
5: 1 0 1

[[2]]
   a b c
1: 1 2 0
2: 0 3 0
3: 0 4 0
4: 0 1 0
5: 1 2 1

[[3]]
   a b c
1: 1 0 3
2: 0 1 4
3: 0 0 1
4: 0 0 2
5: 1 0 3

Что я пробовал:

z <- lapply(names(x), function(i) {
  x[ , i, with = FALSE]
})

w <- rep(list(y), ncol(y))

myfun <- function(obj1, obj2) {
  cbind(obj1, obj2)
}

u <- Map(myfun, obj1 = z, obj2 = w)

u <- lapply(u, function(i) {
  setcolorder(i[ , unique(names(i)), with = FALSE], names(x))
})

Это дает мне желаемый результат, но очень неуклюжий и требует слишком много шагов, следовательно, этовероятно, неэффективно с большими data.table с.Я хотел бы иметь больше в data.table пути.Я попробовал что-то, что, как я предполагал, будет работать:

lapply(names(x), function(i) {
  y[ , (i) := x[ , i, with = FALSE]]
})

Однако, он возвращает первый компонент списка пустым и копирует все значения x в следующие компоненты списка.

Может кто-топомочь?

1 Ответ

3 голосов
/ 06 апреля 2019

Здесь нам может понадобиться copy из 'y' при создании list 'w' вместо

w <- rep(list(y), ncol(y))

Соблазнительно использовать приведенное ниже выражение rep. Однако, есть проблема в элементах w, так как они указывают на то же место в памяти

w <- rep(list(copy(x)), ncol(y))

Присвоение (:=) по ссылке изменяет значения столбцов в каждом цикле, поскольку они ссылаются на один и тот же объект в памяти. В первом случае, после присваивания, он также меняет «y» вместе с «w» list элементами. Во втором случае он может изменить только «w» и оставить «y», потому что мы copy отметили. Чтобы понять поведение, выполните присваивание set в цикле for

for(j in seq_along(x)) {print(w[[j]][[j]])
        set(w[[j]], i = NULL, j =j, x[[j]])
        print("----")
        print(w[[j]])
   } 

Чтобы избежать этого, используйте replicate

w <- replicate(ncol(y), copy(y), simplify = FALSE)

и затем выполните цикл for (после повторного воссоздания объектов после замены значений из предыдущего запуска)

for(j in seq_along(x)) {print(w[[j]][[j]])
        set(w[[j]], i = NULL, j =j, x[[j]])
        print("----")
        print(w[[j]])
   } 

Или Map на основе назначения

Map(function(u, v) u[, (v) := x[[v]]][],  w, names(x))
#[[1]]
#   a b c
#1: 1 0 0
#2: 2 1 0
#3: 3 0 0
#4: 4 0 0
#5: 1 0 1

#[[2]]
#   a b c
#1: 1 2 0
#2: 0 3 0
#3: 0 4 0
#4: 0 1 0
#5: 1 2 1

#[[3]]
#   a b c
#1: 1 0 3
#2: 0 1 4
#3: 0 0 1
#4: 0 0 2
#5: 1 0 3

Вместо присвоения по ссылке это можно сделать с помощью простого Map из base R, если мы не copy указали объект 'y' при создании 'w'

Map(function(u, v) {u[[v]] <- x[[v]]
                    u}, w, names(x))

...