Заменить значение во фрейме данных на основе условного оператора (`if`)

Во фрейме данных R, кодированном ниже, я хотел бы заменить все случаи появления B на b.

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

это обеспечивает:

   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

Моя первоначальная попытка заключалась в использовании таких операторов for и if:

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

но, как я уверен, вы можете видеть, это заменяет ВСЕ значения junk$nm на b. Я понимаю, почему это происходит, но я не могу заставить его заменить только те случаи нежелательной почты $ nm, где исходное значение было B.

ПРИМЕЧАНИЕ. Мне удалось решить проблему с gsub, но в интересах изучения R я все еще хотел бы знать, как заставить работать мой оригинальный подход (если это возможно)


person DQdlM    schedule 28.04.2011    source источник
comment
вы можете добавить stringsAsFactors = FALSE к исходной конструкции data.frame.   -  person jimmyb    schedule 29.04.2011
comment
@jimmyb Почему? Факторы полезны и необходимы, если вы моделируете большую часть кода моделирования R. Правильный способ справиться с этим - признать, что данные являются фактором. Если вы не хотите / не нуждаетесь в этом преобразовании, вы можете делать то, что говорите. Если вам нужен фактор, то есть простые способы выполнить манипуляции, которые хочет выполнить @Kenny.   -  person Gavin Simpson    schedule 29.04.2011
comment
Таким образом, раньше факторы были более популярными из-за производительности, однако теперь, когда строки неизменяемы и хэшируются, значение факторов менее очевидно, поскольку большая часть базовых функций R просто преобразует их (хотя и с предупреждениями) напрямую. Я думаю, что факторы приводят к значительному количеству ошибок, которые я нахожу в коде R людей.   -  person jimmyb    schedule 29.04.2011


Ответы (10)


Проще преобразовать нм в символы, а затем внести изменения:

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

РЕДАКТИРОВАТЬ: И если вам действительно нужно поддерживать нм как факторы, добавьте это в конце:

junk$nm <- as.factor(junk$nm)
person diliop    schedule 28.04.2011
comment
as.character () значительно упрощает жизнь при работе с факторами. +1 - person Brandon Bertelsen; 29.04.2011
comment
что, если у вас несколько столбцов? - person geodex; 20.04.2015
comment
@diliop: Спасибо за это: что, если я захочу изменить нежелательную $ nm, если она принимает значения B, Y, Z, ...? - person simo; 30.03.2021

еще один полезный способ замены значений

library(plyr)
junk$nm <- revalue(junk$nm, c("B"="b"))
person Oriol Prat    schedule 14.12.2013

Короткий ответ:

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

Взгляните на Векторы индекса в R Введение (если вы еще не читали).


РЕДАКТИРОВАТЬ. Как отмечалось в комментариях, это решение работает для символьных векторов, поэтому не справляйтесь с вашими данными.

Для фактора лучше всего изменить уровень:

levels(junk$nm)[levels(junk$nm)=="B"] <- "b"
person Marek    schedule 28.04.2011
comment
Краткое дополнение: использование% в% действительно помогает, только если у вас есть набор с правой стороны, как c("B","C"). Лучше делать junk$nm[junk$nm == "B"]. - person Thilo; 29.04.2011
comment
О, еще одно важное дополнение: чтобы сделать это так, нужно сначала добавить коэффициент b к коэффициенту nm. Версия diliop на самом деле лучше, если вы хотите работать с персонажами, а не с факторами. (Всегда думайте о типе ваших переменных в первую очередь!) - person Thilo; 29.04.2011
comment
это не работает с данными, созданными @Kenny, потому что данные являются факторами. Вы забыли шаг или у вас есть глобальная настройка, чтобы остановить преобразование символов в множители? - person Gavin Simpson; 29.04.2011
comment
@Thilo Одно из важных различий между %in% и == - это NA обработка: c(1,2,NA)==1 дает TRUE, FALSE, NA, а c(1,2,NA) %in% 1 дает TRUE, FALSE, FALSE. И да, я забыл проверить, работает ли это: / - person Marek; 29.04.2011

Поскольку данные, которые вы показываете, являются факторами, это немного усложняет ситуацию. Ответ @diliop решает проблему путем преобразования nm в символьную переменную. Чтобы вернуться к исходным факторам, необходим следующий шаг.

Альтернативой является манипулирование уровнями действующего фактора.

> 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

Это довольно просто, и я часто забываю, что есть функция замены для levels().

Изменить: как отметил @Seth в комментариях, это можно сделать однострочно без потери ясности:

within(junk, levels(nm)[levels(nm) == "B"] <- "b")
person Gavin Simpson    schedule 28.04.2011
comment
Отлично. Я не знал о функции замены для levels(). Как насчет одного лайнера junk <- within(junk, levels(nm)[levels(nm)=="B"] <- "b")? - person ; 29.04.2011
comment
@Marek хлопает головой Просто показывает, что не следует отвечать на комментарии к SO, когда уже давно пора спать. Давай попробуем еще раз ... - person Gavin Simpson; 29.04.2011
comment
@Seth Действительно - приятно. Не знаете, почему я разделил ступени? Возможно для экспозиции ... - person Gavin Simpson; 29.04.2011

Самый простой способ сделать это с помощью одной команды - использовать команду which, а также не нужно преобразовывать множители в символы, выполнив следующие действия:

junk$nm[which(junk$nm=="B")]<-"b"
person user1021713    schedule 07.01.2012

Вы создали факторную переменную в nm, поэтому вам нужно либо избегать этого, либо добавить дополнительный уровень к атрибутам фактора. Вам также следует избегать использования <- в аргументах data.frame ()

Опция 1:

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

Вариант 2:

levels(junk$nm) <- c(levels(junk$nm), "b")
junk$nm[junk$nm == "B"] <- "b"
junk
person IRTFM    schedule 28.04.2011
comment
@DWin благодарит за ваш вклад в решение проблемы и за необходимость учитывать тип переменной. Я принял ответ @diliop, потому что он был первым рабочим. Я знаю, что есть много проблем с ‹- vs =, но (если можно ответить кратко), почему = следует использовать с data.frame? - person DQdlM; 29.04.2011
comment
Вам не нужно добавлять b в качестве уровня, просто измените уровень с B на b. - person Gavin Simpson; 29.04.2011
comment
@KennyPeanuts: название столбца - это одна из проблем, посмотрите на a <- data.frame(x<-1:10). Имя его столбца не x, а скорее беспорядочное x....1.10. Лучше использовать data.frame (x = 1:10). Тогда вы знаете, как называется ваш столбец. - person IRTFM; 29.04.2011
comment
@Gavin: Легче добавить, чем заменить, и еще проще не делать этого фактором. - person IRTFM; 29.04.2011
comment
@Dwin Проще? Я не согласен - см. Мой ответ для чего-то простого. Добавление уровней может вас уловить, скажем, при моделировании с predict(), которое будет жаловаться, если уровни факторов в новых данных не совпадают с теми, которые используются для соответствия модели. Более чистый в долгосрочной перспективе, чтобы данные отформатированы так, как вы хотите, должным образом, чем полагаться на короткие пути. Я согласен, что было бы проще не делать это фактором, но если он уже есть, или должен быть им для некоторых упражнений по моделированию ... - person Gavin Simpson; 29.04.2011
comment
@Gavin: Не мог бы предсказать ошибку при либо добавленном или замененном уровне? - person IRTFM; 29.04.2011
comment
@DWin зависит от того, какую функцию моделирования вы используете. С lm() добавление или замена уровня работает одинаково хорошо. Скажем, rpart(), если уровни не точно одинаковы, произойдет сбой. Так что это действительно зависит от того, какие функции вы используете, но мы можем сказать, что если вы структурируете свои данные так, как вы хотите, до любого моделирования, тогда predict() всегда будет работать. - person Gavin Simpson; 29.04.2011
comment
@DWin, спасибо за разъяснения по поводу = vs ‹-, что имеет смысл. Пока что я узнал много неожиданного из комментариев ... это здорово. - person DQdlM; 29.04.2011

Если вы работаете с символьными переменными (обратите внимание, что stringsAsFactors здесь false), вы можете использовать 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
# ...
person loki    schedule 20.02.2018
comment
Это работает для всех типов переменных. Я просто использовал его для вставки NA по определенным индексам в целочисленный вектор. - person Jonas Lindeløv; 20.10.2020

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)])
}

Вызовите эту функцию, используя строку ниже.

d=stata.replace(d,"under20",1,"age<20")
person Devendra Karanjit    schedule 08.04.2019

Вы также можете использовать ifelse, что очень просто для понимания

junk$val <- ifelse(junk$nm == "B", "b", junk$val)

Если вы все еще хотите сделать это через for loop правильный способ сделать это

for(i in 1:nrow(junk)){
  if(junk[i, "nm"] == "B"){
    junk[i, "val"] <- "b"
  }
}

junk
> junk
   nm val
1   A   a
2   B   b
3   C   c
4   D   d
5   A   e
6   B   b
7   C   g
8   D   h
9   A   i
10  B   b
11  C   k
12  D   l
person AnilGoyal    schedule 31.03.2021

У меня такая же проблема, вы также можете сделать то же самое для каждого столбца,

 fix_junk <- function(x){
      #x <- as.character(x)
      x[x == "B"] <- "b"
      x
    }
    junk[] <- lapply(junk, fix_junk); junk # junk[] to get a data frame rather than a list
    junk[1:3] <- lapply(junk[1:3], fix_junk); junk
person Seyma Kalay    schedule 16.04.2021