Мне все еще трудно думать о том, как работать со столбцами R data.table, которые являются списками.
Вот таблица R data.table:
library(data.table)
dt = data.table(
numericcol = rep(42, 8),
listcol = list(c(1, 22, 3), 6, 1, 12, c(5, 6, 1123), 3, 42, 1)
)
> dt
numericcol listcol
1: 42 1,22, 3
2: 42 6
3: 42 1
4: 42 12
5: 42 5, 6,1123
6: 42 3
7: 42 42
8: 42 1
Я хотел бы создать столбец для абсолютных значений между элементами numericcol
и listcol
:
> dt
numericcol listcol absvals
1: 42 1,22, 3 41, 20, 39
2: 42 6 36
3: 42 1 41
4: 42 12 30
5: 42 5, 6,1123 37, 36, 1081
6: 42 3 39
7: 42 42 0
8: 42 1 41
Итак, моей первой мыслью было бы использовать sapply()
следующим образом:
dt[, absvals := sapply(listcol, function(x) abs(x-numericcol))]
Это выводит следующее:
> dt
numericcol listcol absvals
1: 42 1,22, 3 41
2: 42 6 20
3: 42 1 39
4: 42 12 41
5: 42 5, 6,1123 20
6: 42 3 39
7: 42 42 41
8: 42 1 20
Итак, absvals
теперь представляет собой столбец элементов, не включенных в список, с отдельным элементом в каждой строке и имеет другое измерение, чем таблица data.table.
(1) Как создать absvals
, чтобы сохранить структуру списка listcol
?
(2) В подобных случаях, если меня интересует только вектор значений, как пользователи R data.table создают такую структуру данных?
Может быть
vec = as.vector(dt[, absvals := sapply(listcol, function(x) abs(x-numericcol))])
?
Я думаю, что это в основном построчная операция, поэтому подход будет немного нестабильным. И ключ, который следует помнить о столбцах list
в data.table
, заключается в том, что [.data.table
предполагает, что любой вывод j
, который является list
, относится к столбцам, поэтому вам нужно снова обернуть любой list
в list
, чтобы j
понял, что есть только один столбец.
Я думаю, что это работает в вашем случае:
dt[ , abs_vals := list(lapply(seq_along(.I), function(ii)
abs(listcol[[ii]] - numericcol[ii])))][]
# numericcol listcol abs_vals
# 1: 42 1,22, 3 41,20,39
# 2: 42 6 36
# 3: 42 1 41
# 4: 42 12 30
# 5: 42 5, 6,1123 37, 36,1081
# 6: 42 3 39
# 7: 42 42 0
# 8: 42 1 41
Компонент seq_along(.I)
обрабатывает построчный аспект.
Другое решение с использованием mapply
:
dt[, absvals := mapply(listcol, numericcol, FUN = function(x, y) abs(x-y))]
#output
dt
numericcol listcol absvals
1: 42 1,22, 3 41,20,39
2: 42 6 36
3: 42 1 41
4: 42 12 30
5: 42 5, 6,1123 37, 36,1081
6: 42 3 39
7: 42 42 0
8: 42 1 41
Мы можем использовать Map
dt[, absvals := Map(function(x, y) abs(x-y), listcol, numericcol)]
dt
# numericcol listcol absvals
#1: 42 1,22, 3 41,20,39
#2: 42 6 36
#3: 42 1 41
#4: 42 12 30
#5: 42 5, 6,1123 37, 36,1081
#6: 42 3 39
#7: 42 42 0
#8: 42 1 41
Или с purrr::map
dt[, absvals := map2(listcol, numericcol, ~ abs(.x -.y))]
Вместо многократного зацикливания, есть также опция unlist
и получение абсолютного отличия от rep
licated 'numericol' на основе lengths
'listval'ов. Это могло быть более эффективно
dt[, absvals := relist(abs(rep(numericcol, lengths(listcol)) -
unlist(listcol)), skeleton = listcol)]
ПРИМЕЧАНИЕ. Здесь нет необходимости в репликации, поскольку это то же значение для 'numericol', но rep
предназначен для общего случая.
Вы можете использовать apply()
, чтобы просмотреть ваш data.table
строка за строкой и получить абсолютное значение разности numericol
и каждого элемента listcol
следующим образом;
dt[, absvals := apply(.SD,
1,
function(x) abs(x$numericcol - x$listcol))]
Результат такой:
numericcol listcol absvals
1: 42 1,22, 3 41,20,39
2: 42 6 36
3: 42 1 41
4: 42 12 30
5: 42 5, 6,1123 37, 36,1081
6: 42 3 39
7: 42 42 0
8: 42 1 41
Может, столбец списка действительно не нужен? Похоже, все это можно было бы сделать проще.
# convert to long format:
dt2 <- dt[, .(var = unlist(listcol)), by = numericcol]
dt2[, absval := abs(var - numericcol)]
dt2
numericcol var absval
1: 42 1 41
2: 42 22 20
3: 42 3 39
4: 42 6 36
5: 42 1 41
6: 42 12 30
7: 42 5 37
8: 42 6 36
9: 42 1123 1081
10: 42 3 39
11: 42 42 0
12: 42 1 41
По моему опыту, работать с объектами списка сложнее и намного медленнее, чем с простыми data.tables.
превосходный ответ. очень хорошо