延长一定长度的运行

问题描述 投票:1回答:2

我有一个640 x 2500的数据框,里面有数值和几个 NA 值。我的目标是找到至少75个连续的 NA 的值。对于每一个这样的运行,我想替换之前的 以下50个细胞 NA 值。

下面是一个缩小的例子,是一行。

x <- c(1, 3, 4, 5, 4, 3, NA, NA, NA, NA, 6, 9, 3, 2, 4, 3)
#        run of four NA:  ^   ^   ^   ^     

我想检测连续四次的运行 NA,然后将运行前的三个值和运行后的三个值替换为 NA:

c(1, 3, 4, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 2, 4, 3) 
#           ^   ^   ^                   ^   ^   ^

我曾试图首先确定连续 NArle但是运行 rle(is.na(df)) 给出错误 'x' must be a vector of an atomic type. 即使在我选择单行时也会出现这种情况。

遗憾的是,我不知道下一步该如何将之前和之后的50个单元格转换为NA。

如果能得到任何帮助,我将不胜感激,提前表示感谢。

r type-conversion na run-length-encoding eye-tracking
2个回答
2
投票

因为你评论说,在你的数据中"有的[行]以数行开头,有的[行]以数行结尾。NAs",希望这能更好地代表真实数据。

  A  B  C  D  E  F  G  H  I  J
1 1  2  3 NA NA  6  7  8 NA 10
2 1 NA NA NA  5  6  7 NA NA NA
3 1  2  3  4 NA NA NA  8  9 10

让我们假设最小运行长度为: NA 拟扩大为 NA 是2,运行前的两个值和运行后的两个值应替换为 NA. 在这个例子中,第2行将代表你在评论中提到的情况。

首先是一些数据的处理。我更喜欢用 data.table 格式。随着 data.table 我们可以获得有用的常数 .I.N,并且可以轻松地用 rleid.

# convert data.frame to data.table
library(data.table)
setDT(d)

# set minimum length of runs to be expanded
len = 2L

# set number of values to replace on each side of run
n = 2L

# number of columns of original data (for truncation of indices)
nc = ncol(d)

# create a row index to keep track of the original rows in the long format
d[ , ri := 1:.N]

# melt from wide to long format
d2 = melt(d, id.vars = "ri")

# order by row index
setorder(d2, ri)

现在,实际计算的运行和他们的指数。

d2[
  # check if the run is an "NA run" and has sufficient length 
  d2[ , if(anyNA(value) & .N >= len){

    # get indices before and after run, where values should be changed to NA  
    ix = c(.I[1] - n:1L, .I[.N] + 1L:n)

    # truncate indices to keep them within (original) rows 
    ix[ix >= 1 + (ri - 1) * nc & ix <= nc * ri]},

    # perform the calculation by row index and run index
    # grab replacement indices
    by = .(ri, rleid(is.na(value)))]$V1,

  # at replacement indices, set value to NA 
  value := NA]

如果需要的话,可以投回宽幅

dcast(d2, ri ~ variable, value.vars = "value")
#    ri  A  B  C  D  E  F  G  H  I  J
# 1:  1  1 NA NA NA NA NA NA  8 NA 10
# 2:  2 NA NA NA NA NA NA NA NA NA NA
# 3:  3  1  2 NA NA NA NA NA NA NA 10

1
投票

类型胁迫对我有效。

rle(as.logical(is.na(x[MyRow, ])))

1
投票

这是我的解决方案。不过不知道有没有比我的更简洁的解决方案。

library(data.table)
df <- matrix(nrow = 1,ncol = 16)
df[1,] <- c(1, 3, 4, 5, 4, 3, NA, NA, NA, NA, 6, 9, 3, 2, 4, 3)
df <- df %>%
  as.data.table() # dataset created

# A function to do what you need
NA_replacer <- function(x){
  Vector <- unlist(x) # pull the values into a vector

  NAs <- which(is.na(Vector)) # locate the positions of the NAs
  NAs_Position_1 <- cumsum(c(1, diff(NAs) - 1)) # Find those that are in sequential order
  NAs_Position_2 <- rle(NAs_Position_1) # Find their values

  NAs <- NAs[which(
    NAs_Position_1 == with(NAs_Position_2,
                           values[which(
                             lengths == 4)]))] # Locate the position of those NAs that are repeated exactly 4 times

  if(length(NAs == 4)){ # Check if there are a stretch of 4 WAs
    Vector[seq(NAs[1]-3,
               NAs[1]-1,1)] <- NA # this part deals with the 3 positions occuring before the first NA
    Vector[seq(NAs[length(NAs)]+1,
               NAs[length(NAs)]+3,1)] <- NA # this part deals with the 3 positions occuring after the last NA
  }
  Vector
}
> df # the original dataset
V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16
1:  1  3  4  5  4  3 NA NA NA  NA   6   9   3   2   4   3
# the transformed dataset
apply(df, 1, function(x) NA_replacer(x)) %>%
  as.data.table() %>%
  data.table::transpose()

V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16
1:  1  3  4 NA NA NA NA NA NA  NA  NA  NA  NA   2   4   3

说句题外话,对于一个大小为640*2500的假设数据框来说,速度是相当不错的,其中有一段75个或更多的NA必须被定位,而且前后50个值必须用一个NA来代替。

df <- matrix(nrow = 640,ncol = 2500)

for(i in 1:nrow(df)){
  df[i,] <- c(1:100,rep(NA,75),rep(1,2325))
}

NA_replacer <- function(x){
  Vector <- unlist(x) # pull the values into a vector

  NAs <- which(is.na(Vector)) # locate the positions of the NAs
  NAs_Position_1 <- cumsum(c(1, diff(NAs) - 1)) # Find those that are in sequential order
  NAs_Position_2 <- rle(NAs_Position_1) # Find their values

  NAs <- NAs[which(
    NAs_Position_1 == with(NAs_Position_2,
                           values[which(
                             lengths >= 75)]))] # Locate the position of those NAs that are repeated exactly 75 times or more than 75 times

  if(length(NAs >= 75)){ # Check if the condition is met
    Vector[seq(NAs[1]-50,
               NAs[1]-1,1)] <- NA # this part deals with the 50 positions occuring before the first NA
    Vector[seq(NAs[length(NAs)]+1,
               NAs[length(NAs)]+50,1)] <- NA # this part deals with the 50 positions occuring after the last NA
  }
  Vector
}
# Check how many NAs are present in the first row of the dataset prior to applying the function
which(is.na(df %>%
              as_tibble() %>%
              slice(1) %>%
              unlist())) %>% # run the code till here to get the indices of the NAs
  length() 

[1] 75
df <- apply(df, 1, function(x) NA_replacer(x)) %>%
  as.data.table() %>%
  data.table::transpose()

# Check how many NAs are present in the first row post applying the function
which(is.na(df %>%
              slice(1) %>%
              unlist())) %>% # run the code till here to get the indices of the NAs
  length()

[1] 175
system.time(df <- apply(df, 1, function(x) NA_replacer(x)) %>%
              as.data.table() %>%
              data.table::transpose())
user  system elapsed 
  0.216   0.002   0.220
© www.soinside.com 2019 - 2024. All rights reserved.