【问题标题】:Distance from the closest non NA value in a dataframe与数据框中最接近的非 NA 值的距离
【发布时间】:2018-07-22 12:36:26
【问题描述】:

我有以下数据框 df,我想为每一行添加一个与最近的非 NA 值的距离的列。

df <- data.frame(x = 1:20)
df[c(1, 3, 4, 5, 11, 14, 15, 16), "x"] <-  NA

换句话说,我正在寻找以下值:

df$distance <- c(1, 0, 1, 2, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 2, 1, 0, 0, 0, 0)

如何自动执行此操作?

【问题讨论】:

    标签: r dataframe


    【解决方案1】:

    x 成为包含NA 的向量,你的问题是

    a <- which(!is.na(x))
    b <- which(is.na(x))
    

    为每个b[i] 查找min(abs(a - b[i]))

    使用 R 代码高效不容易完成这类任务。用编译后的代码编写循环通常是更好的选择;除非某些包中的某些功能已经为我们完成了这项工作。

    以下是一些幼稚但直接的解决方案。

    如果x不太长,我们可以使用outer

    distance <- numeric(length(x))
    distance[is.na(x)] <- apply(abs(outer(a, b, "-")), 2L, min)
    

    如果它很长并且outer的内存使用成为问题,我们可能会这样做

    distance <- numeric(length(x))
    distance[is.na(x)] <- sapply(b, function (bi) min(abs(bi - a)))
    

    注意,就算法而言,没有一种方法是真正有效的。

    【讨论】:

    • 我有 20000 行。应该没问题的。
    【解决方案2】:

    这是使用rlerank 的另一种方法:

    library(dplyr)
    library(magrittr)
    
    df <- data.frame(x=seq(1, 20))
    df[c("1", "3", "4", "5", "11", "14", "15", "16"), 1] <-  NA
    
    rle.len <- df$x %>% is.na %>% rle %$% lengths
    
    df %>% 
      mutate(na.seq=rle.len %>% seq_along %>% rep(rle.len)) %>% 
      group_by(na.seq) %>%
      mutate(distance=ifelse(is.na(x), pmin(rank(na.seq, ties.method = "first"),
                                            rank(na.seq, ties.method = "last")), 0))
    
        x na.seq distance
    1  NA      1        1
    2   2      2        0
    3  NA      3        1
    4  NA      3        2
    5  NA      3        1
    

    【讨论】:

    • 谢谢。我刚刚注意到您的方法不适用于在最后一行中使用 NA 完成数据帧的情况。它将考虑与非 NA 值距离为 1 的最后一行(即 NA)。
    【解决方案3】:

    您可以使用findInterval。首先,找到NAnon-NA 值的索引,并初始化一个距离列:

    na <- which(is.na(df$x))
    non_na <- which(!is.na(df$x))
    df$distance2 <- 0
    

    然后,使用findInterval 和非NA 索引的中点作为断点,找出NA 索引落入哪个区间。使用区间提取对应的非NA 索引,计算与@987654329 的绝对差@indices,并在NAindices 处分配这些:

    df$distance2[na] <- abs(na - non_na[findInterval(na, (non_na[-length(non_na)] + non_na[-1]) / 2) + 1])
    
    df
    #     x distance distance2
    # 1  NA        1         1
    # 2   2        0         0
    # 3  NA        1         1
    # 4  NA        2         2
    # 5  NA        1         1
    # 6   6        0         0
    # 7   7        0         0
    # 8   8        0         0
    # 9   9        0         0
    # 10 10        0         0
    # 11 NA        1         1
    # 12 12        0         0
    # 13 13        0         0
    # 14 NA        1         1
    # 15 NA        2         2
    # 16 NA        1         1
    # 17 17        0         0
    # 18 18        0         0
    # 19 19        0         0
    # 20 20        0         0
    

    【讨论】:

      【解决方案4】:

      一种方法是使用raster 包中的distance(),在使用包将矩阵转换为使用raster() function 的RasterLayer 对象之后。

      这个包是为地图设计的,所以当你使用raster()时,你的对象会有单位、分辨率等。因此,当你使用distance()时,距离一个元素的距离可能非常大非 NA(对我来说是 15796.35)。只需除以这个数量(可能是 round() 由于舍入错误)即可得到答案。

      例如,如果我有一个名为a1 的 NA 数组对象:

      > a1 = array(
          c(
             c(1, 5, 6, NA, 1, 2, 5),
             c(3, 4, NA, NA, NA, 8, 1),
             c(5, 1, 7, NA, 2, 3, 7),
             c(8, 1, 1, 2, 3, 6, 2)
           ),
          c(7, 4)
        )
      > r1 = raster(a1)
      > d1 = distance(r1)
      > as.matrix(d1)    
      
               [,1]     [,2]     [,3] [,4]
      [1,]     0.00     0.00     0.00    0
      [2,]     0.00     0.00     0.00    0
      [3,]     0.00 15796.35     0.00    0
      [4,] 15796.33 31592.66 15796.33    0
      [5,]     0.00 15796.33     0.00    0
      [6,]     0.00     0.00     0.00    0
      [7,]     0.00     0.00     0.00    0
      
      > round(
           as.matrix(d1) / 15796.35,
           0
        )
      
           [,1] [,2] [,3] [,4]
      [1,]    0    0    0    0
      [2,]    0    0    0    0
      [3,]    0    1    0    0
      [4,]    1    2    1    0
      [5,]    0    1    0    0
      [6,]    0    0    0    0
      [7,]    0    0    0    0
      

      你的答案是什么。不过,我不知道distance() 函数背后的代码效率如何,所以我不知道它是否需要一段时间。

      编辑:在具有 29000 个 NA 的数组对象上进行了测试,需要很长时间。我建议您仅将其用于具有少量 NA 的对象。

      【讨论】:

        猜你喜欢
        • 2023-04-09
        • 2018-12-20
        • 1970-01-01
        • 2014-06-13
        • 2011-11-12
        • 1970-01-01
        相关资源
        最近更新 更多