Функцияrollapply для определенного столбца кадров данных в списке
Вопрос
Я должен признаться в своем полном безумии, пытаясь понять, как функции внутри функций определяются и передаются в R.Примеры всегда предполагают, что вы понимаете все нюансы, и не содержат описания процесса.Я еще не встречал простого английского руководства для идиотов, объясняющего процесс.Итак, первый вопрос: знаете ли вы о таком?
Теперь моя физическая проблема.
У меня есть список data.frames:fileData.
Я хочу использовать функциюrollapply() для определенных столбцов в каждом data.frame.Затем я хочу объединить все результаты (списки).Итак, начнём с одного из data.frames, используя в качестве примера встроенные фреймы данных mtcars:
Конечно, мне нужно указатьrollapply() использовать функцию PPI() вместе со связанными параметрами, которые являются столбцами.
PPI <- function(a, b){
value = (a + b)
PPI = sum(value)
return(PPI)
}
Я попробовал это:
f <- function(x) PPI(x$mpg, x$disp)
fileData<- list(mtcars, mtcars, mtcars)
df <- fileData[[1]]
и был остановлен на
rollapply(df, 20, f)
Error in x$mpg : $ operator is invalid for atomic vectors
Я думаю, что это связано с использованием матриц в Zoo, но другие многочисленные попытки не смогли решить проблему роллаппли.Итак, перейдем к тому, что, по моему мнению, будет следующим:
lapply(fileData, function(x) rollapply ......
Кажется, в миле отсюда.Некоторые рекомендации и решения будут очень приветствоваться.
Спасибо.
Решение 3
Я потел и потребовался некоторое время, чтобы постепенно понять, как сломать процесс и протокол о вызове функции с аргументами из другой функции.Отличный сайт, который помог был Продвинутый R изЕдинственный Хэдли Уикхам, опять же!Картинки, показывающие разбивку процесса, близки к идеалу.Хотя мне все еще нужна моя мышца на несколько деталей.
Вот полный пример с примечаниями.Надеюсь, кто-то еще найдет это полезным.
library(zoo)
#Create a list of dataframes for the example.
listOfDataFrames<- list(mtcars, mtcars, mtcars)
#Give each element a name.
names(listOfDataFrames) <- c("A", "B", "C")
#This is a simple function just for the example!
#I want to perform this function on column 'col' of matrix 'm'.
#Of course to make the whole task worthwhile, this function is usually something more complex.
fApplyFunction <- function(m,col){
mean(m[,col])
}
#This function is called from lapply() and does 'something' to the dataframe that is passed.
#I created this function to keep lapply() very simply.
#The something is to apply the function fApplyFunction(), wich requires an argument 'thisCol'.
fOnEachElement <- function(thisDF, thisCol){
#Convert to matrix for zoo library.
thisMatrix <- as.matrix(thisDF)
rollapply(thisMatrix, 5, fApplyFunction, thisCol, partial = FALSE, by.column = FALSE)
}
#This is where the program really starts!
#
#Apply a function to each element of list.
#The list is 'fileData', with each element being a dataframe.
#The function to apply to each element is 'fOnEachElement'
#The additional argument for 'fOnEachElement' is "vs", which is the name of the column I want the function performed on.
#lapply() returns each result as an element of a list.
listResults <- lapply(listOfDataFrames, fOnEachElement, "vs")
#Combine all elements of the list into one dataframe.
combinedResults <- do.call(cbind, listResults)
#Now that I understand the argument passing, I could call rollapply() directly from lapply()...
#Note that ONLY the additional arguments of rollapply() are passed. The primary argurment is passed automatically by lapply().
listResults2 <- lapply(listOfDataFrames, rollapply, 5, fApplyFunction, "vs", partial = FALSE, by.column = FALSE)
.
Результаты:
> combinedResults
A B C
[1,] 0.4 0.4 0.4
[2,] 0.6 0.6 0.6
[3,] 0.6 0.6 0.6
[4,] 0.6 0.6 0.6
[5,] 0.6 0.6 0.6
[6,] 0.8 0.8 0.8
[7,] 0.8 0.8 0.8
[8,] 0.8 0.8 0.8
[9,] 0.6 0.6 0.6
[10,] 0.4 0.4 0.4
[11,] 0.2 0.2 0.2
[12,] 0.0 0.0 0.0
[13,] 0.0 0.0 0.0
[14,] 0.2 0.2 0.2
[15,] 0.4 0.4 0.4
[16,] 0.6 0.6 0.6
[17,] 0.8 0.8 0.8
[18,] 0.8 0.8 0.8
[19,] 0.6 0.6 0.6
[20,] 0.4 0.4 0.4
[21,] 0.2 0.2 0.2
[22,] 0.2 0.2 0.2
[23,] 0.2 0.2 0.2
[24,] 0.4 0.4 0.4
[25,] 0.4 0.4 0.4
[26,] 0.4 0.4 0.4
[27,] 0.2 0.2 0.2
[28,] 0.4 0.4 0.4
> listResults
$A
[1] 0.4 0.6 0.6 0.6 0.6 0.8 0.8 0.8 0.6 0.4 0.2 0.0 0.0 0.2 0.4 0.6 0.8 0.8 0.6
[20] 0.4 0.2 0.2 0.2 0.4 0.4 0.4 0.2 0.4
$B
[1] 0.4 0.6 0.6 0.6 0.6 0.8 0.8 0.8 0.6 0.4 0.2 0.0 0.0 0.2 0.4 0.6 0.8 0.8 0.6
[20] 0.4 0.2 0.2 0.2 0.4 0.4 0.4 0.2 0.4
$C
[1] 0.4 0.6 0.6 0.6 0.6 0.8 0.8 0.8 0.6 0.4 0.2 0.0 0.0 0.2 0.4 0.6 0.8 0.8 0.6
[20] 0.4 0.2 0.2 0.2 0.4 0.4 0.4 0.2 0.4
> listResults2
$A
[1] 0.4 0.6 0.6 0.6 0.6 0.8 0.8 0.8 0.6 0.4 0.2 0.0 0.0 0.2 0.4 0.6 0.8 0.8 0.6
[20] 0.4 0.2 0.2 0.2 0.4 0.4 0.4 0.2 0.4
$B
[1] 0.4 0.6 0.6 0.6 0.6 0.8 0.8 0.8 0.6 0.4 0.2 0.0 0.0 0.2 0.4 0.6 0.8 0.8 0.6
[20] 0.4 0.2 0.2 0.2 0.4 0.4 0.4 0.2 0.4
$C
[1] 0.4 0.6 0.6 0.6 0.6 0.8 0.8 0.8 0.6 0.4 0.2 0.0 0.0 0.2 0.4 0.6 0.8 0.8 0.6
[20] 0.4 0.2 0.2 0.2 0.4 0.4 0.4 0.2 0.4
. Другие советы
Я постараюсь помочь вам и показать, как можно устранить проблему.Один трюк, который очень полезен в R, — это научиться отлаживать.Гнерелли я использую browser
функция.
проблема :
Здесь я меняю вашу функцию f
добавив одну строку:
f <- function(x) {
browser()
PPI(x$changeFactor_A, x$changeFactor_B)
}
Теперь, когда вы запустите:
rollapply(df, 1, f)
Отладчик останавливается, и вы можете проверить значение аргумента x:
Browse[1]> x
[1,]
1e+05
как вы видите, это скалярное значение, поэтому вы не можете применить $
оператор на нем, поэтому вы получаете ошибку:
Error in x$changeFactor_A : $ operator is invalid for atomic vectors
общие руководства
Сейчас я объясню, как вам следует это сделать.
- Либо вы измените свою функцию PPI, чтобы иметь один параметр
excees
:поэтому вы выполняете вычитание за его пределами (проще) - Или вы используете
mapply
получить обобщенное решение.(Сложнее, но более общий и очень полезный) - Избегать использования
$
внутри функций.Лично я использую его только на консоли R.
полное решение:
Я предполагаю, что у вас data.frames(объекты зоопарка) есть столбцы ChangeFactor_A и ChangeFactor_B.
sapply(fileData,function(dat){
dat <- transform(dat,excess= changeFactor_A-changeFactor_B)
rollapply(dat[,'excess'],2,sum)
}
Или в более общем смысле:
sapply(fileData,function(dat){
excess <- get_excess(dat,'changeFactor_A','changeFactor_B')
rollapply(excess,2,sum)
}
Где
get_excess <-
function(data,colA,colB){
### do whatever you want here
### return a vector
excess
}
Ознакомьтесь с разделом "Использование" на странице справки, чтобы ?rollapply
.Я признаю, что страницы справки R нелегко разобрать, и я вижу, как вы запутались.
Проблема в том, что rollapply
может иметь дело с ts
, zoo
или общий numeric
векторы, но только одна серия.Вы передаете ему функцию, которая выполняет два аргументы, asset
и benchmark
.Предоставленный, ваш f
и PPI
может быть тривиально векторизован, но rollapply
просто не создан для этого.
Решение:рассчитайте свой excess
снаружи rollapply
(excess
легко вычисляется векторным способом, и это не требует каких-либо скользящих вычислений), и только тогда rollapply
ваша функция по отношению к нему:
> mtcars$excess <- mtcars$mpg-mtcars$disp
> rollapply(mtcars$excess, 3, sum)
[1] -363.2 -460.8 -663.1 -784.8 -893.9 ...
Возможно, вас заинтересует mapply
, который векторизирует функцию для множественный аргументы, аналогичные apply
и друзья, которые работают над одинокий аргументы.Однако я не знаю ни одного аналога mapply
с катящийся Windows.