ฉันมีปัญหากับกรอบข้อมูลและไม่สามารถจริงๆแก้ไขปัญหาที่ตัวเอง: dataframeมีพลคุณสมบัติเป็นคอลัมน์และแต่ละแถวเป็นหนึ่งในชุดข้อมูล
คำถามคือ:
วิธีการกำจัดคอลัมน์ที่ทุกค่าแถวคือ NA ?
ฉันมีปัญหากับกรอบข้อมูลและไม่สามารถจริงๆแก้ไขปัญหาที่ตัวเอง: dataframeมีพลคุณสมบัติเป็นคอลัมน์และแต่ละแถวเป็นหนึ่งในชุดข้อมูล
คำถามคือ:
วิธีการกำจัดคอลัมน์ที่ทุกค่าแถวคือ NA ?
คำตอบ:
ลองสิ่งนี้:
df <- df[,colSums(is.na(df))<nrow(df)]
ทั้งสองวิธีที่นำเสนอป่านนี้ล้มเหลวด้วยชุดข้อมูลขนาดใหญ่เป็น (ในหมู่ปัญหาหน่วยความจำอื่น ๆ ) พวกเขาสร้างซึ่งจะเป็นวัตถุที่มีขนาดเดียวกับis.na(df)
df
ต่อไปนี้เป็นสองวิธีที่มีหน่วยความจำและเวลามีประสิทธิภาพมากขึ้น
วิธีการใช้ Filter
Filter(function(x)!all(is.na(x)), df)
และแนวทางการใช้ data.table (สำหรับเวลาทั่วไปและประสิทธิภาพของหน่วยความจำ)
library(data.table)
DT <- as.data.table(df)
DT[,which(unlist(lapply(DT, function(x)!all(is.na(x))))),with=F]
big_data <- replicate(10, data.frame(rep(NA, 1e6), sample(c(1:8,NA),1e6,T), sample(250,1e6,T)),simplify=F)
bd <- do.call(data.frame,big_data)
names(bd) <- paste0('X',seq_len(30))
DT <- as.data.table(bd)
system.time({df1 <- bd[,colSums(is.na(bd) < nrow(bd))]})
# error -- can't allocate vector of size ...
system.time({df2 <- bd[, !apply(is.na(bd), 2, all)]})
# error -- can't allocate vector of size ...
system.time({df3 <- Filter(function(x)!all(is.na(x)), bd)})
## user system elapsed
## 0.26 0.03 0.29
system.time({DT1 <- DT[,which(unlist(lapply(DT, function(x)!all(is.na(x))))),with=F]})
## user system elapsed
## 0.14 0.03 0.18
data.frame
แม้ว่า data.table
ไม่มีอะไรที่นี่ที่ต้องการจริงๆ ที่สำคัญเป็นที่หลีกเลี่ยงสำเนาของวัตถุทั้งหมดทำโดยlapply
is.na(df)
+10 สำหรับการชี้ให้เห็น
bd1 <- bd[, unlist(lapply(bd, function(x), !all(is.na(x))))]
,
after function(x)
- ขอบคุณสำหรับตัวอย่าง btw
dplyr
ตอนนี้มีselect_if
คำกริยาที่อาจเป็นประโยชน์ที่นี่:
library(dplyr)
temp <- data.frame(x = 1:5, y = c(1,2,NA,4, 5), z = rep(NA, 5))
not_all_na <- function(x) any(!is.na(x))
not_any_na <- function(x) all(!is.na(x))
> temp
x y z
1 1 1 NA
2 2 2 NA
3 3 NA NA
4 4 4 NA
5 5 5 NA
> temp %>% select_if(not_all_na)
x y
1 1 1
2 2 2
3 3 NA
4 4 4
5 5 5
> temp %>% select_if(not_any_na)
x
1 1
2 2
3 3
4 4
5 5
dplyr
แก้ไข ก็ไม่ผิดหวัง ขอบคุณ!
อีกวิธีหนึ่งก็คือการใช้apply()
ฟังก์ชั่น
หากคุณมี data.frame
df <- data.frame (var1 = c(1:7,NA),
var2 = c(1,2,1,3,4,NA,NA,9),
var3 = c(NA)
)
จากนั้นคุณสามารถใช้apply()
เพื่อดูว่าคอลัมน์ใดที่ตอบสนองเงื่อนไขของคุณและเพื่อให้คุณสามารถทำการย่อยเช่นเดียวกับในคำตอบโดย Musa เพียงapply
วิธีการเท่านั้น
> !apply (is.na(df), 2, all)
var1 var2 var3
TRUE TRUE FALSE
> df[, !apply(is.na(df), 2, all)]
var1 var2
1 1 1
2 2 2
3 3 1
4 4 3
5 5 4
6 6 NA
7 7 NA
8 NA 9
มาถึงช่วงท้ายเกม แต่คุณสามารถใช้janitor
แพ็คเกจได้ ฟังก์ชันนี้จะลบคอลัมน์ที่เป็น NA ทั้งหมดและสามารถเปลี่ยนเพื่อลบแถวที่มี NA ทั้งหมดได้เช่นกัน
df <- janitor::remove_empty(df, which = "cols")
df[sapply(df, function(x) all(is.na(x)))] <- NULL
ตัวเลือกอื่นด้วยpurrr
แพ็คเกจ:
library(dplyr)
df <- data.frame(a = NA,
b = seq(1:5),
c = c(rep(1, 4), NA))
df %>% purrr::discard(~all(is.na(.)))
df %>% purrr::keep(~!all(is.na(.)))
ฉันหวังว่านี่อาจช่วยได้เช่นกัน มันสามารถสร้างเป็นคำสั่งเดียว แต่ฉันพบว่ามันง่ายกว่าสำหรับฉันที่จะอ่านโดยแบ่งมันออกเป็นสองคำสั่ง ฉันทำฟังก์ชั่นตามคำแนะนำต่อไปนี้และทำงานเร็ว
naColsRemoval = function (DataTable) {
na.cols = DataTable [ , .( which ( apply ( is.na ( .SD ) , 2 , all ) ) )]
DataTable [ , unlist (na.cols) := NULL , with = F]
}
.SD จะอนุญาตให้ จำกัด การตรวจสอบเฉพาะส่วนของตารางหากคุณต้องการ แต่จะใช้ทั้งตารางเป็น
base R
ตัวเลือกที่มีประโยชน์อาจเป็นcolMeans()
:
df[, colMeans(is.na(df)) != 1]
คุณสามารถใช้แพ็คเกจ Janitor remove_empty
library(janitor)
df %>%
remove_empty(c("rows", "cols")) #select either row or cols or both
นอกจากนี้ยังมีวิธี dplyr อื่น
library(dplyr)
df %>% select_if(~all(!is.na(.)))
หรือ
df %>% select_if(colSums(!is.na(.)) == nrow(df))
สิ่งนี้มีประโยชน์หากคุณต้องการยกเว้น / รักษาคอลัมน์ที่มีค่าที่หายไปจำนวนหนึ่งเช่น
df %>% select_if(colSums(!is.na(.))>500)