ลบคอลัมน์ออกจาก dataframe โดยที่ค่าทั้งหมดคือ NA


149

ฉันมีปัญหากับกรอบข้อมูลและไม่สามารถจริงๆแก้ไขปัญหาที่ตัวเอง: dataframeมีพลคุณสมบัติเป็นคอลัมน์และแต่ละแถวเป็นหนึ่งในชุดข้อมูล

คำถามคือ:
วิธีการกำจัดคอลัมน์ที่ทุกค่าแถวคือ NA ?

คำตอบ:


155

ลองสิ่งนี้:

df <- df[,colSums(is.na(df))<nrow(df)]

3
สิ่งนี้จะสร้างขนาดวัตถุของวัตถุเก่าซึ่งเป็นปัญหาเกี่ยวกับหน่วยความจำบนวัตถุขนาดใหญ่ ดีกว่าที่จะใช้ฟังก์ชั่นเพื่อลดขนาด คำตอบที่ร้องโดยใช้ตัวกรองหรือใช้ data.table จะช่วยให้การใช้หน่วยความจำของคุณ
mtelesha

3
สิ่งนี้ดูเหมือนจะไม่ทำงานกับคอลัมน์ที่ไม่ใช่ตัวเลข
verbamour

มันจะเปลี่ยนชื่อคอลัมน์หากพวกเขาซ้ำกัน
Peter.k

97

ทั้งสองวิธีที่นำเสนอป่านนี้ล้มเหลวด้วยชุดข้อมูลขนาดใหญ่เป็น (ในหมู่ปัญหาหน่วยความจำอื่น ๆ ) พวกเขาสร้างซึ่งจะเป็นวัตถุที่มีขนาดเดียวกับ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]

ตัวอย่างที่ใช้ข้อมูลขนาดใหญ่ (30 คอลัมน์ 1 แถว 6 แถว)

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 

6
ดีมาก. คุณสามารถทำเช่นเดียวกันกับdata.frameแม้ว่า data.tableไม่มีอะไรที่นี่ที่ต้องการจริงๆ ที่สำคัญเป็นที่หลีกเลี่ยงสำเนาของวัตถุทั้งหมดทำโดยlapply is.na(df)+10 สำหรับการชี้ให้เห็น
Matt Dowle

1
คุณจะทำอย่างไรกับ data.frame? @ matt-dowle
s_a

8
@s_a, bd1 <- bd[, unlist(lapply(bd, function(x), !all(is.na(x))))]
mnel

6
@mnel ฉันคิดว่าคุณต้องลบ,after function(x)- ขอบคุณสำหรับตัวอย่าง btw
Thieme Hennis

1
คุณสามารถทำได้เร็วขึ้นด้วย: = หรือด้วยชุด ()?
skan

49

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แก้ไข ก็ไม่ผิดหวัง ขอบคุณ!
Andrew Brēza

ฉันพบว่าสิ่งนี้มีปัญหาว่ามันจะลบตัวแปรที่มีค่าส่วนใหญ่ แต่ไม่หายไปทั้งหมด
MBorg

15

อีกวิธีหนึ่งก็คือการใช้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

3
ฉันคาดหวังว่าสิ่งนี้จะเร็วขึ้นเนื่องจากโซลูชัน colSum () ดูเหมือนว่าจะทำงานได้มากขึ้น แต่ในชุดทดสอบของฉัน (213 obs. ของ 1,614 ตัวแปรก่อนและ 1377 ตัวแปรหลังจากนั้น) มันใช้เวลานานกว่า 3 เท่า (แต่ +1 สำหรับวิธีการที่น่าสนใจ)
Darren Cook

10

มาถึงช่วงท้ายเกม แต่คุณสามารถใช้janitorแพ็คเกจได้ ฟังก์ชันนี้จะลบคอลัมน์ที่เป็น NA ทั้งหมดและสามารถเปลี่ยนเพื่อลบแถวที่มี NA ทั้งหมดได้เช่นกัน

df <- janitor::remove_empty(df, which = "cols")



4

คำตอบที่ยอมรับไม่สามารถใช้ได้กับคอลัมน์ที่ไม่ใช่ตัวเลข จากคำตอบนี้การทำงานต่อไปนี้กับคอลัมน์ที่มีชนิดข้อมูลต่างกัน

Filter(function(x) !all(is.na(x)), df)

มีคนอื่นโพสต์คำตอบเดียวกันในหัวข้อนี้ 4 ปีก่อนคุณ ... ดูคำตอบของ mnel ด้านล่าง
André.B

2

ตัวเลือกอื่นด้วย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(.)))

1

ฉันหวังว่านี่อาจช่วยได้เช่นกัน มันสามารถสร้างเป็นคำสั่งเดียว แต่ฉันพบว่ามันง่ายกว่าสำหรับฉันที่จะอ่านโดยแบ่งมันออกเป็นสองคำสั่ง ฉันทำฟังก์ชั่นตามคำแนะนำต่อไปนี้และทำงานเร็ว

naColsRemoval = function (DataTable) { na.cols = DataTable [ , .( which ( apply ( is.na ( .SD ) , 2 , all ) ) )] DataTable [ , unlist (na.cols) := NULL , with = F] }

.SD จะอนุญาตให้ จำกัด การตรวจสอบเฉพาะส่วนของตารางหากคุณต้องการ แต่จะใช้ทั้งตารางเป็น



0

คุณสามารถใช้แพ็คเกจ 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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.