Substitua um valor em um quadro de dados com base em uma instrução condicional (`if`)


122

No quadro de dados R codificado abaixo, gostaria de substituir todas as vezes que B aparecerem b.

junk <- data.frame(x <- rep(LETTERS[1:4], 3), y <- letters[1:12])
colnames(junk) <- c("nm", "val")

isso fornece:

   nm val
1   A   a
2   B   b
3   C   c
4   D   d
5   A   e
6   B   f
7   C   g
8   D   h
9   A   i
10  B   j
11  C   k
12  D   l

Minha tentativa inicial foi usar as instruções a fore ifassim:

for(i in junk$nm) if(i %in% "B") junk$nm <- "b"

mas como tenho certeza de que você pode ver, isso substitui TODOS os valores de junk$nmcom b. Eu posso ver por que isso está fazendo isso, mas não consigo substituí-lo apenas nos casos de lixo eletrônico $ nm onde o valor original estava B.

NOTA: Consegui resolver o problema, gsubmas com o interesse de aprender, o RI ainda gostaria de saber como obter minha abordagem original para o trabalho (se possível)


1
convém adicionar stringsAsFactors = FALSE à construção data.frame original.
jimmyb

@jimmyb Por quê? Fatores são úteis e necessários se alguém estiver modelando com a maioria do código de modelagem de R. A maneira correta de lidar com isso é reconhecer que os dados são um fator. Se você não quiser / precisar dessa conversão, poderá fazer o que quiser. Se você deseja o fator, existem maneiras fáceis de manipular o @Kenny que deseja executar.
Gavin Simpson

1
Portanto, os fatores costumavam ser mais populares por causa do desempenho, agora que as seqüências são imutáveis ​​e dividem o valor dos fatores é menos óbvio, pois a maioria das funcionalidades básicas do R apenas as converte diretamente (embora com avisos). Eu acho que os fatores resultam em um número significativo de bugs que encontro no código R das pessoas.
jimmyb

Respostas:


217

Mais fácil converter nm em caracteres e depois fazer a alteração:

junk$nm <- as.character(junk$nm)
junk$nm[junk$nm == "B"] <- "b"

EDIT: E se você realmente precisa manter nm como fatores, adicione isso no final:

junk$nm <- as.factor(junk$nm)

4
as.character () facilita muito a vida ao trabalhar com fatores. +1
Brandon Bertelsen

4
e se você tiver várias colunas?
Geodex #

43

outra maneira útil de substituir valores

library(plyr)
junk$nm <- revalue(junk$nm, c("B"="b"))

25

Resposta curta é:

junk$nm[junk$nm %in% "B"] <- "b"

Dê uma olhada nos vetores Index na Introdução R (se você ainda não o leu).


EDITAR. Conforme observado nos comentários, esta solução funciona para vetores de caracteres, portanto falhe em seus dados.

Para fator melhor maneira é mudar de nível:

levels(junk$nm)[levels(junk$nm)=="B"] <- "b"

Breve adição: o uso de% em% realmente ajuda realmente se você tiver um conjunto no lado direito, como c("B","C"). Fazer junk$nm[junk$nm == "B"]é o melhor caminho.
Thilo

1
Ah, outra adição importante: fazê-lo dessa maneira exige primeiro adicionar o nível bdo fator ao fator nm. A versão de diliop é de fato a melhor se você quiser trabalhar com personagens, não com fatores. (Sempre pensar sobre o tipo suas variáveis têm em primeiro lugar!)
Thilo

isso não funciona nos dados criados por @Kenny porque os dados são fatores. Você esqueceu uma etapa ou tem a configuração global para parar de converter caracteres em fatores?
Gavin Simpson

4
@ Thilo Uma das diferenças importantes entre %in%e ==está NAlidando: c(1,2,NA)==1TRUE, FALSE, NAmas c(1,2,NA) %in% 1TRUE, FALSE, FALSE. E sim, eu esqueci de verificar se este trabalho: /
Marek

20

Como os dados que você mostra são fatores, isso complica um pouco as coisas. A resposta de @ diliop aborda o problema convertendo nmpara uma variável de caractere. Para voltar aos fatores originais, é necessária uma etapa adicional.

Uma alternativa é manipular os níveis do fator em vigor.

> lev <- with(junk, levels(nm))
> lev[lev == "B"] <- "b"
> junk2 <- within(junk, levels(nm) <- lev)
> junk2
   nm val
1   A   a
2   b   b
3   C   c
4   D   d
5   A   e
6   b   f
7   C   g
8   D   h
9   A   i
10  b   j
11  C   k
12  D   l

Isso é bastante simples e muitas vezes esqueço que existe uma função de substituição para levels().

Edit: Como observado por @Seth nos comentários, isso pode ser feito em uma linha, sem perda de clareza:

within(junk, levels(nm)[levels(nm) == "B"] <- "b")

6
Agradável. Eu não sabia sobre a função de substituição para levels(). E o liner junk <- within(junk, levels(nm)[levels(nm)=="B"] <- "b")?

Mas você chamá-lo duas vezes :)
Marek

2
@Marek um tapa na cabeça Apenas mostra que não se deve responder aos comentários sobre o SO quando já é hora de dormir. Vamos tentar isso de novo ...
Gavin Simpson

@Eth De fato - bom. Não sabe por que separei as etapas? Talvez por exposição ...
Gavin Simpson

11

A maneira mais fácil de fazer isso em um comando é usar o whichcomando e também não precisar alterar os fatores em caracteres, fazendo o seguinte:

junk$nm[which(junk$nm=="B")]<-"b"

5

Você criou uma variável de fator nmpara evitar evitar isso ou adicionar um nível adicional aos atributos do fator. Você também deve evitar usar <-os argumentos para data.frame ()

Opção 1:

junk <- data.frame(x = rep(LETTERS[1:4], 3), y =letters[1:12], stringsAsFactors=FALSE)
junk$nm[junk$nm == "B"] <- "b"

Opção 2:

levels(junk$nm) <- c(levels(junk$nm), "b")
junk$nm[junk$nm == "B"] <- "b"
junk

@DWin, obrigado por sua opinião sobre o problema e pela necessidade de considerar o tipo de variável. Aceitei a resposta da @ diliop porque era a primeira que funcionava. Eu sei que existem muitos problemas em relação a <- vs = mas (se puder ser respondido brevemente) por que = deve ser usado data.frame?
precisa saber é o seguinte

Você não precisa adicionar bcomo um nível, basta alterar o nível que é Ba b.
Gavin Simpson

@KennyPeanuts: o nome da coluna é um problema, veja a <- data.frame(x<-1:10). O nome da coluna não é xapenas uma bagunça x....1.10. Melhor usar data.frame (x = 1: 10). Então você sabe qual é o nome da sua coluna.
IRTFM

@ Gavin: Mais fácil de adicionar do que substituir, e ainda mais fácil para não torná-lo um fator.
IRTFM

@Dwin é mais fácil? Eu discordo - veja minha resposta para algo simples. A adição de níveis pode surpreendê-lo, digamos, na modelagem com a predict()qual reclamará se os níveis de fatores em novos dados não corresponderem aos usados ​​para se ajustar ao modelo. Mais limpo a longo prazo para obter os dados formatados conforme desejado, do que depender de atalhos. Concordo que pode ser mais fácil para não torná-lo um fator, mas se ele já é um, ou precisa ser um para algum exercício de modelagem ...
Gavin Simpson

1

Se você estiver trabalhando com variáveis ​​de caractere (observe que stringsAsFactorsé falso aqui), poderá usar replace:

junk <- data.frame(x <- rep(LETTERS[1:4], 3), y <- letters[1:12], stringsAsFactors = FALSE)
colnames(junk) <- c("nm", "val")

junk$nm <- replace(junk$nm, junk$nm == "B", "b")
junk
#    nm val
# 1   A   a
# 2   b   b
# 3   C   c
# 4   D   d
# ...

0
stata.replace<-function(data,replacevar,replacevalue,ifs) {
  ifs=parse(text=ifs)
  yy=as.numeric(eval(ifs,data,parent.frame()))
  x=sum(yy)
  data=cbind(data,yy)
  data[yy==1,replacevar]=replacevalue
  message=noquote(paste0(x, " replacement are made"))
  print(message)
  return(data[,1:(ncol(data)-1)])
}

Chame esta função usando a linha abaixo.

d=stata.replace(d,"under20",1,"age<20")
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.