função rollapply em coluna específica de dataframes dentro da lista
Pergunta
Devo admitir que sou uma loucura completa ao tentar entender como funções dentro de funções são definidas e passadas em R.Os exemplos sempre presumem que você entende todas as nuances e não fornecem descrições do processo.Ainda não encontrei um guia idiota em inglês simples que descreva o processo.Então a primeira pergunta é: você conhece algum?
Agora meu problema físico.
Eu tenho uma lista de data.frames:arquivoData.
Quero usar a função rollapply() em colunas específicas em cada data.frame.Quero então todos os resultados (listas) combinados.Então, começando com um dos data.frames usando os dataframes mtcars integrados como exemplo:
É claro que preciso dizer ao rollapply() para usar a função PPI() junto com os parâmetros associados que são as colunas.
PPI <- function(a, b){
value = (a + b)
PPI = sum(value)
return(PPI)
}
Eu tentei isso:
f <- function(x) PPI(x$mpg, x$disp)
fileData<- list(mtcars, mtcars, mtcars)
df <- fileData[[1]]
e foi parado em
rollapply(df, 20, f)
Error in x$mpg : $ operator is invalid for atomic vectors
Acho que isso está relacionado ao uso de matrizes pelo Zoo, mas outras inúmeras tentativas não conseguiram resolver o problema do rollapply.Então, passando para o que acredito ser o próximo:
lapply(fileData, function(x) rollapply ......
Parece a um quilômetro de distância.Algumas orientações e soluções seriam muito bem-vindas.
Obrigado.
Solução 3
Eu suei muito e levei algum tempo para entender lentamente como quebrar o processo e o protocolo de chamada de uma função com argumentos de outra função.Um ótimo site que ajudou foi R avançado do único Hadley Wickham, de novo!As imagens que mostram a análise do processo são quase ideais.Embora eu ainda precisasse pensar em alguns detalhes.
Aqui está um exemplo completo com notas.Espero que alguém ache isso útil.
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)
Resultados:
> 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
Outras dicas
Tentarei ajudá-lo e mostrar como você pode depurar o problema.Um truque que é muito útil em R é aprender como depurar.Gnerelly estou usando browser
função.
problema :
Aqui estou eu mudando sua função f
adicionando uma linha:
f <- function(x) {
browser()
PPI(x$changeFactor_A, x$changeFactor_B)
}
Agora, quando você executar:
rollapply(df, 1, f)
O depurador para e você pode inspecionar o valor do argumento x:
Browse[1]> x
[1,]
1e+05
como você vê é um valor escalar, então você não pode aplicar o $
operador nele, daí você recebe o erro:
Error in x$changeFactor_A : $ operator is invalid for atomic vectors
guias gerais
Agora vou explicar como você deve fazer isso.
- Ou você altera sua função PPI, para ter um único parâmetro
excees
:então você faz a subtração fora dele (mais fácil) - Ou você usa
mapply
para obter uma solução generalizada.(Mais difícil, mas mais geral e muito útil) - Evite usar
$
dentro das funções.Pessoalmente, eu uso apenas no console R.
solução completa:
Presumo que você data.frames (objetos zoológicos) tenha colunas changeFactor_A e changeFactor_B.
sapply(fileData,function(dat){
dat <- transform(dat,excess= changeFactor_A-changeFactor_B)
rollapply(dat[,'excess'],2,sum)
}
Ou de forma mais geral:
sapply(fileData,function(dat){
excess <- get_excess(dat,'changeFactor_A','changeFactor_B')
rollapply(excess,2,sum)
}
Onde
get_excess <-
function(data,colA,colB){
### do whatever you want here
### return a vector
excess
}
Consulte a seção "Uso" da página de ajuda para ?rollapply
.Admito que as páginas de ajuda do R não são fáceis de analisar e vejo como você ficou confuso.
O problema é que rollapply
pode lidar com ts
, zoo
ou geral numeric
vetores, mas apenas uma única série.Você está alimentando-o com uma função que leva dois argumentos, asset
e benchmark
.Concedido, seu f
e PPI
pode ser trivialmente vetorizado, mas rollapply
simplesmente não foi feito para isso.
Solução:calcule o seu excess
fora rollapply
(excess
é facilmente calculado vetorialmente e não envolve nenhum cálculo contínuo), e só então rollapply
sua função para ele:
> mtcars$excess <- mtcars$mpg-mtcars$disp
> rollapply(mtcars$excess, 3, sum)
[1] -363.2 -460.8 -663.1 -784.8 -893.9 ...
Você pode estar interessado em mapply
, que vetoriza uma função para múltiplo argumentos, da mesma forma que apply
e amigos, que trabalham solteiro argumentos.No entanto, não conheço nenhum análogo de mapply
com rolando janelas.