En respuesta a otra pregunta, @Marek publicó la siguiente solución: https://stackoverflow.com/a/10432263/636656
dat <- structure(list(product = c(11L, 11L, 9L, 9L, 6L, 1L, 11L, 5L,
7L, 11L, 5L, 11L, 4L, 3L, 10L, 7L, 10L, 5L, 9L, 8L)), .Names = "product", row.names = c(NA, -20L), class = "data.frame")
`levels<-`(
factor(dat$product),
list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)
)
Que produce como salida:
[1] Generic Generic Bayer Bayer Advil Tylenol Generic Advil Bayer Generic Advil Generic Advil Tylenol
[15] Generic Bayer Generic Advil Bayer Bayer
Esto es solo la impresión de un vector, por lo que para almacenarlo puede hacer lo que es aún más confuso:
res <- `levels<-`(
factor(dat$product),
list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)
)
Claramente, esta es una especie de llamada a la función de niveles, pero no tengo idea de lo que se está haciendo aquí. ¿Cuál es el término para este tipo de hechicería y cómo puedo aumentar mi habilidad mágica en este dominio?
names<-
y[<-
.structure(...)
construcción en lugar de solodata.frame(product = c(11L, 11L, ..., 8L))
? (¡Si hay algo de magia sucediendo allí, me gustaría"levels<-"
función:, unafunction (x, value) .Primitive("levels<-")
especie de comoX %in% Y
es una abreviatura de"%in%"(X, Y)
.Respuestas:
Las respuestas aquí son buenas, pero les falta un punto importante. Déjame intentar describirlo.
R es un lenguaje funcional y no le gusta mutar sus objetos. Pero sí permite declaraciones de asignación, usando funciones de reemplazo:
es equivalente a
El truco es que esta reescritura se realiza mediante
<-
; no lo hacelevels<-
.levels<-
es solo una función regular que toma una entrada y da una salida; no muta nada.Una consecuencia de eso es que, según la regla anterior,
<-
debe ser recursiva:es
es
Es hermoso que esta transformación puramente funcional (hasta el final, donde ocurre la asignación) sea equivalente a lo que sería una asignación en un lenguaje imperativo. Si mal no recuerdo, esta construcción en lenguajes funcionales se llama lente.
Pero luego, una vez que ha definido funciones de reemplazo como
levels<-
, obtiene otra ganancia inesperada: no solo tiene la capacidad de hacer asignaciones, tiene una función útil que toma un factor y da otro factor con diferentes niveles. ¡Realmente no hay nada de "asignación" al respecto!Entonces, el código que estás describiendo solo hace uso de esta otra interpretación de
levels<-
. Admito que el nombrelevels<-
es un poco confuso porque sugiere una tarea, pero esto no es lo que está sucediendo. El código simplemente está configurando una especie de canalización:Empezar con
dat$product
Conviértelo en un factor
Cambiar los niveles
Almacene eso en
res
Personalmente, creo que esa línea de código es hermosa;)
fuente
Sin brujería, así es como se definen las funciones de (sub) asignación.
levels<-
es un poco diferente porque es una primitiva (sub) asignar los atributos de un factor, no los elementos en sí. Hay muchos ejemplos de este tipo de función:Otros operadores binarios también se pueden llamar así:
Ahora que lo sabes, algo como esto realmente debería volar tu mente:
fuente
`levels<-`(foo,bar)
es lo mismo quelevels(foo) <- bar
. Usando el ejemplo de @ Marek:`levels<-`(as.factor(foo),bar)
es lo mismo quefoo <- as.factor(foo); levels(foo) <- bar
.levels<-
realidad es solo una abreviatura deattr<-(x, "levels") <- value
, o al menos probablemente lo fue hasta que se convirtió en una primitiva y se entregó al código C?La razón de esa "magia" es que el formulario de "asignación" debe tener una variable real sobre la que trabajar. Y el
factor(dat$product)
no fue asignado a nada.fuente
within()
y dondetransform()
el objeto así modificado se devuelve y se asigna.Para el código de usuario, me pregunto por qué se usan tales manipulaciones del lenguaje. Pregunta qué magia es esta y otros han señalado que está llamando a la función de reemplazo que tiene el nombre
levels<-
. Para la mayoría de la gente esto es mágico y realmente el uso previsto lo eslevels(foo) <- bar
.El caso de uso que muestra es diferente porque
product
no existe en el entorno global, por lo que solo existe en el entorno local de la llamada, porlevels<-
lo que el cambio que desea realizar no persiste; no hubo reasignación dedat
.En estas circunstancias,
within()
es la función ideal a utilizar. Naturalmente desearía escribiren R pero, por supuesto
product
, no existe como objeto.within()
evita esto porque configura el entorno en el que desea ejecutar su código R y evalúa su expresión dentro de ese entorno. Asignar el objeto de retorno de la llamada awithin()
tiene éxito en el marco de datos correctamente modificado.Aquí hay un ejemplo (no necesita crear uno nuevo
datX
, solo lo hago para que los pasos intermedios permanezcan al final)Lo que da:
Me cuesta ver cómo las construcciones como la que muestra son útiles en la mayoría de los casos: si desea cambiar los datos, cambie los datos, no cree otra copia y cambie eso (que es todo lo que hace la
levels<-
llamada después de todo ).fuente