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

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

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

             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 только в некоторых столбцах, поэтому я также могу получить этот результат:

             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
662
задан 01 февр. '11 в 14:52
источник поделиться
15 ответов

Также проверьте 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 намного более понятно и быстрее.

841
ответ дан 01 февр. '11 в 15:21
источник

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

205
ответ дан 01 февр. '11 в 15:00
источник

Я предпочитаю следующий способ проверить, содержат ли строки любые 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]" на "apply" ). Как правило, решение Joris Meys кажется более элегантным.

78
ответ дан 03 февр. '11 в 0:58
источник

Если вам нравятся трубы (%>%), tidyr new 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
53
ответ дан 16 авг. '16 в 11:49
источник

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

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. Логическая логика может быть затем изменена в соответствии с конкретными требованиями.

36
ответ дан 05 нояб. '13 в 9:30
источник

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

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

Или укажите максимальное допустимое число разрешенных NA:

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
31
ответ дан 26 мая '15 в 17:49
источник

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

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

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

final[rowSums(is.na(final))<(length(final)-1),]
14
ответ дан 19 сент. '14 в 15:36
источник

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

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

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

12
ответ дан 12 нояб. '14 в 1:15
источник

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

dplyr::filter(df,  !is.na(columnname))
12
ответ дан 12 апр. '17 в 8:44
источник

Если производительность является приоритетом, используйте 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.

enter image description here

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

#-------  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<p] <- NA
  return(m)
}
df <- cbind( data.frame(id = paste0('ID',seq(row_size)), 
                        stringsAsFactors = FALSE),
             data.frame(fakeData(row_size, col_size, p_missing) )
             )
dt <- data.table(df)

par(las=3, mfcol=c(1,2), mar=c(22,4,1,1)+0.1)
boxplot(
  microbenchmark(
    df[complete.cases(df), ],
    na.omit(df),
    df %>% 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))
)
11
ответ дан 16 февр. '18 в 18:41
источник

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

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

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

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

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

11
ответ дан 09 февр. '16 в 20:52
источник

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

#' 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)
}
8
ответ дан 03 февр. '16 в 20:48
источник

Предполагая 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
5
ответ дан 15 марта '17 в 19:51
источник
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', '?')

1
ответ дан 23 февр. '18 в 1:19
источник

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

  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
0
ответ дан 08 мая '18 в 23:35
источник

Другие вопросы по меткам или Задайте вопрос