Я хотел бы удалить строки в этом фрейме данных, которые:
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
б) содержать 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
Также проверьте 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)
. Что касается второго вопроса, попробуйте опубликовать его как другой вопрос (для ясности).
В tidyr
появилась новая функция 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
Я предпочитаю следующий способ проверки того, содержат ли строки какие-либо NA:
row.has.na <- apply(final, 1, function(x){any(is.na(x))})
Это возвращает логический вектор со значениями, обозначающими, есть ли в ряду NA. Вы можете использовать его, чтобы узнать, сколько строк вам придется отбросить:
sum(row.has.na)
и в конечном итоге отбросить их
final.filtered <- final[!row.has.na,]
Для фильтрации строк с определенной частью NA это становится немного сложнее (например, вы можете скормить 'final[,5:6]' в 'apply'). В целом, решение Joris Meys' кажется более элегантным.
Другой вариант, если вы хотите большего контроля над тем, как строки считается недействительным
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 удаляется, поскольку это единственная строка, содержащая NAS для обоих rnor
и 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.omit.data.table
является самым быстрым на мой тест (см. ниже), если для всех столбцов или выбрать столбцы (ОП вопрос Часть 2).
, используйте
полный.случаи()`.На данных ваниль.рама
, complete.cases
быстрее, чем на.опустить()](https://www.rdocumentation.org/packages/stats/versions/3.4.3/topics/na.fail) или [
dplyr::drop_na()](https://www.rdocumentation.org/packages/tidyr/versions/0.8.0/topics/drop_na). Обратите внимание, что на.опустить.данных.рамка "не поддерживает" смещ_по_столбцам=
.
Тест ###результат
Вот сравнение базы (синий), dplyr
(розовый), и данных.таблица` (желтый) методов для сбрасывания либо все, либо выбрать отсутствующих наблюдений, на условных данных 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<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))
)
Это будет возвращать строки, которые имеют по крайней мере один-на значение.
final[rowSums(is.na(final))<length(final),]
Это будет возвращать строки, которые имеют по крайней мере два номера-на стоимость.
final[rowSums(is.na(final))<(length(final)-1),]
Что касается вашего первого вопроса, у меня есть код, который меня устраивает, чтобы избавиться от всех нас. Спасибо @Грегор, чтобы сделать его проще.
final[!(rowSums(is.na(final))),]
Что касается второго вопроса, то код-это просто чередование от предыдущего решения.
final[as.logical((rowSums(is.na(final))-5)),]
Обратите внимание на -5-количество столбцов в данных. Это позволит устранить строк с всех нас, поскольку rowSums добавляет до 5 и они становятся нулями после вычитания. На этот раз, как.логически необходимо.
Мы также можем использовать функцию подмножества для этого.
finalData<-subset(data,!(is.na(data["mmul"]) | is.na(data["rnor"])))
Это даст только те строки, которые не имеют на обоих mmul и rnor
Я синтезатор:). Здесь я объединил ответы в одну функцию:
#' 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)
}
Предполагая, что дат
, как ваши таблицы данных, ожидаемые результаты могут быть достигнуты при использовании
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)
Выше функция служит для удаления всех строк из фрейма данных, который 'Н' в любом столбце и возвращает полученные данные. Если вы хотите, чтобы проверить несколько значений, как на
и ?
изменить Дарт=с('Н') в функции param в
Дарт=с('на', '?')`
Один из подходов, который'ы как общие, так и дает довольно-читабельный код, чтобы использовать "фильтр" функция и ее вариантов в dplyr пакет (filter_all
, filter_at
, filter_if
):
library(dplyr)
vars_to_check <- c("rnor", "cfam")
# Filter a specific list of columns to keep only non-missing entries
df %>%
filter_at(.vars = vars(one_of(vars_to_check)),
~ !is.na(.))
# Filter all the columns to exclude NA
df %>%
filter_all(~ !is.na(.))
# Filter only numeric columns
df %>%
filter_if(is.numeric,
~ !is.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