Estou tentando escrever uma função para aceitar um data.frame ( x
) e um column
dele. A função executa alguns cálculos em xe posteriormente retorna outro data.frame. Estou preso no método de práticas recomendadas para passar o nome da coluna para a função.
Os dois exemplos mínimos fun1
e fun2
abaixo produzem o resultado desejado, podendo realizar operações no x$column
, utilizando max()
como exemplo. No entanto, ambos contam com o aparentemente (pelo menos para mim) deselegante
- ligue para
substitute()
e possivelmenteeval()
- a necessidade de passar o nome da coluna como um vetor de caracteres.
fun1 <- function(x, column){
do.call("max", list(substitute(x[a], list(a = column))))
}
fun2 <- function(x, column){
max(eval((substitute(x[a], list(a = column)))))
}
df <- data.frame(B = rnorm(10))
fun1(df, "B")
fun2(df, "B")
Gostaria de poder chamar a função como fun(df, B)
, por exemplo. Outras opções que considerei, mas não tentei:
- Passe
column
como um inteiro do número da coluna. Acho que isso evitariasubstitute()
. Idealmente, a função poderia aceitar qualquer um. with(x, get(column))
, mas, mesmo que funcione, acho que ainda exigiriasubstitute
- Faça uso de
formula()
ematch.call()
, nenhum dos quais tenho muita experiência.
Subquestão : É do.call()
preferível eval()
?
B
para supor que B é um objeto em si.[[
solução era a única que funcionava para mim.Essa resposta abrangerá muitos dos mesmos elementos das respostas existentes, mas esse problema (passar nomes de colunas para funções) surge com frequência o suficiente para que houvesse uma resposta que abrangesse as coisas de forma um pouco mais abrangente.
Suponha que temos um quadro de dados muito simples:
e gostaríamos de escrever uma função que crie uma nova coluna
z
que é a soma das colunasx
ey
.Um obstáculo muito comum aqui é que uma tentativa natural (mas incorreta) geralmente se parece com isto:
O problema aqui é que
df$col1
não avalia a expressãocol1
. Ele simplesmente procura uma colunadf
literalmente chamadacol1
. Este comportamento é descrito em?Extract
seção "Objetos recursivos (semelhantes a listas)".A solução mais simples e mais frequentemente recomendada é simplesmente alternar de
$
para[[
e passar os argumentos da função como strings:Isso geralmente é considerado a "melhor prática", pois é o método mais difícil de errar. Passar os nomes das colunas como strings é o mais inequívoco que você pode imaginar.
As duas opções a seguir são mais avançadas. Muitos pacotes populares fazem uso desses tipos de técnicas, mas usá-los bem requer mais cuidado e habilidade, pois podem apresentar complexidades sutis e pontos de falha imprevistos. Esta seção do livro Advanced R de Hadley é uma excelente referência para alguns desses problemas.
Se você realmente deseja evitar que o usuário digite todas as aspas, uma opção pode ser converter os nomes das colunas vazias e não citadas em strings usando
deparse(substitute())
:Isso é, francamente, um pouco bobo provavelmente, já que estamos realmente fazendo a mesma coisa que em
new_column1
, apenas com um monte de trabalho extra para converter nomes simples em strings.Finalmente, se quisermos ser realmente sofisticados, podemos decidir que, em vez de passar os nomes de duas colunas a serem adicionadas, gostaríamos de ser mais flexíveis e permitir outras combinações de duas variáveis. Nesse caso, provavelmente recorreríamos ao uso
eval()
de uma expressão envolvendo as duas colunas:Só por diversão, ainda estou usando
deparse(substitute())
para o nome da nova coluna. Aqui, todos os itens a seguir funcionarão:Portanto, a resposta curta é basicamente: passe os nomes das colunas data.frame como strings e use
[[
para selecionar colunas únicas. Apenas começar a se aprofundareval
,substitute
etc. se você realmente sabe o que está fazendo.fonte
Pessoalmente, acho que passar a coluna como uma string é muito feio. Eu gosto de fazer algo como:
que renderá:
Observe como a especificação de um data.frame é opcional. você pode até trabalhar com funções de suas colunas:
fonte
Outra maneira é usar a
tidy evaluation
abordagem. É muito simples passar colunas de um quadro de dados como strings ou nomes de coluna vazios. Veja mais sobretidyeval
aqui .Use os nomes das colunas como strings
Use nomes de coluna simples
Criado em 01/03/2019 pelo pacote reprex (v0.2.1.9000)
fonte
Como um pensamento extra, se for necessário passar o nome da coluna sem aspas para a função personalizada, talvez
match.call()
possa ser útil também neste caso, como uma alternativa paradeparse(substitute())
:Se houver um erro de digitação no nome da coluna, seria mais seguro parar com um erro:
Criado em 11/01/2019 pelo pacote reprex (v0.2.1)
Não acho que usaria essa abordagem, pois há digitação e complexidade extras do que apenas passar o nome da coluna citada conforme apontado nas respostas acima, mas bem, é uma abordagem.
fonte