Удалите строки со всеми или некоторыми НС (отсутствующие значения) в data.frame

Я хотел бы удалить строки в этом фрейме данных, которые:

a) содержат NA s по всем столбцам. Ниже приведен примерный кадр данных.

  gene hsap mmul mmus rnor cfam 1 ENSG00000208234 0 NA NA NA NA 2 ENSG00000199674 0 2 2 2 2 3 ENSG00000221622 0 NA NA NA NA 4 ENSG00000207604 0 NA NA 1 2 5 ENSG00000207431 0 NA NA NA NA 6 ENSG00000221312 0 1 2 3 2 

В принципе, я хотел бы получить фрейм данных, такой как следующее.

  gene hsap mmul mmus rnor cfam 2 ENSG00000199674 0 2 2 2 2 6 ENSG00000221312 0 1 2 3 2 

b) содержать NA s только в некоторых столбцах , поэтому я также могу получить этот результат:

  gene hsap mmul mmus rnor cfam 2 ENSG00000199674 0 2 2 2 2 4 ENSG00000207604 0 NA NA 1 2 6 ENSG00000221312 0 1 2 3 2 

Также проверьте complete.cases :

 > final[complete.cases(final), ] gene hsap mmul mmus rnor cfam 2 ENSG00000199674 0 2 2 2 2 6 ENSG00000221312 0 1 2 3 2 

na.omit лучше всего удалить все NA . complete.cases позволяет частичный выбор, включая только определенные столбцы кадра данных:

 > final[complete.cases(final[ , 5:6]),] gene hsap mmul mmus rnor cfam 2 ENSG00000199674 0 2 2 2 2 4 ENSG00000207604 0 NA NA 1 2 6 ENSG00000221312 0 1 2 3 2 

Ваше решение не может работать. Если вы настаиваете на использовании is.na , тогда вам нужно сделать что-то вроде:

 > final[rowSums(is.na(final[ , 5:6])) == 0, ] gene hsap mmul mmus rnor cfam 2 ENSG00000199674 0 2 2 2 2 4 ENSG00000207604 0 NA NA 1 2 6 ENSG00000221312 0 1 2 3 2 

но использование complete.cases довольно много яснее и быстрее.

Попробуйте na.omit(your.data.frame) . Что касается второго вопроса, попробуйте отправить его как еще один вопрос (для ясности).

Я предпочитаю следующий способ проверить, содержат ли строки любые NA:

 row.has.na <- apply(final, 1, function(x){any(is.na(x))}) 

Это возвращает логический вектор со значениями, указывающими, есть ли какой-либо NA в строке. Вы можете использовать его, чтобы увидеть, сколько строк вам нужно отбросить:

 sum(row.has.na) 

и в конечном итоге

 final.filtered <- final[!row.has.na,] 

Для фильтрации строк с определенной частью НС становится немного сложнее (например, вы можете кормить 'final [, 5: 6]' to 'apply'). Как правило, решение Joris Meys кажется более элегантным.

Если вам нравятся трубы ( %>% ), новый drop_na – ваш друг:

 library(tidyr) df %>% drop_na() # gene hsap mmul mmus rnor cfam # 2 ENSG00000199674 0 2 2 2 2 # 6 ENSG00000221312 0 1 2 3 2 df %>% drop_na(rnor, cfam) # gene hsap mmul mmus rnor cfam # 2 ENSG00000199674 0 2 2 2 2 # 4 ENSG00000207604 0 NA NA 1 2 # 6 ENSG00000221312 0 1 2 3 2 

Другой вариант, если вы хотите больше контролировать, как строки считаются недействительными, является

 final <- final[!(is.na(final$rnor)) | !(is.na(rawdata$cfam)),] 

Используя вышеизложенное, это:

  gene hsap mmul mmus rnor cfam 1 ENSG00000208234 0 NA NA NA 2 2 ENSG00000199674 0 2 2 2 2 3 ENSG00000221622 0 NA NA 2 NA 4 ENSG00000207604 0 NA NA 1 2 5 ENSG00000207431 0 NA NA NA NA 6 ENSG00000221312 0 1 2 3 2 

становится:

  gene hsap mmul mmus rnor cfam 1 ENSG00000208234 0 NA NA NA 2 2 ENSG00000199674 0 2 2 2 2 3 ENSG00000221622 0 NA NA 2 NA 4 ENSG00000207604 0 NA NA 1 2 6 ENSG00000221312 0 1 2 3 2 

... где только строка 5 удаляется, так как она является единственной строкой, содержащей NA для обоих rnor AND cfam . Логическая логика может быть затем изменена в соответствии с конкретными требованиями.

Если вы хотите контролировать количество НС для каждой строки, попробуйте эту функцию. Для многих наборов данных опроса слишком много пустых ответов на вопросы могут испортить результаты. Поэтому они удаляются после определенного порога. Эта функция позволит вам выбрать количество НС, которое может иметь строка до ее удаления:

 delete.na <- function(DF, n=0) { DF[rowSums(is.na(DF)) <= n,] } 

По умолчанию он удалит все НС:

 delete.na(final) gene hsap mmul mmus rnor cfam 2 ENSG00000199674 0 2 2 2 2 6 ENSG00000221312 0 1 2 3 2 

Или укажите максимальное допустимое число НС:

 delete.na(final, 2) gene hsap mmul mmus rnor cfam 2 ENSG00000199674 0 2 2 2 2 4 ENSG00000207604 0 NA NA 1 2 6 ENSG00000221312 0 1 2 3 2 

Это вернет строки, имеющие хотя бы одно значение, отличное от NA.

 final[rowSums(is.na(final)) 

Это вернет строки, имеющие по крайней мере два значения, отличных от NA.

 final[rowSums(is.na(final))<(length(final)-1),] 

Мы можем также использовать для этого функцию подмножества.

 finalData<-subset(data,!(is.na(data["mmul"]) | is.na(data["rnor"]))) 

Это даст только те строки, которые не имеют NA в обоих mmul и rnor

Используя пакет dplyr, мы можем фильтровать NA следующим образом:

 dplyr::filter(df, !is.na(columnname)) 

Если производительность является приоритетом, используйте data.table и na.omit() с дополнительными параметрами cols= .

na.omit.data.table является самым быстрым в моем тестировании (см. ниже), будь то для всех столбцов или для выбранных столбцов (часть вопроса 2 вопроса).

Если вы не хотите использовать data.table , используйте complete.cases() .

На data.frame , complete.cases быстрее, чем na.omit() или dplyr::drop_na() . Обратите внимание: na.omit.data.frame не поддерживает cols= .

Результат теста

Ниже приведено сравнение базовых (синих), dplyr (розовых) и data.table (желтых) методов для data.table всех или выбора отсутствующих наблюдений на условном наборе данных из 1 миллиона наблюдений из 20 числовых переменных с независимой вероятностью 5% отсутствует, и подмножество из 4 переменных для части 2.

Ваши результаты могут отличаться в зависимости от длины, ширины и разреженности вашего конкретного набора данных.

Шкала журнала регистрации по оси y.

введите описание изображения здесь

Сценарий производительности

 #------- Adjust these assumptions for your own use case ------------ row_size <- 1e6L col_size <- 20 # not including ID column p_missing <- 0.05 # likelihood of missing observation (except ID col) col_subset <- 18:21 # second part of question: filter on select columns #------- System info for benchmark ---------------------------------- R.version # R version 3.4.3 (2017-11-30), platform = x86_64-w64-mingw32 library(data.table); packageVersion('data.table') # 1.10.4.3 library(dplyr); packageVersion('dplyr') # 0.7.4 library(tidyr); packageVersion('tidyr') # 0.8.0 library(microbenchmark) #------- Example dataset using above assumptions -------------------- fakeData <- function(m, n, p){ set.seed(123) m <- matrix(runif(m*n), nrow=m, ncol=n) m[m% drop_na, dt[complete.cases(dt), ], na.omit(dt) ), xlab='', main = 'Performance: Drop any NA observation', col=c(rep('lightblue',2),'salmon',rep('beige',2)) ) boxplot( microbenchmark( df[complete.cases(df[,col_subset]), ], #na.omit(df), # col subset not supported in na.omit.data.frame df %>% drop_na(col_subset), dt[complete.cases(dt[,col_subset,with=FALSE]), ], na.omit(dt, cols=col_subset) # see ?na.omit.data.table ), xlab='', main = 'Performance: Drop NA obs. in select cols', col=c('lightblue','salmon',rep('beige',2)) ) 

Для вашего первого вопроса у меня есть код, который мне удобен, чтобы избавиться от всех НС. Спасибо за @Gregor, чтобы сделать его проще.

 final[!(rowSums(is.na(final))),] 

Во втором вопросе код является просто чередованием предыдущего решения.

 final[as.logical((rowSums(is.na(final))-5)),] 

Обратите внимание, что -5 – это количество столбцов в ваших данных. Это позволит исключить строки со всеми NA, поскольку rowSums добавляет до 5, и после вычитания они становятся нулями. На этот раз, как.logical, необходимо.

Я синтезатор :). Здесь я объединил ответы в одну функцию:

 #' keep rows that have a certain number (range) of NAs anywhere/somewhere and delete others #' @param df a data frame #' @param col restrict to the columns where you would like to search for NA; eg, 3, c(3), 2:5, "place", c("place","age") #' \cr default is NULL, search for all columns #' @param n integer or vector, 0, c(3,5), number/range of NAs allowed. #' \cr If a number, the exact number of NAs kept #' \cr Range includes both ends 3<=n<=5 #' \cr Range could be -Inf, Inf #' @return returns a new df with rows that have NA(s) removed #' @export ez.na.keep = function(df, col=NULL, n=0){ if (!is.null(col)) { # R converts a single row/col to a vector if the parameter col has only one col # see https://radfordneal.wordpress.com/2008/08/20/design-flaws-in-r-2-%E2%80%94-dropped-dimensions/#comments df.temp = df[,col,drop=FALSE] } else { df.temp = df } if (length(n)==1){ if (n==0) { # simply call complete.cases which might be faster result = df[complete.cases(df.temp),] } else { # credit: http://stackoverflow.com/a/30461945/2292993 log <- apply(df.temp, 2, is.na) logindex <- apply(log, 1, function(x) sum(x) == n) result = df[logindex, ] } } if (length(n)==2){ min = n[1]; max = n[2] log <- apply(df.temp, 2, is.na) logindex <- apply(log, 1, function(x) {sum(x) >= min && sum(x) <= max}) result = df[logindex, ] } return(result) } 

Предполагая, dat качестве вашего фрейма данных ожидаемый результат может быть достигнут с использованием

1. rowSums

 > dat[!rowSums((is.na(dat))),] gene hsap mmul mmus rnor cfam 2 ENSG00000199674 0 2 2 2 2 6 ENSG00000221312 0 1 2 3 2 

2. lapply

 > dat[!Reduce('|',lapply(dat,is.na)),] gene hsap mmul mmus rnor cfam 2 ENSG00000199674 0 2 2 2 2 6 ENSG00000221312 0 1 2 3 2 
 delete.dirt <- function(DF, dart=c('NA')) { dirty_rows <- apply(DF, 1, function(r) !any(r %in% dart)) DF <- DF[dirty_rows, ] } mydata <- delete.dirt(mydata) 

Вышефункция удаляет все строки из фрейма данных, который имеет «NA» в любом столбце и возвращает результирующие данные. Если вы хотите проверить несколько значений, например NA и ? change dart=c('NA') в параметре функции dart=c('NA', '?')

Я предполагаю, что это может быть более элегантно решено таким образом

  m <- matrix(1:25, ncol = 5) m[c(1, 6, 13, 25)] <- NA df <- data.frame(m) library(dplyr) df %>% filter_all(any_vars(is.na(.))) #> X1 X2 X3 X4 X5 #> 1 NA NA 11 16 21 #> 2 3 8 NA 18 23 #> 3 5 10 15 20 NA 
  • Только чтение ограниченного количества столбцов
  • Почему объекты R не печатаются в функции или в цикле «для»?
  • Условное слияние / замена в R
  • Выбор строк из фрейма данных на основе значений в векторе
  • Есть ли встроенная функция для поиска режима?
  • Как суммировать переменную по группе?
  • Использование ~ (тильда) в R-программировании Язык
  • Как создать список фреймов данных?
  • Отобразить точное значение переменной в R
  • Как объединить (объединить) кадры данных (внутренний, внешний, левый, правый)?
  • Как разбить фрейм данных?
  • Давайте будем гением компьютера.