R语言 我在试图找到一种方法,循环遍历一系列数字,直到列表中的所有数字都与缺失或NA之前的最后一个数字相同

0s7z1bwu  于 2023-03-27  发布在  其他
关注(0)|答案(4)|浏览(109)

让我们使用这些数字作为示例,因为我不能共享实际数据end=c(1,3,6,NA,6,7,8,NA,12,23,NA)。我需要将其转换为end=c(6,6,6,NA,8,8,8,NA,23,23,NA)。

`repeat{
  end[k]=end[k+1]
  k=k+1
  if(is.na(end[k+1]) | k==length(end)){
    
    break
  }
}`

我已经尝试了这个作为第一步,但我被困在如何循环通过这个,而最终跳过NA。我已经尝试添加这个循环的方法,但它没有给予我想要的输出。

`j=1
while (j <= length(end)){
k <- j
repeat{
  end[k]=end[k+1]
  k=k+1
  if(is.na(end[k+1]) | k==length(end)){
    
    break
  }
}
 j=j+1
}`

有没有人知道如何循环通过这些数字,而暂时停止循环在一个NA和转换的最后一个数字之前的NA所有以前的数字。

vom3gejh

vom3gejh1#

溶液
将NA之间的值替换为每个NA之前的值。这是最快的解决方案(查看基准测试)。

na_pos <- which(is.na(end))
end[-na_pos] <- rep(end[na_pos-1], diff(c(0,na_pos))-1)
end
#> [1]  6  6  6 NA  8  8  8 NA 23 23 NA

如果end没有以NA结尾,解决方案将无法工作。因此为了安全起见,您可以这样修改它:

na_pos <- union(which(is.na(end)), length(end)+1)
end[-na_pos] <- rep(end[na_pos-1], diff(c(0,na_pos))-1)
end
#> [1]  6  6  6 NA  8  8  8 NA 23 23 NA

虽然速度稍微慢了一点,但它仍然是最快的解决方案。

FOR循环解决方案

为了帮助你,我在这里留下一个循环的解决方案,你从结尾到开头,当你得到,你用以前的非NA值替换任何NA值。

for(i in seq(length(end), 2)){
  
  if(!is.na(end[i]) & !is.na(end[i-1])) end[i-1] <- end[i]
  
}
end
#> [1]  6  6  6 NA  8  8  8 NA 23 23 NA

基准

在这个例子中,napos方法是目前为止最快的方法。我也冒昧地与其他解决方案进行了比较。

library(dplyr)

microbenchmark::microbenchmark(
  
  napos = {
    end <- c(1,3,6,NA,6,7,8,NA,12,23,NA)
    na_pos <- union(which(is.na(end)), length(end)+1)
    end[-na_pos] <- rep(end[na_pos-1], diff(c(0,na_pos))-1)},
  
  forloop = {
    end <- c(1,3,6,NA,6,7,8,NA,12,23,NA)
    for(i in seq(length(end), 2)){

      if(!is.na(end[i]) & !is.na(end[i-1])) end[i-1] <- end[i]

    }
  },
  cumsum = {
    end <- c(1,3,6,NA,6,7,8,NA,12,23,NA)
    grp <- cumsum(is.na(end))
    end <- ave(end, grp, FUN=function(x) ifelse(!is.na(x), tail(x, 1), NA))
  },
  
  rle = {
    
    end <- c(1,3,6,NA,6,7,8,NA,12,23,NA)
    end <- ave(end, 
        with(rle(is.na(end)), rep(seq_along(values), lengths)),
        FUN = function(x) tail(x, 1))
  },
  
  dplyr = {
  
    end <- tibble(end = c(1,3,6,NA,6,7,8,NA,12,23,NA)) |> 
      mutate(
        section = if_else(is.na(end), NA_integer_, cumsum(is.na(end)))
      ) |> 
      group_by(section) |> 
      mutate(
        end = tail(end, 1) # or alternatively last element
      ) |> 
      pull(end)
})
#> Unit: microseconds
#>     expr     min       lq      mean   median       uq     max neval
#>    napos    23.4    41.65    63.071    64.25    77.70   133.0   100
#>  forloop  5430.0  6154.35  7307.699  6729.85  7359.55 15330.1   100
#>   cumsum    98.5   143.40   227.069   195.55   254.00  2614.1   100
#>      rle   134.3   192.05   270.753   259.20   291.65  1964.7   100
#>    dplyr 13386.2 14998.55 16530.285 15647.60 16779.35 40551.9   100

创建于2023-03-21带有reprex v2.0.2

q43xntqr

q43xntqr2#

使用基数R,我们可以使用cumsumis.na来定义一个“组”或一组值。然后我们可以使用ave()来转换这些组中的值,只保留最后一个值并保留NA值。

grp <- cumsum(is.na(end))
ave(end, grp, FUN=function(x) ifelse(!is.na(x), tail(x, 1), NA))
#  [1]  6  6  6 NA  8  8  8 NA 23 23 NA

这将返回所需的结果,而无需显式循环。

83qze16e

83qze16e3#

@MrFlick的答案可能是:

ave(end, 
    with(rle(is.na(end)), rep(seq_along(values), lengths)),
    FUN = function(x) tail(x, 1))

 [1]  6  6  6 NA  8  8  8 NA 23 23 NA
des4xlb0

des4xlb04#

使用dplyr的一个想法。首先创建组,然后使用它们来提取所需的数字。

library(tidyverse)

data <-
  tibble(end = c(1,3,6,NA,6,7,8,NA,12,23,NA)) |> 
  mutate(
    section = if_else(is.na(end), NA, cumsum(is.na(end)))
  )

data2 <- 
  data |> 
  group_by(section) |> 
  mutate(
    end2 = max(end), # use maximum
    end3 = tail(end, 1) # or alternatively last element
  )

相关问题