أرغب في إزالة الأسطر في إطار البيانات هذا والتي:
أ) يحتوي على 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
ب) يحتوي على 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)
. بالنسبة للسؤال الثاني ، حاول نشره كسؤال آخر (للوضوح).
أفضل الطريقة التالية للتحقق مما إذا كانت الصفوف تحتوي على أي أرقام تعريفية:
row.has.na <- apply(final, 1, function(x){any(is.na(x))})
هذا يعيد الموجه المنطقي مع القيم التي تشير إلى ما إذا كان هناك أي NA في صف واحد. يمكنك استخدامه لمعرفة عدد الصفوف التي يجب عليك إسقاطها:
sum(row.has.na)
وفي النهاية أسقطها
final.filtered <- final[!row.has.na,]
بالنسبة لتصفية الصفوف التي تحتوي على جزء معين من NAs ، يصبح الأمر أصعب قليلاً (على سبيل المثال ، يمكنك تغذية "نهائي [، 5: 6]" إلى "تطبيق"). بشكل عام ، يبدو حل Joris Meys أكثر أناقة.
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
خيار آخر إذا كنت تريد تحكمًا أكبر في كيفية اعتبار الصفوف غير صالحة
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
AND cfam
. يمكن بعد ذلك تغيير المنطق المنطقي ليلائم متطلبات محددة.
إذا كنت تريد التحكم في عدد NAs الصالحة لكل صف ، فجرّب هذه الوظيفة. بالنسبة للعديد من مجموعات بيانات الاستطلاع ، يمكن للعديد من إجابات الأسئلة الفارغة أن تدمر النتائج. لذلك يتم حذفها بعد عتبة معينة. ستتيح لك هذه الوظيفة اختيار عدد التعدادات التي يمكن أن يحتوي عليها الصف قبل حذفها:
delete.na <- function(DF, n=0) {
DF[rowSums(is.na(DF)) <= n,]
}
بشكل افتراضي ، سيؤدي ذلك إلى القضاء على جميع NAs:
delete.na(final)
gene hsap mmul mmus rnor cfam
2 ENSG00000199674 0 2 2 2 2
6 ENSG00000221312 0 1 2 3 2
أو حدد الحد الأقصى لعدد NAs المسموح بها:
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
data.table
و na.omit()
مع param cols=
الاختياري.na.omit.data.table
هو الأسرع على مقياس الأداء الخاص بي (انظر أدناه) ، سواء بالنسبة لجميع الأعمدة أو للأعمدة المحددة (سؤال سؤال الجزء 2).
data.table
، فاستخدم complete.cases()
.على Vanilla data.frame
، complete.cases
أسرع من na.omit()
أو dplyr::drop_na()
. لاحظ أن na.omit.data.frame
لا يدعم cols=
.
في ما يلي مقارنة بين الأساليب الأساسية (الأزرق) و dplyr
(الوردي) و data.table
(الصفراء) لإسقاط إما الكل أو تحديد الملاحظات المفقودة ، على مجموعة بيانات افتراضية تضم 1 مليون ملاحظة من 20 متغيرًا رقميًا مع احتمال مستقل بنسبة 5٪ ، ومجموعة فرعية من 4 متغيرات للجزء 2.
قد تختلف النتائج وفقًا لطول مجموعة البيانات المحددة وعرضها وتناثرها.
لاحظ مقياس السجل على المحور ص.
#------- 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))
)
باستخدام حزمة dplyr يمكننا تصفية NA على النحو التالي:
dplyr::filter(df, !is.na(columnname))
سيعود هذا الصفوف التي تحتوي على قيمة واحدة على الأقل غير NA.
final[rowSums(is.na(final))<length(final),]
سيؤدي ذلك إلى إرجاع الصفوف التي تحتوي على قيمة لا تحتوي على NA على الأقل.
final[rowSums(is.na(final))<(length(final)-1),]
بالنسبة إلى سؤالك الأول ، لدي رمز أشعر بالراحة للتخلص من جميع NAs. شكرًا علىGregor لجعله أكثر بساطة.
final[!(rowSums(is.na(final))),]
للسؤال الثاني ، الكود هو مجرد بديل عن الحل السابق.
final[as.logical((rowSums(is.na(final))-5)),]
لاحظ -5 هو عدد الأعمدة في البيانات الخاصة بك. سيؤدي هذا إلى القضاء على الصفوف مع جميع NAs ، حيث يضيف rowSums ما يصل إلى 5 وتصبح أصفارًا بعد الطرح. هذه المرة ، as.logical ضروري.
يمكننا أيضا استخدام وظيفة مجموعة فرعية لهذا الغرض.
finalData<-subset(data,!(is.na(data["mmul"]) | is.na(data["rnor"])))
هذا سيعطي فقط تلك الصفوف التي لا تحتوي على NA في كل من 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)
}
بافتراض 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
و ?
، فغيّر Dart=c('NA')
في دالة param إلى 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
تتمثل إحدى الطرق العامة والخاصة بإعطاء رمز قابل للقراءة إلى حد ما في استخدام الدالة filter
ومتغيراتها في الحزمة 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(.))