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:
dat <- data.frame(x = 1:4,
y = 5:8)
e gostaríamos de escrever uma função que crie uma nova coluna z
que é a soma das colunas x
e y
.
Um obstáculo muito comum aqui é que uma tentativa natural (mas incorreta) geralmente se parece com isto:
foo <- function(df,col_name,col1,col2){
df$col_name <- df$col1 + df$col2
df
}
#Call foo() like this:
foo(dat,z,x,y)
O problema aqui é que df$col1
não avalia a expressão col1
. Ele simplesmente procura uma coluna df
literalmente chamada col1
. 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:
new_column1 <- function(df,col_name,col1,col2){
#Create new column col_name as sum of col1 and col2
df[[col_name]] <- df[[col1]] + df[[col2]]
df
}
> new_column1(dat,"z","x","y")
x y z
1 1 5 6
2 2 6 8
3 3 7 10
4 4 8 12
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())
:
new_column2 <- function(df,col_name,col1,col2){
col_name <- deparse(substitute(col_name))
col1 <- deparse(substitute(col1))
col2 <- deparse(substitute(col2))
df[[col_name]] <- df[[col1]] + df[[col2]]
df
}
> new_column2(dat,z,x,y)
x y z
1 1 5 6
2 2 6 8
3 3 7 10
4 4 8 12
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:
new_column3 <- function(df,col_name,expr){
col_name <- deparse(substitute(col_name))
df[[col_name]] <- eval(substitute(expr),df,parent.frame())
df
}
Só por diversão, ainda estou usando deparse(substitute())
para o nome da nova coluna. Aqui, todos os itens a seguir funcionarão:
> new_column3(dat,z,x+y)
x y z
1 1 5 6
2 2 6 8
3 3 7 10
4 4 8 12
> new_column3(dat,z,x-y)
x y z
1 1 5 -4
2 2 6 -4
3 3 7 -4
4 4 8 -4
> new_column3(dat,z,x*y)
x y z
1 1 5 5
2 2 6 12
3 3 7 21
4 4 8 32
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 aprofundar eval
, substitute
etc. se você realmente sabe o que está fazendo.