2016-07-22 64 views
2

我在尋找一個簡單的方法來與至少一個NA將所有行數據幀/ DataTable的底部。例如:移動NA的底部

> df <- data.table(aaa=c(1,2,3,4,NA,6,7), 
        bbb=c(1,9,5,NA,3,NA,9), 
        ccc=c(NA,3,NA,4,8,NA,2) 
        ) 
> df 
    aaa bbb ccc 
1: 1 1 NA 
2: 2 9 3 
3: 3 5 NA 
4: 4 NA 4 
5: NA 3 8 
6: 6 NA NA 
7: 7 9 2 

會變成這樣的事情:

> df2 <- moveNAtoBottom(df) 
> df2 
    aaa bbb ccc 
1: 2 9 3 
2: 7 9 2 
3: 1 1 NA 
4: 3 5 NA 
5: 4 NA 4 
6: NA 3 8 
7: 6 NA NA 

即頂部沒有NA的所有列。

注:

  • 現在,我真的不關心,如果應用有任何排序的行,或者如果爲了保持或逆轉......只要表現有兩個部分,上面一個沒有來港,下一個爲NAS
  • 這可以在dataframes或數據表
  • 依賴是正常

更妙的是,如果我能能夠在指定列哪個搜索NA必須被應用。例如,仍與前面的例子:

> df3 <- moveNAtoBottom(df, applyTo = c("bbb", "ccc")) 
> df3 
    aaa bbb ccc 
1: 2 9 3 
2: NA 3 8 
3: 7 9 2 
4: 1 1 NA 
5: 3 5 NA 
6: 4 NA 4 
7: 6 NA NA 

感謝

回答

1

一個簡單的基礎R解決方案是:

> df <- data.frame(aaa=c(1,2,3,4,NA,6,7), 
+     bbb=c(1,9,5,NA,3,NA,9), 
+     ccc=c(NA,3,NA,4,8,NA,2)) 
> ok <- complete.cases(df) 
> rbind(df[ok,], df[!ok,]) 
    aaa bbb ccc 
2 2 9 3 
7 7 9 2 
1 1 1 NA 
3 3 5 NA 
4 4 NA 4 
5 NA 3 8 
6 6 NA NA 

而且只選擇一些列:

> ok <- complete.cases(df[, c("bbb","ccc")]) 
> rbind(df[ok,], df[!ok,]) 
    aaa bbb ccc 
2 2 9 3 
5 NA 3 8 
7 7 9 2 
1 1 1 NA 
3 3 5 NA 
4 4 NA 4 
6 6 NA NA 
1

嘗試:

na=apply(df,1,function(x)sum(is.na(x))) 

df[order(na),] 

或函數

moveNAtoBottom<-function(df, applyTo=NULL){ 
    df<-as.data.frame(df) 
    if (is.null(applyTo)) applyTo=colnames(df) 
    na=rowSums(is.na(df[,applyTo,drop=F])) 
    data.table(df[order(na),]) 
} 

moveNAtoBottom(df) 
moveNAtoBottom(df,c("ccc", "aaa")) 

    aaa bbb ccc 
1: 2 9 3 
2: 4 NA 4 
3: 7 9 2 
4: 1 1 NA 
5: 3 5 NA 
6: NA 3 8 
7: 6 NA NA 
5

你可以這樣做:

library(dplyr) 
df %>% 
    arrange(rowSums(is.na(.))) 

其中給出:

# aaa bbb ccc 
#1 2 9 3 
#2 7 9 2 
#3 1 1 NA 
#4 3 5 NA 
#5 4 NA 4 
#6 NA 3 8 
#7 6 NA NA 

還是應該要指定列名:

df %>% 
    arrange(rowSums(is.na(select_(., 'bbb:ccc')))) 

其中給出:

# aaa bbb ccc 
#1 2 9 3 
#2 NA 3 8 
#3 7 9 2 
#4 1 1 NA 
#5 3 5 NA 
#6 4 NA 4 
#7 6 NA NA 
+1

效果很好, 謝謝。我能問一個關於'dplyr'問題?爲什麼它與管道的工作,但並非沒有?如果他們以同樣的方式爲'magrittr'的管道工程,人們會認爲'安排(rowSums(是。na(select_(df,'bbb:ccc'))))'也可以,不是嗎? –

+1

你需要指定你想要在哪個'data'上執行'arrange()':嘗試'arrange(df,rowSums(is.na(select_(df,'bbb:ccc'))))' –

+1

哦,當然,我明白,謝謝。 –

5

由於OP使用data.table,與data.table一個選項循環通過與lapply列,檢查它是否是NA(is.na),獲得該行逐行總和與Reduce,並用它來order「DF」的行。

df[order(Reduce(`+`, lapply(df, is.na)))] 
# aaa bbb ccc 
#1: 2 9 3 
#2: 7 9 2 
#3: 1 1 NA 
#4: 3 5 NA 
#5: 4 NA 4 
#6: NA 3 8 
#7: 6 NA NA 

這裏還有一個類似的方法:

df[, anyna := FALSE] 
for (k in names(df)) df[!(anyna), anyna := is.na(get(k))] 
df[order(anyna)][, anyna := NULL][] 
+3

不錯。有一個未導出的函數'is_na',在這裏非常有用。也許我們應該出口它。 – Arun

+0

它也有穩定版本。只是沒有出口。 – Arun

+0

@Arun謝謝,這將是有益的出口 – akrun