Zastąp wartość w ramce danych na podstawie instrukcji warunkowej („if”)


122

W ramce danych R zakodowanej poniżej chciałbym zamienić wszystkie czasy, które B pojawiają się na b.

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

to zapewnia:

   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

Moja pierwsza próba polegała na użyciu a fori iftakich stwierdzeń:

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

ale jak na pewno widzisz, zastępuje to WSZYSTKIE wartości junk$nmz b. Rozumiem, dlaczego tak się dzieje, ale nie wydaje mi się, aby zastępował tylko te przypadki złomu $ nm, w których była pierwotna wartość B.

UWAGA: udało mi się rozwiązać problem, gsubale w interesie nauki RI nadal chciałbym wiedzieć, jak uzyskać moje oryginalne podejście do pracy (jeśli jest to możliwe)


1
możesz chcieć dodać stringiAsFactors = FALSE do oryginalnej konstrukcji data.frame.
jimmyb

@jimmyb Why? Czynniki są przydatne i konieczne, jeśli modeluje się za pomocą większości kodu modelowania języka R. Prawidłowym sposobem radzenia sobie z tym jest uznanie, że dane są czynnikiem. Jeśli nie chcesz / potrzebujesz tej konwersji, możesz zrobić, co mówisz. Jeśli chcesz mieć ten czynnik, istnieją proste sposoby na wykonanie manipulacji, którą @Kenny chce wykonać.
Gavin Simpson

1
Tak więc czynniki były bardziej popularne ze względu na wydajność, jednak teraz, gdy ciągi znaków są niezmienne i haszowane, wartość współczynników jest mniej oczywista, ponieważ większość podstawowych funkcji języka R po prostu je konwertuje (aczkolwiek z ostrzeżeniami) bezpośrednio. Myślę, że czynniki powodują znaczną liczbę błędów, które znajduję w ludzkim kodzie R.
jimmyb

Odpowiedzi:


217

Łatwiej przekonwertować nm na znaki, a następnie wprowadzić zmianę:

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

EDYCJA: A jeśli rzeczywiście musisz zachować nm jako czynniki, dodaj to na końcu:

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

4
as.character () znacznie ułatwia życie podczas pracy z czynnikami. +1
Brandon Bertelsen

4
co jeśli masz wiele kolumn?
geodex

43

kolejny przydatny sposób zastępowania wartości

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

25

Krótka odpowiedź brzmi:

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

Spójrz na wektory indeksu w Wprowadzenie do języka R (jeśli jeszcze tego nie czytałeś).


EDYTOWAĆ. Jak zauważono w komentarzach, to rozwiązanie działa dla wektorów znaków, więc nie działa na danych.

Najlepszym sposobem jest zmiana poziomu:

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

Krótki dodatek: użycie% w% naprawdę pomaga tylko wtedy, gdy masz zestaw po prawej stronie, jak c("B","C"). Robi junk$nm[junk$nm == "B"]to lepszy sposób.
Thilo

1
Aha, kolejny ważny dodatek: zrobienie tego w ten sposób wymaga najpierw dodania poziomu czynnika bdo współczynnika nm. Wersja diliopa jest w rzeczywistości lepsza, jeśli chcesz pracować z postaciami, a nie czynnikami. (Zawsze myśl najpierw o typie, jaki mają twoje zmienne!)
Thilo

to nie działa na danych utworzonych przez @Kenny, ponieważ dane są czynnikami. Czy zapomniałeś o kroku lub masz globalne ustawienie, aby przestać konwertować znaki na czynniki?
Gavin Simpson

4
@Thilo Jedną z ważnych różnic między %in%i ==jest NAobsługa: c(1,2,NA)==1daje, TRUE, FALSE, NAale c(1,2,NA) %in% 1daje TRUE, FALSE, FALSE. I tak zapomniałem sprawdzić czy to działa: /
Marek

20

Ponieważ dane, które pokazujesz, są czynnikami, komplikuje to trochę sprawę. Odpowiedź @ diliopa rozwiązuje problem, przekształcając nmją w zmienną znakową . Aby wrócić do pierwotnych czynników, wymagany jest kolejny krok.

Alternatywą jest manipulowanie istniejącymi poziomami czynnika.

> 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

Jest to dość proste i często zapominam, że istnieje funkcja zastępująca levels().

Edycja: jak zauważył @Seth w komentarzach, można to zrobić w jednej linijce, bez utraty przejrzystości:

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

6
Miły. Nie wiedziałem o funkcji zamiany dla levels(). A co z jednym wkładem junk <- within(junk, levels(nm)[levels(nm)=="B"] <- "b")?

Ale dzwonisz dwa razy :)
Marek

2
@Marek uderza w głowę Po prostu pokazuje, że nie należy odpowiadać na komentarze dotyczące SO, gdy jest już dobrze po spaniu. Spróbujmy jeszcze raz ...
Gavin Simpson,

@Seth Rzeczywiście - miło. Nie wiesz, dlaczego oddzieliłem kroki? Może na wystawę ...
Gavin Simpson

11

Najłatwiej to zrobić w jednym poleceniu, używając whichpolecenia, a także nie trzeba zmieniać współczynników na charakter, wykonując następujące czynności:

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

5

Utworzyłeś zmienną czynnikową w programie, nmwięc musisz albo tego unikać, albo dodać dodatkowy poziom do atrybutów współczynnika. Należy również unikać używania <-w argumentach funkcji data.frame ()

Opcja 1:

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

Opcja 2:

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

@DWin dziękuję za wkład w problem i konieczność rozważenia rodzaju zmiennej. Przyjąłem odpowiedź @ diliop, ponieważ była to pierwsza działająca. Wiem, że istnieje wiele problemów dotyczących <- vs =, ale (jeśli można na nie krótko odpowiedzieć), dlaczego należy używać = data.frame?
DQdlM

Nie trzeba dodawać b, jak najwyższym poziomie, wystarczy zmienić poziom, który jest Bdo b.
Gavin Simpson

@KennyPeanuts: nazwa kolumny to jeden problem, spójrz na a <- data.frame(x<-1:10). Nazwa jego kolumny nie jest, xale raczej niechlujna x....1.10. Lepiej użyć data.frame (x = 1:10). Wtedy wiesz, jaka jest nazwa twojej kolumny.
IRTFM

@Gavin: Łatwiej jest dodawać niż wymieniać, a jeszcze łatwiej nie robić z tego żadnego znaczenia.
IRTFM

@Dwin Łatwiej? Nie zgadzam się - zobacz moją odpowiedź na coś prostego. Dodawanie poziomów może Cię zaskoczyć, na przykład w modelowaniu, na predict()które będzie narzekać, jeśli poziomy czynników w nowych danych nie będą pasować do tych, które były używane do dopasowania modelu. Czystsze na dłuższą metę, aby poprawnie sformatować dane, niż polegać na skrótach. Zgadzam się, że może być łatwiej nie robić tego czynnikiem, ale jeśli już nim jest, lub musi nim być do jakiegoś ćwiczenia modelarskiego ...
Gavin Simpson

1

Jeśli pracujesz ze zmiennymi znakowymi (zwróć uwagę, że stringsAsFactorstutaj jest fałsz), możesz użyć zamiany:

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

Wywołaj tę funkcję za pomocą poniższej linii.

d=stata.replace(d,"under20",1,"age<20")
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.