Функцияrollapply для определенного столбца кадров данных в списке

StackOverflow https://stackoverflow.com//questions/24029937

Вопрос

Я должен признаться в своем полном безумии, пытаясь понять, как функции внутри функций определяются и передаются в 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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top