Очищення значень `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.