`sapply` успешен для объекта «зоопарк», но не для объекта «xts», почему?

#r #dataframe #xts #zoo #sapply

#r #фрейм данных #xts #зоопарк #sapply

Вопрос:

Вот пример, который показывает четкое различие между «zoo» и «xts».

 library(xts)

mydf = as.data.frame(replicate(6, sample(c(1:10), 10, rep = T)))
myzoo = zoo(mydf, order.by = Sys.Date()   1:10)
resultzoo = sapply(myzoo, function(x) x 1)
  

Хотя я теряю дату (это поведение уже прокомментировано здесь), приведенный выше код работает нормально. Однако приведенный ниже код выдает ошибку

 myxts = xts(mydf, order.by = Sys.Date()   1:10)
resultxts = sapply(myxts, function(x) x 1)
# Error in array(r, dim = d, dimnames = if (!(is.null(n1 <- names(x[[1L]])) amp;  : 
# length of 'dimnames' [1] not equal to array extent
  

Я не могу найти никакого объяснения этому странному поведению. Любая идея приветствуется.

Комментарии:

1. Обратите внимание, что sapply это должно вызываться со списком для первого аргумента, X . И X будет принудительно преобразован в список, если он не один. Лучшей альтернативой вызову sapply объекта zoo / xts является просто использование apply(X, 2, FUN) для перебора столбцов.

Ответ №1:

Я думаю, вы подняли очень хороший вопрос. Прежде чем ответить, я хотел бы прокомментировать, что вы можете использовать

 sapply(myzoo, " ", 1)
sapply(myxts, " ", 1)
  

вместо

 sapply(myzoo, function (x) x   1)
sapply(myxts, function (x) x   1)
  

Это потому " " , что это уже функция. Попробуйте 1 2 и " "(1, 2) .


sapply выполняется в два этапа. Первый этап — это обычный вызов lapply ; второй этап — это вызов simplify2array для упрощения результата. Полученное сообщение об ошибке сообщает, что на втором этапе происходит что-то не так. Действительно, если мы попытаемся:

 x1 <- lapply(myzoo, " ", 1)
x2 <- lapply(myxts, " ", 1)
  

мы вообще не получаем ошибок!

Однако, если мы сравним x1 и x2 , мы увидим разницу. Для аккуратности я просто извлеку первый элемент списка:

 x1[[1]]

#2016-09-30 2016-10-01 2016-10-02 2016-10-03 2016-10-04 2016-10-05 2016-10-06 
#         3          4          5          7          2          2          4 
#2016-10-07 2016-10-08 2016-10-09 
#         3          5          3 

x2[[1]]

#           V1
#2016-09-30  3
#2016-10-01  4
#2016-10-02  5
#2016-10-03  7
#2016-10-04  2
#2016-10-05  2
#2016-10-06  4
#2016-10-07  3
#2016-10-08  5
#2016-10-09  3
  

Ах, для объекта «зоопарк» измерение отбрасывается, поэтому мы получаем вектор; в то время как для объекта «xts» измерение не отбрасывается, следовательно, мы получаем матрицу с одним столбцом!

Именно по этой причине sapply происходит сбой. По умолчанию опция упрощения для sapply is simplify = TRUE , которая всегда пытается упростить до 1D-вектора или 2D-матрицы. Для x1 этого нет проблем; но для x2 этого это невозможно.

Если мы используем более мягкую настройку: simplify = "array" , мы получим соответствующее поведение:

  1. sapply(myzoo, " ", 1, simplify = "array") выдает 2D-массив (т. Е. Матрицу, Которую вы видите);
  2. sapply(myxts, " ", 1, simplify = "array") дает 3D-массив.

Из этого примера мы видим, что sapply это не всегда желательно. Почему бы не использовать следующее:

 y1 <- do.call(cbind, x1)
y2 <- do.call(cbind, x2)

#           V1 V2 V3 V4 V5 V6
#2016-09-30  3  8  6  4 11  3
#2016-10-01  4  3  9  2  5  7
#2016-10-02  5  7  9  7  7 10
#2016-10-03  7  2  5  3  5  3
#2016-10-04  2  6  7  2  4  5
#2016-10-05  2  2 11  2  4  7
#2016-10-06  4  3 10 10  8  2
#2016-10-07  3  6  4  5  9  4
#2016-10-08  5  4 10 10  3  8
#2016-10-09  3  3 11  8 11  7
  

Они выдают один и тот же результат, и вы получаете даты в виде имен строк! Более того, соблюдается исходный класс объекта!

 class(y1)
# [1] "zoo"

class(y2)
# [1] "xts" "zoo"
  

Последующие действия

Из curiosity….is есть ли функция для *apply семьи, выполняющая эквивалент вашей двухэтапной процедуры (т.е. lapply do.call )?

Кажется, нет. Вы можете получить их все ?lapply (включая часть «См. Также»). Если это действительно так, люди на этом сайте не будут так lapply do.call часто использовать комбинацию .

Комментарии:

1. Большое спасибо за ваш подробный ответ. Действительно полезно. Из curiosity….is есть ли функция для семейства «применить», выполняющая эквивалент вашей двухэтапной процедуры (т.е. lapply do.call)?