Dalam data.frame (atau data.table), saya ingin "mengisi maju" NAS dengan nilai non-NA terdekat sebelumnya. Contoh sederhana, menggunakan vektor (bukan a data.frame
) adalah sebagai berikut:
> y <- c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA)
Saya ingin fungsi fill.NAs()
yang memungkinkan saya membangun yy
sedemikian rupa sehingga:
> yy
[1] NA NA NA 2 2 2 2 3 3 3 4 4
Saya perlu mengulangi operasi ini untuk banyak (total ~ 1 Tb) data.frame
s kecil (~ 30-50 Mb), di mana baris adalah NA adalah semua entri. Apa cara yang baik untuk mendekati masalah?
Solusi jelek yang saya buat menggunakan fungsi ini:
last <- function (x){
x[length(x)]
}
fill.NAs <- function(isNA){
if (isNA[1] == 1) {
isNA[1:max({which(isNA==0)[1]-1},1)] <- 0 # first is NAs
# can't be forward filled
}
isNA.neg <- isNA.pos <- isNA.diff <- diff(isNA)
isNA.pos[isNA.diff < 0] <- 0
isNA.neg[isNA.diff > 0] <- 0
which.isNA.neg <- which(as.logical(isNA.neg))
if (length(which.isNA.neg)==0) return(NULL) # generates warnings later, but works
which.isNA.pos <- which(as.logical(isNA.pos))
which.isNA <- which(as.logical(isNA))
if (length(which.isNA.neg)==length(which.isNA.pos)){
replacement <- rep(which.isNA.pos[2:length(which.isNA.neg)],
which.isNA.neg[2:max(length(which.isNA.neg)-1,2)] -
which.isNA.pos[1:max(length(which.isNA.neg)-1,1)])
replacement <- c(replacement, rep(last(which.isNA.pos), last(which.isNA) - last(which.isNA.pos)))
} else {
replacement <- rep(which.isNA.pos[1:length(which.isNA.neg)], which.isNA.neg - which.isNA.pos[1:length(which.isNA.neg)])
replacement <- c(replacement, rep(last(which.isNA.pos), last(which.isNA) - last(which.isNA.pos)))
}
replacement
}
Fungsi fill.NAs
ini digunakan sebagai berikut:
y <- c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA)
isNA <- as.numeric(is.na(y))
replacement <- fill.NAs(isNA)
if (length(replacement)){
which.isNA <- which(as.logical(isNA))
to.replace <- which.isNA[which(isNA==0)[1]:length(which.isNA)]
y[to.replace] <- y[replacement]
}
Keluaran
> y
[1] NA 2 2 2 2 3 3 3 4 4 4
... yang sepertinya berhasil. Tapi, kawan, apakah itu jelek! Ada saran?
r
data.table
zoo
r-faq
Ryogi
sumber
sumber
roll=TRUE
didata.table
.fill
diR
tidyr::fill()
.Jawaban:
Anda mungkin ingin menggunakan
na.locf()
fungsi dari paket kebun binatang untuk melakukan pengamatan terakhir ke depan untuk mengganti nilai NA Anda.Ini adalah awal dari contoh penggunaannya dari halaman bantuan:
sumber
na.locf
di kebun binatang bekerja dengan vektor biasa serta objek kebun binatang. Itsna.rm
argumen dapat berguna dalam beberapa aplikasi.na.locf(cz, na.rm=FALSE)
untuk terus memimpinNA
.Maaf karena menggali pertanyaan lama. Saya tidak bisa mencari fungsi untuk melakukan pekerjaan ini di kereta, jadi saya menulis sendiri.
Saya bangga mengetahui bahwa ini sedikit lebih cepat.
Ini kurang fleksibel.
Tapi bermain bagus
ave
, itulah yang saya butuhkan.Edit
Karena ini menjadi jawaban saya yang paling banyak dipilih, saya sering diingatkan bahwa saya tidak menggunakan fungsi saya sendiri, karena saya sering membutuhkan kebun binatang
maxgap
argumen . Karena kebun binatang memiliki beberapa masalah aneh dalam kasus tepi ketika saya menggunakan tanggal dplyr + yang tidak dapat saya debug, saya kembali ke sini hari ini untuk meningkatkan fungsi lama saya.Saya membuat tolok ukur fungsi saya yang ditingkatkan dan semua entri lainnya di sini. Untuk serangkaian fitur dasar,
tidyr::fill
tercepat dan juga tidak menghilangkan kasus tepi. Entri Rcpp oleh @BrandonBertelsen masih lebih cepat, tetapi tidak fleksibel mengenai tipe input (ia menguji case edge secara tidak benar karena kesalahpahamanall.equal
).Jika Anda perlu
maxgap
, fungsi saya di bawah ini lebih cepat daripada kebun binatang (dan tidak memiliki masalah aneh dengan tanggal).Saya memasang dokumentasi tes saya .
fungsi baru
Saya juga memasukkan fungsi ke dalam paket formr saya (hanya Github).
sumber
df
dengan beberapa kolom?na.locf0
yang sekarang memiliki cakupan dan kinerja yang mirip denganrepeat_last
fungsi Anda . Petunjuknya adalah menggunakandiff
daripadacumsum
dan menghindariifelse
. Fungsi utamana.locf.default
masih agak lambat karena melakukan pengecekan lagi dan menangani banyak kolom dll.a
data.table
solusi:pendekatan ini dapat bekerja dengan mengisi nol di depan juga:
metode ini menjadi sangat berguna pada data pada skala dan di mana Anda ingin melakukan pengisian maju dengan kelompok, yang sepele dengan
data.table
. cukup tambahkan grup keby
klausa sebelumcumsum
logika.sumber
Berurusan dengan volume data besar, agar lebih efisien, kita bisa menggunakan paket data.table.
sumber
replaceNaWithLatest <- function( dfIn, nameColsNa = names(dfIn)[1] ){ dtTest <- data.table(dfIn) invisible(lapply(nameColsNa, function(nameColNa){ setnames(dtTest, nameColNa, "colNa") dtTest[, segment := cumsum(!is.na(colNa))] dtTest[, colNa := colNa[1], by = "segment"] dtTest[, segment := NULL] setnames(dtTest, "colNa", nameColNa) })) return(dtTest) }
Melemparkan topiku:
Menyiapkan sampel dasar dan tolok ukur:
Dan jalankan beberapa tolok ukur:
Untuk berjaga-jaga:
Memperbarui
Untuk vektor numerik, fungsinya sedikit berbeda:
sumber
Ini berhasil bagi saya:
kecepatan juga masuk akal:
sumber
replace_na_with_last(c(NA,1:4,NA))
(Yaitu mereka diisi dengan nilai berikut). Ini juga merupakan perilaku default dariimputeTS::na.locf(x, na.remaining = "rev")
.replace_na_with_last<-function(x,p=is.na,d=0)c(d,x)[cummax(seq_along(x)*(!p(x)))+1]
Coba fungsi ini. Tidak memerlukan paket ZOO:
Contoh:
sumber
if (!anyNA(x)) return(x)
.Memiliki pemimpin
NA
adalah sedikit kerutan, tetapi saya menemukan cara yang sangat mudah dibaca (dan vektor) untuk melakukan LOCF ketika istilah terkemuka tidak hilang adalah:na.omit(y)[cumsum(!is.na(y))]
Modifikasi yang sedikit kurang mudah dibaca secara umum:
c(NA, na.omit(y))[cumsum(!is.na(y))+1]
memberikan hasil yang diinginkan:
c(NA, 2, 2, 2, 2, 3, 3, 4, 4, 4)
sumber
Anda dapat menggunakan
data.table
fungsi ininafill
, tersedia daridata.table >= 1.12.3
.Jika vektor Anda adalah kolom dalam
data.table
, Anda juga dapat memperbaruinya dengan referensi dengansetnafill
:Jika ada
NA
di beberapa kolom ...... Anda dapat mengisinya dengan referensi dalam sekali jalan:
Perhatikan bahwa:
Fungsionalitas kemungkinan besar akan segera diperpanjang; lihat isu terbuka nafill, setnafill untuk karakter, faktor dan tipe lainnya , di mana Anda juga menemukan solusi sementara .
sumber
Paket rapi mengusulkan cara sederhana untuk melakukan itu:
sumber
Ada banyak paket yang menawarkan fungsi
na.locf
(NA
Pengamatan Terakhir Dilakukan Maju):xts
-xts::na.locf
zoo
-zoo::na.locf
imputeTS
-imputeTS::na.locf
spacetime
-spacetime::na.locf
Dan juga paket lain di mana fungsi ini dinamai berbeda.
sumber
Menindaklanjuti kontribusi Brandon Bertelsen Rcpp. Bagi saya, versi NumericVector tidak berfungsi: itu hanya menggantikan NA pertama. Ini karena
ina
vektor hanya dievaluasi sekali, di awal fungsi.Sebagai gantinya, seseorang dapat mengambil pendekatan yang sama persis seperti untuk fungsi IntegerVector. Berikut ini bekerja untuk saya:
Jika Anda membutuhkan versi CharacterVector, pendekatan dasar yang sama juga berfungsi:
sumber
Berikut ini adalah modifikasi dari solusi @ AdamO. Yang ini berjalan lebih cepat, karena melewati
na.omit
fungsi. Ini akan menimpaNA
nilai - nilai dalam vektory
(kecuali untukNA
s terkemuka ).sumber
Saya mencoba di bawah ini:
nullIdx mendapatkan nomor idx di mana pernah masterData $ RequiredColumn memiliki nilai Null / NA. Pada baris berikutnya kita menggantinya dengan nilai Idx-1 yang sesuai, yaitu nilai bagus terakhir sebelum setiap NULL / NA
sumber
1 NA NA
berubah menjadi1 1 NA
. Juga, saya pikiras.array()
itu tidak perlu.Ini bekerja untuk saya, walaupun saya tidak yakin apakah ini lebih efisien daripada saran lainnya.
sumber
Reduce adalah konsep pemrograman fungsional yang bagus yang mungkin berguna untuk tugas serupa. Sayangnya di R ~ 70 kali lebih lambat dari
repeat.before
pada jawaban di atas.sumber
Saya pribadi menggunakan fungsi ini. Saya tidak tahu seberapa cepat atau lambatnya itu. Tetapi ia melakukan tugasnya tanpa harus menggunakan perpustakaan.
jika Anda ingin menerapkan fungsi ini dalam kerangka data, jika kerangka data Anda disebut df maka cukup
sumber