Ответ 1
Я использовал это в прошлом,
expand_args <- function(...){
dots <- list(...)
max_length <- max(sapply(dots, length))
lapply(dots, rep, length.out = max_length)
}
Одна приятная особенность R, связанная с присущей ей векторизованной природе, - это правило утилизации, описанное в разделе Введение в R в разделе 2.2.
Векторы, встречающиеся в одном выражении, не обязательно должны иметь одинаковую длину. Если это не так, значение выражения представляет собой вектор с той же длиной, что и самый длинный вектор, который встречается в выражении. Более короткие векторы в выражении перерабатываются так часто, как нужно (возможно, дробно), пока они не совпадут с длиной самого длинного вектора. В частности, константа просто повторяется.
Большинство стандартных функций используют это, но код, который делает это, похоронен в базовом C-коде.
Существует ли канонический способ реализации стандартных правил утилизации для функции полностью в R-коде? То есть, учитывая такую функцию, как
mock <- function(a, b, c) {
# turn a, b, and c into appropriate recycled versions
# do something with recycled a, b, and c in some appropriately vectorized way
}
где a
, b
и c
- векторы, возможно, различной длины и неизвестных типов/классов, существует ли канонический способ получить новый набор векторов, которые перерабатываются в соответствии со стандартными правилами утилизации? В частности, я не могу предположить, что шаг "сделать что-то" будет выполнять надлежащую переработку, поэтому мне нужно сделать это заранее.
Я использовал это в прошлом,
expand_args <- function(...){
dots <- list(...)
max_length <- max(sapply(dots, length))
lapply(dots, rep, length.out = max_length)
}
Я бы использовал аргумент length.out
rep()
, чтобы выполнить большую часть реальной работы.
Вот пример, который создает функцию better.data.frame()
(ее действительно следует называть "better".data.frame()
), которая не накладывает ограничений на длины векторов, которые она передала в качестве аргументов. В этом случае я перерабатываю все векторы на длину самого длинного, но вы можете, очевидно, адаптировать это, чтобы удовлетворить ваши собственные потребности в утилизации!
better.data.frame <- function(...) {
cols <- list(...)
names(cols) <- sapply(as.list(match.call()), deparse)[-1]
# Find the length of the longest vector
# and then recycle all columns to that length.
n <- max(sapply(cols, length))
cols <- lapply(cols, rep, length.out = n)
as.data.frame(cols)
}
# Try it out
a <- Sys.Date() + 0:9
b <- 1:3
c <- letters[1:4]
data.frame(a,b,c)
# Error in data.frame(a, b, c) :
# arguments imply differing number of rows: 10, 3, 4
better.data.frame(a,b,c)
# a b c
# 1 2012-02-17 1 a
# 2 2012-02-18 2 b
# 3 2012-02-19 3 c
# 4 2012-02-20 1 d
# 5 2012-02-21 2 a
# 6 2012-02-22 3 b
# 7 2012-02-23 1 c
# 8 2012-02-24 2 d
# 9 2012-02-25 3 a
# 10 2012-02-26 1 b
Один короткий и грязный маршрут для численных аргументов - полагаться на автоматическую переработку cbind. Например:
f.abc <- function(a,b,c) {
df.abc <- as.data.frame( suppressWarnings( cbind(a=a, b=b, c=c) ) )
#Then use, for example, with() to use a, b and c inside the data frame,
#or apply(df.abc,1, ...)
}
Он сильно полагается на отсутствие других законных причин для предупреждений.