Ganti nilai dalam bingkai data berdasarkan pernyataan kondisional (`if`)


122

Dalam bingkai data R yang dikodekan di bawah ini, saya ingin mengganti semua waktu yang B muncul dengan b.

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

ini memberikan:

   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

Upaya awal saya adalah menggunakan fordan ifpernyataan seperti ini:

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

tetapi seperti yang saya yakin Anda bisa lihat, ini menggantikan SEMUA nilai junk$nmdengan b. Saya dapat melihat mengapa ini melakukan ini tetapi saya tidak bisa mendapatkannya hanya untuk mengganti kasus sampah $ nm di mana nilai aslinya B.

CATATAN: Saya berhasil menyelesaikan masalah dengan gsubtetapi untuk kepentingan belajar RI masih ingin tahu bagaimana cara mendapatkan pendekatan asli saya untuk bekerja (jika memungkinkan)


1
Anda mungkin ingin menambahkan stringsAsFactors = FALSE ke konstruksi data.frame asli.
jimmyb

@jimmyb Mengapa? Faktor berguna, dan diperlukan jika seseorang memodelkan dengan sebagian besar kode pemodelan R. Cara yang benar untuk menangani hal ini adalah dengan mengakui bahwa data adalah suatu faktor. Jika Anda tidak menginginkan / membutuhkan konversi ini maka Anda dapat melakukan apa yang Anda katakan. Jika Anda memang menginginkan faktor tersebut, maka ada cara mudah untuk melakukan manipulasi yang ingin dilakukan @Kenny.
Gavin Simpson

1
Jadi faktor-faktor dulu lebih populer karena kinerja, namun, sekarang string tidak dapat diubah dan nilai faktor di-hash menjadi kurang jelas, karena sebagian besar fungsionalitas dasar R hanya akan mengonversinya (meskipun dengan peringatan) secara langsung. Saya pikir faktor-faktor menghasilkan sejumlah besar bug yang saya temukan di kode R.
jimmyb

Jawaban:


217

Lebih mudah untuk mengubah nm menjadi karakter dan kemudian melakukan perubahan:

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

EDIT: Dan jika memang Anda perlu mempertahankan nm sebagai faktor, tambahkan ini pada akhirnya:

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

4
as.character () membuat hidup jadi lebih mudah saat bekerja dengan faktor. +1
Brandon Bertelsen

4
bagaimana jika Anda memiliki banyak kolom?
geodex

43

cara lain yang berguna untuk mengganti nilai

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

25

Jawaban singkatnya adalah:

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

Lihatlah vektor Indeks dalam Pendahuluan R (jika Anda belum membacanya).


EDIT. Seperti yang diperhatikan dalam komentar, solusi ini berfungsi untuk vektor karakter jadi gagal pada data Anda.

Untuk faktor cara terbaik adalah dengan mengubah level:

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

Penambahan singkat: Penggunaan% dalam% hanya sangat membantu jika Anda memiliki himpunan di sisi kanan, seperti c("B","C"). Melakukan junk$nm[junk$nm == "B"]adalah cara yang lebih baik.
Thilo

1
Oh, tambahan penting lainnya: Melakukannya seperti ini membutuhkan terlebih dahulu menambahkan tingkat faktor bke faktor nm. Versi diliop sebenarnya lebih baik jika Anda ingin bekerja dengan karakter, bukan faktor. (Selalu pikirkan jenis variabel Anda terlebih dahulu!)
Thilo

yang tidak berfungsi pada data yang dibuat oleh @Kenny karena datanya adalah faktor. Apakah Anda lupa satu langkah atau apakah Anda memiliki pengaturan global untuk berhenti mengubah karakter menjadi faktor?
Gavin Simpson

4
@Thilo Salah satu perbedaan penting antara %in%dan ==adalah NAmenangani: c(1,2,NA)==1memberi TRUE, FALSE, NAtetapi c(1,2,NA) %in% 1memberi TRUE, FALSE, FALSE. Dan ya saya lupa memeriksa apakah ini berhasil: /
Marek

20

Karena data yang Anda tunjukkan adalah faktor, itu sedikit memperumit masalah. Jawaban @ diliop mendekati masalah dengan mengubahnya nmmenjadi variabel karakter. Untuk kembali ke faktor awal diperlukan langkah lebih lanjut.

Alternatifnya adalah memanipulasi level faktor yang ada.

> 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

Itu cukup sederhana dan saya sering lupa bahwa ada fungsi pengganti untuk levels().

Edit: Seperti dicatat oleh @Seth di komentar, ini dapat dilakukan dalam satu baris, tanpa kehilangan kejelasan:

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

6
Bagus. Saya tidak tahu tentang fungsi pengganti untuk levels(). Bagaimana dengan satu liner junk <- within(junk, levels(nm)[levels(nm)=="B"] <- "b")?

Tapi Anda menyebutnya dua kali :)
Marek

2
@Marek tamparan kepala Hanya pergi untuk menunjukkan bahwa seseorang seharusnya tidak menanggapi komentar di SO ketika sudah lewat waktu tidur mereka. Mari kita coba lagi ...
Gavin Simpson

@Seth Memang - bagus. Tidak yakin mengapa saya memisahkan langkah-langkahnya? Mungkin untuk eksposisi ...
Gavin Simpson

11

Cara termudah untuk melakukan ini dalam satu perintah adalah dengan menggunakan whichperintah dan juga tidak perlu mengubah faktor menjadi karakter dengan melakukan ini:

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

5

Anda telah membuat variabel faktor nmsehingga Anda perlu menghindarinya atau menambahkan level tambahan ke atribut faktor. Anda juga harus menghindari penggunaan <-dalam argumen ke data.frame ()

Pilihan 1:

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

Pilihan 2:

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

@DWin terima kasih atas masukan Anda tentang masalah ini dan kebutuhan untuk mempertimbangkan jenis variabel. Saya menerima jawaban @ diliop karena itu yang pertama berhasil. Saya tahu ada banyak masalah tentang <- vs = tetapi (jika dapat dijawab secara singkat) mengapa harus = digunakan data.frame?
DQdlM

Anda tidak perlu menambahkan bsebagai level, cukup ubah levelnya Bmenjadi b.
Gavin Simpson

@KennyPeanuts: nama kolom adalah salah satu masalah, Lihat a <- data.frame(x<-1:10). Nama kolomnya tidak xmelainkan berantakan x....1.10. Lebih baik gunakan data.frame (x = 1: 10). Kemudian Anda tahu apa nama kolom Anda.
IRTFM

@Gavin: Lebih mudah menambahkan daripada mengganti, dan bahkan lebih mudah untuk tidak menjadikannya sebagai faktor.
IRTFM

@Dwin Lebih Mudah? Saya tidak setuju - lihat Jawaban saya untuk sesuatu yang sederhana. Menambahkan level dapat membuat Anda bingung, katakanlah dalam pemodelan predict()yang akan mengeluh jika level faktor dalam data baru tidak cocok dengan yang digunakan untuk menyesuaikan model. Lebih bersih dalam jangka panjang untuk mendapatkan data yang diformat sesuai keinginan Anda, dengan benar, daripada mengandalkan jalan pintas. Saya setuju mungkin lebih mudah untuk tidak menjadikannya faktor, tetapi jika sudah menjadi satu, atau perlu menjadi satu untuk beberapa latihan pemodelan ...
Gavin Simpson

1

Jika Anda bekerja dengan variabel karakter (perhatikan yang stringsAsFactorssalah di sini) Anda dapat menggunakan 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)])
}

Panggil fungsi ini menggunakan baris di bawah ini.

d=stata.replace(d,"under20",1,"age<20")
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.