Thay thế một giá trị trong khung dữ liệu dựa trên câu lệnh có điều kiện (`if`)


122

Trong khung dữ liệu R được mã hóa cho bên dưới, tôi muốn thay thế tất cả thời gian B xuất hiện bằng b.

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

điều này cung cấp:

   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

Nỗ lực ban đầu của tôi là sử dụng a forvà các ifcâu lệnh như sau:

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

nhưng như tôi chắc chắn bạn có thể thấy, điều này thay thế TẤT CẢ các giá trị của junk$nmwith b. Tôi có thể hiểu lý do tại sao điều này đang làm điều này nhưng tôi dường như không thể làm cho nó chỉ thay thế những trường hợp $ nm rác có giá trị ban đầu B.

LƯU Ý: Tôi đã giải quyết được vấn đề gsubnhưng vì quan tâm đến việc học, RI vẫn muốn biết cách để cách tiếp cận ban đầu của tôi hoạt động (nếu có thể)


1
bạn có thể muốn thêm stringAsFactors = FALSE vào cấu trúc data.frame ban đầu.
jimmyb

@jimmyb Tại sao? Các yếu tố hữu ích và cần thiết nếu một người đang lập mô hình với hầu hết mã mô hình của R. Cách chính xác để giải quyết vấn đề này là thừa nhận rằng dữ liệu là một yếu tố. Nếu bạn không muốn / cần chuyển đổi này thì bạn có thể làm như bạn nói. Nếu bạn muốn có yếu tố, thì có nhiều cách dễ dàng để thực hiện thao tác mà @Kenny muốn thực hiện.
Gavin Simpson

1
Vì vậy, các yếu tố được sử dụng để phổ biến hơn vì hiệu suất, tuy nhiên, bây giờ các chuỗi là bất biến và giá trị băm của các yếu tố ít rõ ràng hơn, vì hầu hết chức năng R cơ sở sẽ chỉ chuyển đổi chúng (mặc dù có cảnh báo) trực tiếp. Tôi nghĩ rằng các yếu tố dẫn đến một số lỗi đáng kể mà tôi tìm thấy trong mã R của mọi người.
jimmyb

Câu trả lời:


217

Dễ dàng chuyển đổi nm thành ký tự và sau đó thực hiện thay đổi:

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

CHỈNH SỬA: Và nếu thực sự bạn cần duy trì nm làm nhân tố, hãy thêm điều này vào cuối cùng:

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

4
as.character () làm cho cuộc sống dễ dàng hơn rất nhiều khi làm việc với các yếu tố. +1
Brandon Bertelsen

4
nếu bạn có nhiều cột thì sao?
geodex

43

một cách hữu ích khác để thay thế các giá trị

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

25

Câu trả lời ngắn gọn là:

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

Hãy xem các vectơ chỉ mục trong R Giới thiệu (nếu bạn chưa đọc nó).


BIÊN TẬP. Như đã nhận thấy trong các nhận xét, giải pháp này hoạt động đối với các vectơ ký tự nên không thành công trên dữ liệu của bạn.

Đối với yếu tố, cách tốt nhất là thay đổi cấp độ:

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

Bổ sung ngắn gọn: Việc sử dụng% trong% chỉ thực sự hữu ích nếu bạn có một tập hợp ở phía bên phải, như c("B","C"). Làm junk$nm[junk$nm == "B"]là cách tốt hơn.
Thilo

1
Ồ, một bổ sung quan trọng khác: Làm như thế này, trước tiên cần phải thêm mức yếu tố bvào hệ số nm. Phiên bản của diliop trên thực tế là phiên bản tốt hơn nếu bạn muốn làm việc với các nhân vật chứ không phải các yếu tố. (Luôn nghĩ về loại biến của bạn có trước!)
Thilo

điều đó không hoạt động trên dữ liệu do @Kenny tạo bởi vì dữ liệu là các yếu tố. Bạn đã quên một bước hoặc bạn có cài đặt chung để dừng chuyển đổi ký tự thành hệ số?
Gavin Simpson

4
@Thilo Một trong những điểm khác biệt quan trọng giữa %in%==NAxử lý: c(1,2,NA)==1cho TRUE, FALSE, NAnhưng c(1,2,NA) %in% 1cho TRUE, FALSE, FALSE. Và vâng, tôi đã quên kiểm tra xem cái này có hoạt động không: /
Marek

20

Vì dữ liệu bạn hiển thị là các yếu tố nên nó sẽ làm phức tạp mọi thứ một chút. Câu trả lời của @ diliop tiếp cận vấn đề bằng cách chuyển đổi nmthành một biến ký tự. Để quay lại các yếu tố ban đầu, cần phải thực hiện thêm một bước nữa.

Một giải pháp thay thế là thao tác các mức của yếu tố tại chỗ.

> 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

Điều đó khá đơn giản và tôi thường quên rằng có một chức năng thay thế cho levels().

Chỉnh sửa: Như đã lưu ý bởi @Seth trong phần nhận xét, điều này có thể được thực hiện trong một lớp lót mà không làm mất đi sự rõ ràng:

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

6
Đẹp. Tôi không biết về chức năng thay thế cho levels(). Làm thế nào về một lớp lót junk <- within(junk, levels(nm)[levels(nm)=="B"] <- "b")?

Nhưng bạn gọi nó hai lần :)
Marek

2
@Marek tát vào đầu Chỉ cho thấy rằng một người không nên trả lời các bình luận trên SO khi đã quá giờ đi ngủ. Hãy thử lại ...
Gavin Simpson

@Seth Thật vậy - thật tuyệt. Không chắc tại sao tôi lại tách các bước? Có lẽ để giải thích ...
Gavin Simpson

11

Cách dễ nhất để thực hiện việc này trong một lệnh là sử dụng whichlệnh và cũng không cần thay đổi các yếu tố thành ký tự bằng cách thực hiện điều này:

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

5

Bạn đã tạo một biến yếu tố, nmvì vậy bạn cần tránh làm như vậy hoặc thêm một cấp bổ sung vào thuộc tính yếu tố. Bạn cũng nên tránh sử dụng <-trong các đối số của data.frame ()

Lựa chọn 1:

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

Lựa chọn 2:

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

@DWin cảm ơn bạn đã đóng góp ý kiến ​​về vấn đề và sự cần thiết phải xem xét loại biến. Tôi chấp nhận câu trả lời của @ diliop vì đây là câu trả lời đầu tiên. Tôi biết có rất nhiều vấn đề liên quan đến <- vs = nhưng (nếu có thể trả lời ngắn gọn) tại sao nên dùng = với data.frame?
DQdlM

Bạn không cần thêm bdưới dạng cấp độ, chỉ cần thay đổi cấp độ đó Bthành b.
Gavin Simpson

@KennyPeanuts: tên cột là một vấn đề, hãy nhìn vào a <- data.frame(x<-1:10). Tên cột của nó không xmà là một thứ lộn xộn x....1.10. Tốt hơn nên sử dụng data.frame (x = 1: 10). Sau đó, bạn biết tên cột của bạn là gì.
IRTFM

@Gavin: Thêm dễ hơn thay thế và thậm chí dễ dàng hơn để không biến nó thành một yếu tố.
IRTFM

@Dwin Dễ dàng hơn? Tôi không đồng ý - hãy xem Câu trả lời của tôi để biết điều gì đó đơn giản. Việc thêm các cấp độ có thể khiến bạn không hiểu, chẳng hạn như trong mô hình predict()sẽ phàn nàn nếu các cấp độ yếu tố trong dữ liệu mới không khớp với cấp độ được sử dụng để phù hợp với mô hình. Dọn dẹp hơn về lâu dài để có được dữ liệu được định dạng như bạn muốn, đúng cách, thay vì dựa vào các đường tắt. Tôi đồng ý nó có thể được dễ dàng hơn để không làm cho nó một yếu tố, nhưng nếu nó đã là một, hoặc nhu cầu là một đối với một số tập thể dục mô hình ...
Gavin Simpson

1

Nếu bạn đang làm việc với các biến ký tự (lưu ý stringsAsFactorslà sai ở đây), bạn có thể sử dụng thay thế:

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

Gọi hàm này bằng dòng dưới đây.

d=stata.replace(d,"under20",1,"age<20")
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.