Em resposta a outra pergunta, @Marek postou a seguinte solução: 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 produz como saída:
[1] Generic Generic Bayer Bayer Advil Tylenol Generic Advil Bayer Generic Advil Generic Advil Tylenol
[15] Generic Bayer Generic Advil Bayer Bayer
Esta é apenas a impressão de um vetor, então, para armazená-lo, você pode fazer o ainda mais confuso:
res <- `levels<-`(
factor(dat$product),
list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)
)
Claramente, isso é algum tipo de chamada para a função de níveis, mas não tenho ideia do que está sendo feito aqui. Qual é o termo para esse tipo de feitiçaria e como faço para aumentar minha habilidade mágica neste domínio?
names<-
e[<-
.structure(...)
construção em vez de apenasdata.frame(product = c(11L, 11L, ..., 8L))
? (Se houver alguma mágica acontecendo lá, eu gostaria de usá-la também!)"levels<-"
funçãofunction (x, value) .Primitive("levels<-")
:, mais ou menos comoX %in% Y
é uma abreviatura de"%in%"(X, Y)
.Respostas:
As respostas aqui são boas, mas estão perdendo um ponto importante. Deixe-me tentar descrever.
R é uma linguagem funcional e não gosta de transformar seus objetos. Mas permite instruções de atribuição, usando funções de substituição:
é equivalente a
O truque é que essa reescrita é feita por
<-
; não é feito porlevels<-
.levels<-
é apenas uma função regular que recebe uma entrada e fornece uma saída; não muda nada.Uma consequência disso é que, de acordo com a regra acima,
<-
deve ser recursiva:é
é
É lindo que essa transformação puramente funcional (até o final, onde a atribuição acontece) seja equivalente ao que seria uma atribuição em uma linguagem imperativa. Se bem me lembro, essa construção em linguagens funcionais é chamada de lente.
Mas então, depois de definir as funções de substituição como
levels<-
, você obtém outro ganho inesperado: você não tem apenas a capacidade de fazer atribuições, você tem uma função útil que leva em um fator e fornece outro fator com níveis diferentes. Não há realmente nada de "atribuição" nisso!Portanto, o código que você está descrevendo está apenas fazendo uso dessa outra interpretação
levels<-
. Admito que o nomelevels<-
é um pouco confuso porque sugere uma atribuição, mas não é isso que está acontecendo. O código é simplesmente configurar uma espécie de pipeline:Começar com
dat$product
Converta em um fator
Mudar os níveis
Armazene isso em
res
Pessoalmente, acho que essa linha de código é linda;)
fonte
Sem feitiçaria, é assim que as funções de (sub) atribuição são definidas.
levels<-
é um pouco diferente porque é um primitivo para (sub) atribuir os atributos de um fator, não os próprios elementos. Existem muitos exemplos desse tipo de função:Outros operadores binários também podem ser chamados assim:
Agora que você sabe disso, algo como isso deve realmente explodir sua mente:
fonte
`levels<-`(foo,bar)
é o mesmo quelevels(foo) <- bar
. Usando o exemplo de @ Marek:`levels<-`(as.factor(foo),bar)
é o mesmo quefoo <- as.factor(foo); levels(foo) <- bar
.levels<-
é apenas uma abreviatura deattr<-(x, "levels") <- value
, ou pelo menos provavelmente era até que se tornasse um primitivo e fosse entregue ao código C.A razão para essa "mágica" é que o formulário de "atribuição" deve ter uma variável real para trabalhar. E
factor(dat$product)
não foi atribuído a nada.fonte
within()
etransform()
chamar onde o objeto modificado dessa forma é retornado e atribuído.Para o código do usuário, eu me pergunto por que tais manipulações de linguagem são usadas assim? Você pergunta que mágica é essa e outros apontaram que você está chamando a função de substituição que tem o nome
levels<-
. Para a maioria das pessoas isso é mágico e realmente o uso pretendido élevels(foo) <- bar
.O caso de uso que você mostra é diferente porque
product
não existe no ambiente global; portanto, só existe no ambiente local da chamada para,levels<-
portanto, a mudança que você deseja fazer não persiste - não houve reatribuição dedat
.Nessas circunstâncias,
within()
é a função ideal a ser usada. Você naturalmente desejaria escreverem R, mas é claro
product
que não existe como um objeto.within()
contorna isso porque configura o ambiente no qual você deseja executar seu código R e avalia sua expressão dentro desse ambiente. Assim, atribuir o objeto de retorno da chamada awithin()
é bem-sucedido no quadro de dados modificado corretamente.Aqui está um exemplo (você não precisa criar um novo
datX
- eu apenas faço isso para que as etapas intermediárias permaneçam no final)Que dá:
Eu me esforço para ver como construções como a que você mostra são úteis na maioria dos casos - se você quiser alterar os dados, alterar os dados, não crie outra cópia e altere isso (que é tudo o que a
levels<-
chamada está fazendo, afinal )fonte