Eu tenho um quadro de dados com várias colunas. Para cada linha do quadro de dados, desejo chamar uma função na linha, e a entrada da função está usando várias colunas dessa linha. Por exemplo, digamos que eu tenho esses dados e esse testFunc que aceita dois argumentos:
> df <- data.frame(x=c(1,2), y=c(3,4), z=c(5,6))
> df
x y z
1 1 3 5
2 2 4 6
> testFunc <- function(a, b) a + b
Digamos que eu queira aplicar este testFunc às colunas x e z. Então, para a linha 1, eu quero 1 + 5, e para a linha 2, eu quero 2 + 6. Existe uma maneira de fazer isso sem escrever um loop for, talvez com a família de funções apply?
Eu tentei isso:
> df[,c('x','z')]
x z
1 1 5
2 2 6
> lapply(df[,c('x','z')], testFunc)
Error in a + b : 'b' is missing
Mas tem erro, alguma idéia?
EDIT: a função real que eu quero chamar não é uma soma simples, mas é power.t.test. Eu usei a + b apenas para fins de exemplo. O objetivo final é poder fazer algo assim (escrito em pseudocódigo):
df = data.frame(
delta=c(delta_values),
power=c(power_values),
sig.level=c(sig.level_values)
)
lapply(df, power.t.test(delta_from_each_row_of_df,
power_from_each_row_of_df,
sig.level_from_each_row_of_df
))
onde o resultado é um vetor de saídas para power.t.test para cada linha de df.
dplyr
caminho.Respostas:
Você pode aplicar
apply
a um subconjunto dos dados originais.ou se sua função é apenas soma, use a versão vetorizada:
Se você quiser usar
testFunc
EDIT Para acessar colunas por nome e não por índice, você pode fazer algo assim:
fonte
apply
no big data.frames, ele copiará o objeto inteiro (para converter em uma matriz). Isso também causará problemas Se você tiver diferentes objetos de classe no data.frame.A
data.frame
é umlist
, então ...Para funções vetorizadas
do.call
é geralmente uma boa aposta. Mas os nomes dos argumentos entram em jogo. Aqui vocêtestFunc
é chamado com args x e y no lugar de a e b. O...
permite argumentos irrelevantes para ser passado sem causar um erro:Para funções não vetorizadas ,
mapply
funcionará, mas você precisa corresponder à ordem dos argumentos ou nomeá-los explicitamente:Às vezes
apply
funcionará - como quando todos os argumentos são do mesmo tipo, portanto, coagir adata.frame
uma matriz não causa problemas alterando os tipos de dados. Seu exemplo foi desse tipo.Se sua função deve ser chamada dentro de outra função na qual todos os argumentos são passados, existe um método muito mais preciso que esse. Estude as primeiras linhas do corpo de
lm()
se você deseja seguir esse caminho.fonte
Vectorize
como um wrapper paramapply
vetorizar funçõesUsar
mapply
fonte
Nova resposta com
dplyr
pacoteSe a função que você deseja aplicar for vetorizada, você poderá usar a
mutate
função dodplyr
pacote:Resposta antiga com
plyr
pacoteNa minha humilde opinião, a ferramenta mais adequada para a tarefa é
mdply
doplyr
pacote.Exemplo:
Infelizmente, como Bertjan Broeksema apontou, essa abordagem falha se você não usar todas as colunas do quadro de dados na
mdply
chamada. Por exemplo,fonte
dplyr::mutate_each
. Por exemplo:iris %>% mutate_each(funs(half = . / 2),-Species)
.Outros apontaram corretamente que
mapply
é feito para esse fim, mas (por uma questão de completude), um método conceitualmente mais simples é apenas usar umfor
loop.fonte
Muitas funções já são vetorizadas e, portanto, não há necessidade de iterações (
for
loops ou*pply
funções). O seutestFunc
é um exemplo. Você pode simplesmente ligar para:Em geral, eu recomendaria tentar essas abordagens de vetorização primeiro e ver se elas obtêm os resultados pretendidos.
Como alternativa, se você precisar passar vários argumentos para uma função que não é vetorizada,
mapply
pode ser o que você está procurando:fonte
Aqui está uma abordagem alternativa. É mais intuitivo.
Um aspecto-chave que considero que algumas das respostas não levaram em consideração, as quais aponto para a posteridade, é o aplicativo apply (), que permite fazer cálculos de linha com facilidade, mas apenas para dados matriciais (todos numéricos)
operações em colunas ainda são possíveis para quadros de dados:
Para operar em linhas, fazemos a transposição primeiro.
A desvantagem é que acredito que o R fará uma cópia da sua tabela de dados. O que poderia ser um problema de memória. (Isso é realmente triste, porque é programaticamente simples para o tdf ser apenas um iterador do df original, economizando memória, mas R não permite referência a ponteiro ou iterador.)
Além disso, uma pergunta relacionada é como operar em cada célula individual em um quadro de dados.
fonte
Eu vim aqui procurando o nome da função arrumada - que eu sabia que existia. Adicionando isso para (minha) referência futura e para
tidyverse
entusiastas:purrrlyr:invoke_rows
(purrr:invoke_rows
em versões mais antigas).Com a conexão aos métodos estatísticos padrão, como na pergunta original, o pacote da vassoura provavelmente ajudaria.
fonte
A resposta de @ user20877984 é excelente. Como eles resumiram muito melhor do que minha resposta anterior, aqui está minha tentativa (possivelmente ainda péssima) de uma aplicação do conceito:
Usando de
do.call
maneira básica:Trabalhando em um conjunto de dados completo:
lapply
apower.t.test
função para cada uma das linhas de valores especificados:fonte
2
, por que não aplicar apenas sobre1
?data.table
tem uma maneira realmente intuitiva de fazer isso:O
:=
operador pode ser chamado entre colchetes para adicionar uma nova coluna usando uma funçãoTambém é fácil aceitar constantes como argumentos, usando também este método:
fonte
Se as colunas data.frame forem de tipos diferentes,
apply()
há um problema. Uma sutileza sobre a iteração de linha é como aapply(a.data.frame, 1, ...)
conversão implícita de tipos para tipos de caracteres quando colunas são tipos diferentes; por exemplo. uma coluna fator e numérica. Aqui está um exemplo, usando um fator em uma coluna para modificar uma coluna numérica:A subtração falha porque as colunas são convertidas em tipos de caracteres.
Uma correção é converter novamente a segunda coluna em um número:
Mas as conversões podem ser evitadas mantendo as colunas separadas e usando
mapply()
:mapply()
é necessário porque[[ ]]
não aceita um argumento de vetor. Portanto, a iteração da coluna poderia ser feita antes da subtração passando um vetor para[]
, por um código um pouco mais feio:fonte
Uma função muito bom para isso é
adply
a partirplyr
, especialmente se você deseja anexar o resultado para a trama de dados de origem. Esta função e seu primoddply
me salvaram muitas dores de cabeça e linhas de código!Como alternativa, você pode chamar a função que deseja.
fonte