Xóa các giá trị `Inf` khỏi khung dữ liệu R


101

Trong R, tôi có một phép toán tạo ra một số Infgiá trị khi tôi chuyển đổi khung dữ liệu.

Tôi muốn biến những Infgiá trị này thành NAgiá trị. Mã tôi có chậm đối với dữ liệu lớn, có cách nào nhanh hơn để làm điều này không?

Giả sử tôi có khung dữ liệu sau:

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

Những điều sau đây hoạt động trong một trường hợp duy nhất:

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

Vì vậy, tôi đã khái quát nó bằng vòng lặp sau

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

Nhưng tôi không nghĩ rằng đây thực sự là sử dụng sức mạnh của R.

Câu trả lời:


119

lựa chọn 1

Sử dụng thực tế rằng a data.framelà một danh sách các cột, sau đó sử dụng do.callđể tạo lại a data.frame.

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

Lựa chọn 2 -- data.table

Bạn có thể sử dụng data.tableset. Điều này tránh một số sao chép nội bộ.

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

Hoặc sử dụng số cột (có thể nhanh hơn nếu có nhiều cột):

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

Thời gian

# 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.tablelà nhanh nhất. Sử dụng sapplylàm chậm mọi thứ đáng kể.


1
Rất tốt về thời gian và sửa đổi @mnel. Tôi ước có một cách SO để chuyển đại diện giữa các tài khoản. Tôi nghĩ rằng tôi sẽ đi ra ngoài và tán thành một số câu trả lời khác của bạn.
IRTFM

lỗi trong do.call (train, lapply (train, function (x) Replace (x, is.infinite (x),: 'what' phải là một chuỗi ký tự hoặc một hàm
Hack-R

60

Sử dụng sapplyis.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

Hoặc bạn có thể sử dụng (cấp tín dụng cho @mnel, người có bản chỉnh sửa này),

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

nhanh hơn đáng kể.


5
"Thủ thuật" là nhận ra rằng is.na<-sẽ không chấp nhận một kết quả từ lapplynhưng sẽ chấp nhận một kết quả từ sapply.
IRTFM

Tôi đã thêm một số thời gian. Tôi không chắc tại sao is.na<-giải pháp lại chậm hơn nhiều.
mnel

một chút hồ sơ và tôi đã chỉnh sửa giải pháp của bạn để nhanh hơn nhiều.
mnel

19

[<-với mapplylà một chút nhanh hơn sapply.

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

Với dữ liệu của kênh, thời gian là

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

11

Đây là một giải pháp dplyr / updo bằng hàm na_if () :

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

Lưu ý rằng điều này chỉ thay thế dương vô cùng bằng NA. Cần lặp lại nếu các giá trị âm vô cực cũng cần được thay thế.

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

5

Có một giải pháp rất đơn giản cho vấn đề này trong gói hablar:

library(hablar)

dat %>% rationalize()

Khung dữ liệu trả về có tất cả Inf được chuyển đổi thành NA.

Thời điểm so với một số giải pháp trên. Mã: thư viện thư viện (hablar) thư viện (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))

Kết quả:

> 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 
> 

Có vẻ như data.table nhanh hơn hablar. Nhưng có cú pháp dài hơn.


Thời gian làm ơn?
ricardo

@ricardo thêm một số timings
davsjob

1

Feng Mai có một câu trả lời gọn gàng ở trên để nhận được vô hạn âm và dương:

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

Điều này hoạt động tốt, nhưng một lời cảnh báo là không hoán đổi trong abs (.) Ở đây để thực hiện cả hai dòng cùng một lúc như được đề xuất trong một nhận xét được tán thành. Nó sẽ trông giống như nó hoạt động, nhưng thay đổi tất cả các giá trị âm trong tập dữ liệu thành tích cực! Bạn có thể xác nhận điều này:

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

Đối với một dòng, điều này hoạt động:

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

1
Nắm bắt tốt! Tôi đã thêm nhận xét vào điều này ảnh hưởng đến nhận xét ban đầu - tôi nghĩ rằng đó là một nơi tốt hơn để giải quyết vấn đề hơn là một câu trả lời mới. Cũng tìm thấy một số bài đăng của bạn xứng đáng nhận được sự tán thành để giúp bạn tiến gần hơn một chút đến 50 danh tiếng cần thiết để bình luận ở bất kỳ đâu.
Gregor Thomas

Cảm ơn! Vâng, tôi sẽ để lại một bình luận nếu tôi có thể.
Mark E.

0

Giải pháp khác:

    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, tại sao bạn chỉnh sửa câu trả lời của tôi mà không thêm giải pháp của riêng bạn? Đã có nút "thêm câu trả lời khác"!
Sinh viên

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.