【问题标题】:Move NAs to the end of each column in a data frame将 NA 移动到数据框中每列的末尾
【发布时间】:2014-09-16 12:39:30
【问题描述】:

我有这样一个数据框:

df <- structure(list(a = c(NA, NA, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L), b = c(NA, NA, NA, 1L, 2L, 3L, 4L, 5L, 6L, 7L), d = c(NA, NA, NA, NA, 1L, 2L, 3L, 4L, 5L, 6L)), .Names = c("a", "b", "d"), row.names = c(NA, -10L), class = "data.frame")

> df
    a  b  d
1  NA NA NA
2  NA NA NA
3   1 NA NA
4   2  1 NA
5   3  2  1
6   4  3  2
7   5  4  3
8   6  5  4
9   7  6  5
10  8  7  6

在每一列中,我想将非NA 值移到开头,并将NAs 移到末尾:

> df.out
    a  b  d
1   1  1  1
2   2  2  2
3   3  3  3
4   4  4  4
5   5  5  5
6   6  6  6
7   7  7 NA
8   8 NA NA
9  NA NA NA
10 NA NA NA

更新以使我的问题更清晰..

df <- structure(list(a = c(NA, NA, 1, 5, 34, 7, 3, 5, 8, 4), b = c(NA, 
NA, NA, 57, 2, 7, 9, 5, 12, 100), d = c(NA, NA, NA, NA, 5, 7, 
2, 8, 2, 5)), .Names = c("a", "b", "d"), row.names = c(NA, -10L
), class = "data.frame")

> df
    a   b  d
1  NA  NA NA
2  NA  NA NA
3   1  NA NA
4   5  57 NA
5  34   2  5
6   7   7  7
7   3   9  2
8   5   5  8
9   8  12  2
10  4 100  5

应该导致:

    a   b  d
1   1  57  5
2   5   2  7
3  34   7  2
4   7   9  8
5   3   5  2
6   5  12  5
7   8 100 NA
8   4  NA NA
9  NA  NA NA
10 NA  NA NA

似乎是一项简单的任务,但我不知道从哪里开始......你能帮忙吗?

【问题讨论】:

  • 请注意,这里的数据实际上发生了变化(从行的角度来看):这是故意的,还是您只是想将 NA 排序到底部?
  • 这是故意的,我基本上希望所有数字都按行对齐,因此每一列都应该单独向上移动。
  • 无法想象你需要它做什么,但我发布了一个答案。您可能需要寻找 data.table 解决方案,它们通常是最快的。
  • 等一下...你是说如果你有一个像c(NA, 2, 3, 1)这样的简单向量,那么结果就必须是c(2,3,1,NA)?那真的会改变这个问题!
  • @PascalvKooten 是的,完全正确,抱歉,如果不清楚,我会更新问题!

标签: r dataframe sorting na


【解决方案1】:

使用lapply 的另一种解决方案(无需根据您的 cmets 对数据进行排序/重新排序)

df[] <- lapply(df, function(x) c(x[!is.na(x)], x[is.na(x)]))
df
#     a   b  d
# 1   1  57  5
# 2   5   2  7
# 3  34   7  2
# 4   7   9  8
# 5   3   5  2
# 6   5  12  5
# 7   8 100 NA
# 8   4  NA NA
# 9  NA  NA NA
# 10 NA  NA NA

或使用data.table 以通过引用更新df,而不是创建它的副本(该解决方案也不会对您的数据进行排序)

library(data.table)
setDT(df)[, names(df) := lapply(.SD, function(x) c(x[!is.na(x)], x[is.na(x)]))]
df
#      a   b  d
#  1:  1  57  5
#  2:  5   2  7
#  3: 34   7  2
#  4:  7   9  8
#  5:  3   5  2
#  6:  5  12  5
#  7:  8 100 NA
#  8:  4  NA NA
#  9: NA  NA NA
# 10: NA  NA NA

一些基准测试显示,基本解决方案是迄今为止最快的:

library("microbenchmark")
david <- function() lapply(df, function(x) c(x[!is.na(x)], x[is.na(x)]))
dt <- setDT(df)
david.dt <- function() dt[, names(dt) := lapply(.SD, function(x) c(x[!is.na(x)], x[is.na(x)]))]

microbenchmark(as.data.frame(lapply(df, beetroot)), david(), david.dt())
# Unit: microseconds
#                                 expr      min       lq   median        uq      max neval
#  as.data.frame(lapply(df, beetroot)) 1145.224 1215.253 1274.417 1334.7870 4028.507   100
#                              david()  116.515  127.382  140.965  149.7185  308.493   100
#                           david.dt() 3087.335 3247.920 3330.627 3415.1460 6464.447   100

【讨论】:

  • 恭喜你从一开始就弄清楚了 OP 想要什么。
  • 我喜欢它。我对类似过程的唯一其他想法是:lapply(df, function(x) x[order(is.na(x))] ) 这可能在时间上效率较低。
  • 感谢您的回答-抱歉,同一时间有很多答案/cmets,所以我完全错过了您的答案,直到现在才看到!
  • 我已经根据您的新数据集更新了结果。解决方案保持不变
  • @DavidArenburg 希望你不介意,但我为你的答案添加了一些基准。
【解决方案2】:

在完全误解问题之后,这是我的最终答案:

# named after beetroot for being the first to ever need this functionality
beetroot <- function(x) {
    # count NA
    num.na <- sum(is.na(x))
    # remove NA
    x <- x[!is.na(x)]
    # glue the number of NAs at the end
    x <- c(x, rep(NA, num.na))
    return(x)
}

# apply beetroot over each column in the dataframe
as.data.frame(lapply(df, beetroot))

它将计算 NA,删除 NA,并将 NA 粘在数据框中每一列的底部。

【讨论】:

  • 您不需要在上一个示例中执行as.matrixapply 自动强制转换为矩阵。但是您应该将其包装在 as.data.frame 中以将其返回到 data.frame。
  • 谢谢,但正如在 Thomas 的回答中,这会对每列中的值进行排序,但我需要每列中的行顺序保持不变(NA 的位置应该是唯一的变化)
  • 太棒了,就是这样!我感到很荣幸 :D 如果商业软件公司能够生成像样的 csv 输出文件,我也不需要这个 ;)
【解决方案3】:

为了好玩,你也可以使用length&lt;-na.omit

以下是该组合的作用:

x <- c(NA, 1, 2, 3)
x
# [1] NA  1  2  3
`length<-`(na.omit(x), length(x))
# [1]  1  2  3 NA

应用于您的问题,解决方案是:

df[] <- lapply(df, function(x) `length<-`(na.omit(x), nrow(df)))
df
#     a   b  d
# 1   1  57  5
# 2   5   2  7
# 3  34   7  2
# 4   7   9  8
# 5   3   5  2
# 6   5  12  5
# 7   8 100 NA
# 8   4  NA NA
# 9  NA  NA NA
# 10 NA  NA NA

【讨论】:

    【解决方案4】:

    如果您的列数较少,我建议:

    data.frame( a=sort(example$a, na.last=T), b=sort(example$b, na.last=T), d=sort(example$d, na.last=T))
    

    最好, 阿迪_

    【讨论】:

      【解决方案5】:

      您可以使用 dedupewider 包中的 na_move 函数。

      library(dedupewider)
      
      na_move(df, direction = "bottom")
      #>     a   b  d
      #> 1   1  57  5
      #> 2   5   2  7
      #> 3  34   7  2
      #> 4   7   9  8
      #> 5   3   5  2
      #> 6   5  12  5
      #> 7   8 100 NA
      #> 8   4  NA NA
      #> 9  NA  NA NA
      #> 10 NA  NA NA
      

      【讨论】:

        猜你喜欢
        • 2022-12-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-22
        • 2021-09-09
        相关资源
        最近更新 更多