Очищення значень `Inf` від фрейму даних R


101

У R у мене є операція, яка створює деякі Infзначення, коли я перетворюю фрейм даних.

Я хотів би перетворити ці Infзначення у NAзначення. Код у мене повільний для великих даних, чи є більш швидкий спосіб зробити це?

Скажіть, у мене є такий кадр даних:

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

Але я не думаю, що це реально використовує силу Р.

Відповіді:


119

Варіант 1

Використовуйте той факт, що a data.frame- це список стовпців, а потім використовуйте do.callдля відтворення a 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 (поїзд, лаптоп (поїзд, функція (x) замінити (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<-рішення настільки повільніше.
— квітня 1212

трохи профілювання, і я відредагував ваше рішення набагато швидше.
— Менель

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

Існує дуже просте рішення цієї проблеми в пакеті хабларів:

library(hablar)

dat %>% rationalize()

Які повертають кадр даних із усіма Inf, перетворюються на NA.

Терміни порівняно з деякими вищезазначеними рішеннями. Код: бібліотека (хаблар) бібліотека (таблиця даних)

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 швидше, ніж хаблар. Але має довший синтаксис.


Терміни, будь ласка?
— Рікардо

@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, необхідних для коментарів де завгодно.
— Грегор Томас

Дякую! Так, я б залишив коментар, якби міг.
— Марк Е.

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

Ви також можете скористатись зручною функцією заміна_направлення: https://tidyr.tidyverse.org/reference/replace_na.html


1
Це відповідь лише для прикордонних посилань . Ви повинні розширити свою відповідь, щоб включити сюди якомога більше інформації, а використовувати посилання лише для довідки.
— Goodbye StackExchange
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.