Esta respuesta va a cubrir muchos de los mismos elementos que las respuestas existentes, pero este tema (que pasa nombres de columna a funciones) surge a menudo suficiente que yo quería que hubiera una respuesta que cubría las cosas un poco más exhaustivamente.
Supongamos que tenemos una muy simple trama de datos:
dat <- data.frame(x = 1:4,
y = 5:8)
y nos gustaría escribir una función que crea una nueva columna z
que es la suma de las columnas y x
y
.
Un obstáculo muy común aquí es que un natural (pero incorrecta) tratan a menudo se ve así:
foo <- function(df,col_name,col1,col2){
df$col_name <- df$col1 + df$col2
df
}
#Call foo() like this:
foo(dat,z,x,y)
El problema aquí es que df$col1
no evalúa la expresión col1
. Simplemente busca una columna en df
literalmente llamada col1
. Este comportamiento se describe en ?Extract
en la sección "Objetos recursivos (similares a listas)".
El más simple, y la solución más recomendado es simplemente cambiar del $
a [[
y pasar los argumentos de la función como cadenas:
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
Esto a menudo se considera "mejores prácticas" ya que es el método que sea más difícil meter la pata. Pasar los nombres de las columnas como cadenas es tan inequívoco como puede obtener.
Las siguientes dos opciones son más avanzadas. Muchos paquetes populares hacen uso de este tipo de técnicas, pero usarlos y requiere más cuidado y habilidad, ya que pueden introducir complejidades sutiles y puntos de falla imprevistos. This sección del libro Hadley's Advanced R es una excelente referencia para algunos de estos problemas.
Si realmente desea guardar el usuario escriba todas esas cotizaciones, una opción podría ser para convertir nombres de las columnas desnudas, sin cotización a cadenas 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
Esto es, francamente, un poco tonto probablemente, ya que realmente estamos haciendo lo mismo que en new_column1
, simplemente con un montón de trabajo extra para convertir nombres desnudos en cadenas.
Por último, si queremos obtener realmente de lujo, podríamos decidir que en lugar de pasar los nombres de dos columnas para agregar, nos gustaría ser más flexibles y permitir otras combinaciones de dos variables. En ese caso estaríamos propensos recurrir al uso de eval()
en una expresión que incluya las dos columnas:
new_column3 <- function(df,col_name,expr){
col_name <- deparse(substitute(col_name))
df[[col_name]] <- eval(substitute(expr),df,parent.frame())
df
}
Sólo por diversión, todavía estoy usando deparse(substitute())
para el nombre de la nueva columna. Aquí, todo el siguiente trabajo:
> 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
Así que la respuesta corta es básicamente: Pase data.frame nombres de columna como cadenas y utilizar [[
para seleccionar columnas individuales.Solo comience a profundizar en eval
, substitute
, etc. si realmente sabe lo que está haciendo.
¿Hay alguna manera de pasar el nombre de la columna no como una cadena? – kmm
Debe pasar el nombre de columna citado como carácter o el índice entero para la columna. Solo pasar 'B' supondrá que B es un objeto en sí mismo. – Shane
Ya veo. No estoy seguro de cómo terminé con el complicado sustituto, eval, etc. – kmm