การล้างค่า "Inf" จาก R dataframe


103

ใน R ฉันมีการดำเนินการที่สร้างInfค่าบางอย่างเมื่อฉันแปลงดาต้าเฟรม

ฉันต้องการเปลี่ยนInfค่าเหล่านี้ให้เป็นNAค่านิยม รหัสที่ฉันมีช้าสำหรับข้อมูลขนาดใหญ่มีวิธีที่เร็วกว่านี้ไหม

สมมติว่าฉันมี dataframe ต่อไปนี้:

dat <- data.frame(a=c(1, Inf), b=c(Inf, 3), d=c("a","b"))

ต่อไปนี้ใช้งานได้ในกรณีเดียว:

 dat[,1][is.infinite(dat[,1])] = NA

ดังนั้นฉันจึงสรุปมันด้วยลูปต่อไปนี้

cf_DFinf2NA <- function(x)
{
    for (i in 1:ncol(x)){
          x[,i][is.infinite(x[,i])] = NA
    }
    return(x)
}

แต่คิดไม่ถึงว่านี่คือการใช้พลังของอาร์จริงๆ

คำตอบ:


121

ตัวเลือกที่ 1

ใช้ข้อเท็จจริงที่ว่า a data.frameคือรายการของคอลัมน์จากนั้นใช้do.callเพื่อสร้างไฟล์data.frame.

do.call(data.frame,lapply(DT, function(x) replace(x, is.infinite(x),NA)))

ตัวเลือกที่ 2 - data.table

คุณสามารถใช้data.tableและset. ซึ่งจะหลีกเลี่ยงการคัดลอกภายใน

DT <- data.table(dat)
invisible(lapply(names(DT),function(.name) set(DT, which(is.infinite(DT[[.name]])), j = .name,value =NA)))

หรือใช้หมายเลขคอลัมน์ (อาจเร็วกว่าหากมีคอลัมน์จำนวนมาก):

for (j in 1:ncol(DT)) set(DT, which(is.infinite(DT[[j]])), j, NA)

การกำหนดเวลา

# some `big(ish)` data
dat <- data.frame(a = rep(c(1,Inf), 1e6), b = rep(c(Inf,2), 1e6), 
                  c = rep(c('a','b'),1e6),d = rep(c(1,Inf), 1e6),  
                  e = rep(c(Inf,2), 1e6))
# create data.table
library(data.table)
DT <- data.table(dat)

# replace (@mnel)
system.time(na_dat <- do.call(data.frame,lapply(dat, function(x) replace(x, is.infinite(x),NA))))
## user  system elapsed 
#  0.52    0.01    0.53 

# is.na (@dwin)
system.time(is.na(dat) <- sapply(dat, is.infinite))
# user  system elapsed 
# 32.96    0.07   33.12 

# modified is.na
system.time(is.na(dat) <- do.call(cbind,lapply(dat, is.infinite)))
#  user  system elapsed 
# 1.22    0.38    1.60 


# data.table (@mnel)
system.time(invisible(lapply(names(DT),function(.name) set(DT, which(is.infinite(DT[[.name]])), j = .name,value =NA))))
# user  system elapsed 
# 0.29    0.02    0.31 

data.tableเร็วที่สุด การใช้sapplyสิ่งต่างๆช้าลงอย่างเห็นได้ชัด


1
ทำได้ดีมากกับการกำหนดเวลาและการปรับเปลี่ยน @mnel ฉันหวังว่าจะมีวิธีอื่นในการโอนตัวแทนข้ามบัญชี ฉันคิดว่าฉันจะออกไปข้างนอกและโหวตคำตอบอื่น ๆ ของคุณ
IRTFM

ข้อผิดพลาดใน do.call (train, lapply (train, function (x) replace (x, is.infinite (x),: 'what' ต้องเป็นสตริงอักขระหรือฟังก์ชัน
Hack-R

60

ใช้sapplyและis.na<-

> dat <- data.frame(a=c(1, Inf), b=c(Inf, 3), d=c("a","b"))
> is.na(dat) <- sapply(dat, is.infinite)
> dat
   a  b d
1  1 NA a
2 NA  3 b

หรือคุณสามารถใช้ (ให้เครดิตกับ @mnel ซึ่งมีการแก้ไขนี้)

> is.na(dat) <- do.call(cbind,lapply(dat, is.infinite))

ซึ่งเร็วกว่ามาก


5
ว่า "เคล็ดลับ" ที่อยู่ในการตระหนักถึงis.na<-จะไม่ยอมรับผลจากแต่จะยอมรับจากlapply sapply
IRTFM

ฉันได้เพิ่มการกำหนดเวลาแล้ว ฉันไม่แน่ใจว่าทำไมการis.na<-แก้ปัญหาจึงช้าลงมาก
ช่อง

การทำโปรไฟล์เล็กน้อยและฉันได้แก้ไขโซลูชันของคุณให้เร็วขึ้นมาก
ช่อง

19

[<-ด้วยmapplyจะเร็วกว่าsapplyเล็กน้อย

> dat[mapply(is.infinite, dat)] <- NA

ด้วยข้อมูลของ mnel เวลาคือ

> system.time(dat[mapply(is.infinite, dat)] <- NA)
#   user  system elapsed 
# 15.281   0.000  13.750 

11

นี่คือโซลูชัน dplyr / tidyverse โดยใช้ฟังก์ชัน na_if () :

dat %>% mutate_if(is.numeric, list(~na_if(., Inf)))

โปรดทราบว่าสิ่งนี้จะแทนที่อินฟินิตี้บวกด้วย NA เท่านั้น ต้องทำซ้ำหากต้องเปลี่ยนค่าอินฟินิตี้ที่เป็นลบด้วย

dat %>% mutate_if(is.numeric, list(~na_if(., Inf))) %>% 
  mutate_if(is.numeric, list(~na_if(., -Inf)))

5

มีวิธีแก้ปัญหานี้ง่ายมากในแพ็คเกจ hablar:

library(hablar)

dat %>% rationalize()

ซึ่งส่งคืนเฟรมข้อมูลที่มี Inf ทั้งหมดจะถูกแปลงเป็น NA

การกำหนดเวลาเทียบกับโซลูชันข้างต้นบางส่วน รหัส: library (hablar) library (data.table)

dat <- data.frame(a = rep(c(1,Inf), 1e6), b = rep(c(Inf,2), 1e6), 
                  c = rep(c('a','b'),1e6),d = rep(c(1,Inf), 1e6),  
                  e = rep(c(Inf,2), 1e6))
DT <- data.table(dat)

system.time(dat[mapply(is.infinite, dat)] <- NA)
system.time(dat[dat==Inf] <- NA)
system.time(invisible(lapply(names(DT),function(.name) set(DT, which(is.infinite(DT[[.name]])), j = .name,value =NA))))
system.time(rationalize(dat))

ผลลัพธ์:

> system.time(dat[mapply(is.infinite, dat)] <- NA)
   user  system elapsed 
  0.125   0.039   0.164 
> system.time(dat[dat==Inf] <- NA)
   user  system elapsed 
  0.095   0.010   0.108 
> system.time(invisible(lapply(names(DT),function(.name) set(DT, which(is.infinite(DT[[.name]])), j = .name,value =NA))))
   user  system elapsed 
  0.065   0.002   0.067 
> system.time(rationalize(dat))
   user  system elapsed 
  0.058   0.014   0.072 
> 

ดูเหมือนว่า data.table จะเร็วกว่า hablar แต่มีไวยากรณ์ที่ยาวกว่า


โปรดกำหนดเวลา?
ricardo

@ricardo เพิ่มการกำหนดเวลา
davsjob

1

Feng Mai มีคำตอบที่เป็นระเบียบเรียบร้อยข้างต้นเพื่อรับความไม่สมบูรณ์เชิงลบและเชิงบวก:

dat %>% mutate_if(is.numeric, list(~na_if(., Inf))) %>% 
  mutate_if(is.numeric, list(~na_if(., -Inf)))

วิธีนี้ใช้งานได้ดี แต่คำเตือนคืออย่าสลับใน abs (.) ที่นี่เพื่อทำทั้งสองบรรทัดพร้อมกันตามที่เสนอในความคิดเห็นที่ได้รับการโหวต ดูเหมือนว่าจะใช้งานได้ แต่เปลี่ยนค่าลบทั้งหมดในชุดข้อมูลเป็นค่าบวก! คุณสามารถยืนยันได้ด้วยสิ่งนี้:

data(iris)
#The last line here is bad - it converts all negative values to positive
iris %>% 
  mutate_if(is.numeric, ~scale(.)) %>%
  mutate(infinities = Sepal.Length / 0) %>%
  mutate_if(is.numeric, list(~na_if(abs(.), Inf)))

สำหรับหนึ่งบรรทัดสิ่งนี้ใช้ได้:

  mutate_if(is.numeric, ~ifelse(abs(.) == Inf,NA,.))

1
จับดี! ฉันได้เพิ่มความคิดเห็นเพื่อส่งผลกระทบต่อความคิดเห็นเดิม - ฉันคิดว่านั่นเป็นจุดที่ดีกว่าในการแก้ไขปัญหามากกว่าคำตอบใหม่ นอกจากนี้ยังพบโพสต์บางส่วนของคุณที่ควรค่าแก่การโหวตเพื่อให้คุณเข้าใกล้ชื่อเสียง 50 อันดับที่จำเป็นในการแสดงความคิดเห็น
Gregor Thomas

ขอบคุณ! ใช่ฉันจะแสดงความคิดเห็นถ้าฉันสามารถทำได้
Mark E.

0

วิธีแก้ปัญหาอื่น:

    dat <- data.frame(a = rep(c(1,Inf), 1e6), b = rep(c(Inf,2), 1e6), 
                      c = rep(c('a','b'),1e6),d = rep(c(1,Inf), 1e6),  
                      e = rep(c(Inf,2), 1e6))
    system.time(dat[dat==Inf] <- NA)

#   user  system elapsed
#  0.316   0.024   0.340

MusTheDataGuy ทำไมคุณถึงแก้ไขคำตอบของฉัน แต่ไม่เพิ่มโซลูชันของคุณเอง มีปุ่ม "เพิ่มคำตอบอื่น" แล้ว!
นักศึกษา

-1

คุณยังสามารถใช้ฟังก์ชัน replace_na ที่มีประโยชน์: https://tidyr.tidyverse.org/reference/replace_na.html


1
นี้เป็นเส้นเขตแดนคำตอบการเชื่อมโยงเท่านั้น คุณควรขยายคำตอบเพื่อให้มีข้อมูลมากที่สุดที่นี่และใช้ลิงก์เพื่อการอ้างอิงเท่านั้น
Goodbye StackExchange
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.