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


122

В кадре данных 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 я все еще хотел бы знать, как заставить мой оригинальный подход работать (если это возможно)


1
вы можете добавить stringsAsFactors = FALSE к исходной конструкции data.frame.
Джиммиб

@jimmyb Почему? Факторы полезны и необходимы, если вы моделируете большую часть кода моделирования R. Правильный способ справиться с этим - признать, что данные являются фактором. Если вы не хотите / не нуждаетесь в этом преобразовании, вы можете делать то, что говорите. Если вам нужен фактор, то есть простые способы выполнить манипуляции, которые хочет выполнить @Kenny.
Гэвин Симпсон

1
Таким образом, раньше факторы были более популярными из-за производительности, однако теперь, когда строки неизменяемы и хэширование, значение факторов менее очевидно, поскольку большая часть базовых функций R просто преобразует их (хотя и с предупреждениями) напрямую. Я думаю, что факторы приводят к значительному количеству ошибок, которые я нахожу в коде R людей.
jimmyb

Ответы:


217

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

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

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

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

4
as.character () значительно упрощает жизнь при работе с факторами. +1
Брэндон Бертельсен

4
что, если у вас несколько столбцов?
geodex


25

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

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

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


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

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

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

Краткое добавление: использование% в% действительно помогает, только если у вас есть набор с правой стороны, как c("B","C"). Делать junk$nm[junk$nm == "B"]- лучший способ.
Тило

1
О, еще одно важное дополнение: чтобы сделать это так, нужно сначала добавить уровень фактора bк коэффициенту nm. Версия diliop на самом деле лучше, если вы хотите работать с персонажами, а не с факторами. (Всегда думайте о типе ваших переменных в первую очередь!)
Тило

это не работает с данными, созданными @Kenny, потому что данные являются факторами. Вы забыли шаг или у вас есть глобальная настройка, чтобы остановить преобразование символов в множители?
Гэвин Симпсон

4
@Thilo Одно из важных различий между %in%и ==заключается в NAобработке: c(1,2,NA)==1дает, TRUE, FALSE, NAно c(1,2,NA) %in% 1дает TRUE, FALSE, FALSE. И да, я забыл проверить, работает ли это: /
Марек

20

Поскольку данные, которые вы показываете, являются факторами, это немного усложняет ситуацию. Ответ @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")

6
Ницца. Я не знал о функции замены для levels(). Как насчет одного лайнера junk <- within(junk, levels(nm)[levels(nm)=="B"] <- "b")?

Но вы называете это дважды :)
Марек

2
@Marek хлопает по голове Просто показывает, что нельзя отвечать на комментарии к SO, когда уже давно пора спать. Давай попробуем еще раз ...
Гэвин Симпсон

@Seth Действительно - приятно. Не знаете, почему я разделил ступени? Возможно для экспозиции ...
Гэвин Симпсон

11

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

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

5

Вы создали факторную переменную, 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

@DWin благодарит вас за ваш вклад в решение проблемы и за необходимость учитывать тип переменной. Я принял ответ @diliop, потому что он был первым рабочим. Я знаю, что есть много проблем с <- vs =, но (если можно кратко ответить), почему = следует использовать с data.frame?
DQdlM

Вам не нужно добавлять bкак уровень, просто измените уровень Bдо b.
Гэвин Симпсон

@KennyPeanuts: название столбца - одна проблема, посмотрите a <- data.frame(x<-1:10). Его имя столбца не xа грязный x....1.10. Лучше использовать data.frame (x = 1:10). Тогда вы знаете, как называется ваш столбец.
IRTFM

@Gavin: Легче добавить, чем заменить, и еще проще не делать этого фактором.
IRTFM

@Dwin Проще? Я не согласен - см. Мой ответ для чего-то простого. Добавление уровней может вас уловить, скажем, при моделировании, при predict()котором вы будете жаловаться, если уровни факторов в новых данных не совпадают с теми, которые используются для соответствия модели. Более чистый в долгосрочной перспективе, чтобы данные отформатированы так, как вы хотите, должным образом, чем полагаться на короткие пути. Я согласен, что было бы проще не делать это фактором, но если он уже есть, или должен быть одним из них для некоторых упражнений по моделированию ...
Гэвин Симпсон

1

Если вы работаете с символьными переменными (обратите внимание, что stringsAsFactorsздесь неверно), вы можете использовать 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)])
}

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

d=stata.replace(d,"under20",1,"age<20")
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.