【问题标题】:Picking rows with specific column matching conditions选择具有特定列匹配条件的行
【发布时间】:2019-01-07 16:27:15
【问题描述】:

我有一个data.frame,有 2291 行和 4 列,我想选择那些第 3 列与下一行的第 2 列匹配的行,然后从下一个匹配的行重新开始,直到匹配继续直到它停止。

我尝试使用来自1:nrow(df) 的for 循环,但这并不完全准确,因为i(我认为)并没有真正从匹配行的点开始。

我现在的代码是这样的:

test <- NULL 
x <- c()
y <- c()

for(i in 1:nrow(df)){
    if(df[i,3]==df[i+1,2]){
        x <- df[i,]
        y <- df[i+1,]
        i = i+1 #stuck at this
    }
    test <- rbind(test,x,y)
}

示例数据如下所示:

X  2670000  3750000    C
X  3830000  8680000   E3
X  8680000 10120000 E1-A
X 10120000 11130079    D
X 11170079 11810079   E3
X 11810079 12810079 E2-A
X 12810079 13530079   E3
X 13530079 14050079   E3
X 14050079 15330079    A
X 15330079 16810079 E2-A
X 16810079 17690079 E2-A

我想要的是:

X  3830000  8680000   E3
X  8680000 10120000 E1-A
X 10120000 11130079    D

X 11170079 11810079   E3
X 11810079 12810079 E2-A
X 12810079 13530079   E3
X 13530079 14050079   E3
X 14050079 15330079    A
X 15330079 16810079 E2-A
X 16810079 17690079 E2-A

我实际上对第 4 列的值感兴趣。在这种情况下,当 df[i,3] 不等于 df[i+1,2],是否可以更新代码以将第 4 列值存储在向量中?

例如:这个样本的结果是:

vector_1
"E3" "E1-A" "D"

vector_2
"E3" "E2-A" "E3" "E3" "A" "E2-A" "E2-A" 

到目前为止我得到的是:

X  3830000  8680000   E3
X  8680000 10120000 E1-A
X  8680000 10120000 E1-A
X 10120000 11130079    D
X  8680000 10120000 E1-A
X 10120000 11130079    D
X 11170079 11810079   E3
X 11810079 12810079 E2-A
X 11810079 12810079 E2-A
X 12810079 13530079   E3

如果我从 df 的第 1 行转到最后一行,只要i 的第 3 列与i+1 的第 2 列匹配,我就想继续在向量中添加第 4 列的值。一旦该条件中断,下次满足相同条件时,我想再次继续存储第 4 列的值。

谢谢!

【问题讨论】:

  • 您希望始终包含最后一行?如果i == nrow(df) 则有df[i+1, 3]...

标签: r dataframe


【解决方案1】:

你可以这样做:

# indices of the changes
changed <- c(head(df[[3]],-1) != df[-1,2])
# split by cumsum of those
split_ <- split(df[-1,],cumsum(changed))

split_ 

# $`1`
# V1       V2       V3   V4
# 2  X  3830000  8680000   E3
# 3  X  8680000 10120000 E1-A
# 4  X 10120000 11130079    D
# 
# $`2`
# V1       V2       V3   V4
# 5   X 11170079 11810079   E3
# 6   X 11810079 12810079 E2-A
# 7   X 12810079 13530079   E3
# 8   X 13530079 14050079   E3
# 9   X 14050079 15330079    A
# 10  X 15330079 16810079 E2-A
# 11  X 16810079 17690079 E2-A

# Then to get your vectors :

lapply(split_,`[[`,4)

# $`1`
# [1] "E3"   "E1-A" "D"   
# 
# $`2`
# [1] "E3"   "E2-A" "E3"   "E3"   "A"    "E2-A" "E2-A"

数据

df <- read.table(text = "
X  2670000  3750000    C   
X  3830000  8680000   E3
X  8680000 10120000 E1-A
X 10120000 11130079    D        
X 11170079 11810079   E3
X 11810079 12810079 E2-A
X 12810079 13530079   E3
X 13530079 14050079   E3
X 14050079 15330079    A           
X 15330079 16810079 E2-A                     
X 16810079 17690079 E2-A
",stringsAsFactors=F)

【讨论】:

  • 简直太美了
【解决方案2】:

一个简单的方法是使用 dplyr 包中的 lead 函数。

lead(x, n = 1L, default = NA, order_by = NULL, ...) 在向量中查找“下一个”或“上一个”值。用于比较当前值之前或之后的值。

这也使您可以完全避免 for 循环。由于您没有在问题中命名您的列,我将使用另一个示例:

library(dplyr)
df <- data.frame(a = 1:5, b = c(2, 999, 4, 5, 999))

print(df) # In this example, we want to keep the 1st, 3rd, and 4th rows.
     a   b
   1 1   2
   2 2 999
   3 3   4
   4 4   5
   5 5 999

matching_df <- df[df$b == dplyr::lead(df$a, 1, default = FALSE), ]
print(matching_df)
      a b
    1 1 2
    3 3 4
    4 4 5

non_matching_df <- df[df$b != dplyr::lead(df$a, 1, default = FALSE), ]
print(non_matching_df)
      a   b
    2 2 999
    5 5 999

【讨论】:

  • 谢谢!我还感兴趣的是在列匹配后获取下一行。可以更改代码以获得该代码吗?所以在你的例子中,我应该得到第 1 行和第 2 行(因为 2 与 1 匹配),然后是第 3,4 和 5 行。
  • 如果我从 df 的第 1 行到最后一行,只要i 的第 3 列与i+1 的第 2 列匹配,我就想继续在向量中添加第 4 列的值。一旦该条件中断,下次满足相同条件时,我想再次继续存储第 4 列的值。
【解决方案3】:

使用重复而不是 for 循环。这种方式要容易得多。首先从第二行开始创建第二列的重复列并进行比较。我使用了你的样本数据并得到了想要的结果 -

data = read.csv('clipboard', sep = "", header = F, stringsAsFactors = F)

data$v22 = c(data$V2[2:nrow(data)],0)  
data
    V1       V2       V3   V4      v22
 1   X  2670000  3750000    C  3830000
 2   X  3830000  8680000   E3  8680000
 3   X  8680000 10120000 E1-A 10120000
 4   X 10120000 11130079    D 11170079
 5   X 11170079 11810079   E3 11810079
 6   X 11810079 12810079 E2-A 12810079
 7   X 12810079 13530079   E3 13530079
 8   X 13530079 14050079   E3 14050079
 9   X 14050079 15330079    A 15330079
 10  X 15330079 16810079 E2-A 16810079
 11  X 16810079 17690079 E2-A        0

找到第一个匹配项

 i = 0
 repeat {
   i = i+1
   if (data$V3[i] == data$v22[i]){
     cat(paste('First Match at row',i))
     break
   }    
 }

现在保存额外的匹配行并继续查看匹配中断并保存额外的匹配值

 df = data.frame(NULL)
 xstart = c(i)
 xend = NULL
 repeat{
   if (data$V3[i] == data$v22[i]){
     df = rbind(df,data[i,])
     i = i+1
   } else { xend = c(xend,i)
     if (i < nrow(data)){
     repeat {
       i = i +1
       if (data$V3[i] == data$v22[i]){
       cat(paste('Next Match at row',i))
       xstart = c(xstart,i)
       break
          }
        }
      }
     }     

   if (i == nrow(data)){
     break
        }
  }

打印结果

if (length(xend) < length(xstart)) {xend = c(xend,nrow(data))}

for (l in 1:length(xend)){
  print(data$V4[xstart[l]:xend[l]])
}

 [1] "E3"   "E1-A" "D"   
 [1] "E3"   "E2-A" "E3"   "E3"   "A"    "E2-A" "E2-A"

【讨论】:

    【解决方案4】:

    更新:我意识到我错过了一些我更新代码的条件

    如果您只想保留第 4 列的结果,可以试试这个:

    df <- read.table(text = "
    X  2670000  3750000    C   
    X  3830000  8680000   E3
    X  8680000 10120000 E1-A
    X 10120000 11130079    D        
    X 11170079 11810079   E3
    X 11810079 12810079 E2-A
    X 12810079 13530079   E3
    X 13530079 14050079   E3
    X 14050079 15330079    A           
    X 15330079 16810079 E2-A                     
    X 16810079 17690079 E2-A
    ",stringsAsFactors=F)
    
    eq=c(-1,df$V3[1:(nrow(df)-1)]-df$V2[2:nrow(df)])
    
    storeV3 <- list()
    vector_id = 0
    
    for(i in 1:length(eq)){
        if(eq[i] ==  0)
            storeV3[[paste0("vector_",vector_id)]]=c(storeV3[[paste0("vector_",vector_id)]],df$V4[i])
        if(eq[i] !=  0){
            if(i> 1 && eq[i-1] ==  0){
                vector_id = vector_id + 1
                storeV3[[paste0("vector_",vector_id)]]=c(storeV3[[paste0("vector_",vector_id)]],df$V4[i])
            }
            else if(i < length(eq)  && eq[i+1] ==  0)
                storeV3[[paste0("vector_",vector_id)]]=c(storeV3[[paste0("vector_",vector_id)]],df$V4[i])
        }
    }
    

    我使用的技巧是首先创建一个向量来了解匹配项的索引,然后将V3 的值存储在动态更新索引的向量列表中。这给了我:

    > storeV3
    $vector_0
    [1] "E3"   "E1-A" "D"
    
    $vector_1
    [1] "E3"   "E2-A" "E3"   "E3"   "A"    "E2-A" "E2-A"
    

    【讨论】:

      【解决方案5】:

      这是一个执行任务的函数。

      结果

      # Results
      generate(df)
      # [[1]]
      #   X1       X2       X3   X4
      # 2  X  3830000  8680000   E3
      # 3  X  8680000 10120000 E1-A
      # 4  X 10120000 11130079    D
      # 
      # [[2]]
      #    X1       X2       X3   X4
      # 5   X 11170079 11810079   E3
      # 6   X 11810079 12810079 E2-A
      # 7   X 12810079 13530079   E3
      # 8   X 13530079 14050079   E3
      # 9   X 14050079 15330079    A
      # 10  X 15330079 16810079 E2-A
      # 11  X 16810079 17690079 E2-A 
      

      要仅包含有问题的向量,您可以修改函数 generate 或简单地修改

      out <- generate(df)    
      vec <- lapply(seq_along(out), function (k) out[[k]]$X4)    
      vec
      # [[1]]
      # [1] "E3"   "E1-A" "D"   
      # 
      # [[2]]
      # [1] "E3"   "E2-A" "E3"   "E3"   "A"    "E2-A" "E2-A"  
      

      功能

      finder <- function (k, dt) {
        dt <- dt[k:nrow(dt),]
        index <- NA_integer_
        col2 <- as.integer(.subset2(dt,2L))
        col3_first <- as.integer(.subset2(dt,3L))[1]
        index <- which(col2 == col3_first)[1L] +k-1
        index
      }
      getIndex <- function (k, dt) {
        curr <- finder(k, dt)
        if (curr == nrow(dt) || is.na(curr)) return (curr)
        out <- c(k, curr)
        s <- k
        while (!is.na(curr)) {
          s <- s+1
          curr <- finder(s, dt)
          out <- c(out, curr)
        }
        out <- out[!is.na(out)]
        out
      }
      generate <- function (dt) {
        rows <- 1:nrow(dt)
        skip <- integer(0)
        counter <- 0L
        res <- list()
        for (k in rows) {
          if (k %in% skip) next
          index <- getIndex(k, dt)
          if (is.na(index) || length(index) == 1) next
          skip <- c(skip, index)
          counter <- counter + 1L
          res[[counter]] <- df[index,]
        }
        res
      }
      

      说明

      对于给定的行号k

      • finder 将产生第一个 n (> k) 使得 df[k,3] == df[i,2]
      • getIndex 将产生最长的递增序列(k=)k_0;k_1;...;k_n 使得
        df[k_i,3] == df[k_i+1,2] 为所有i = 1;...;n

      最后,generate 为所有行从getIndex 生成序列,但是如果给定行k 包含在前一个序列中,则它会跳过该行。

      数据

      structure(list(X1 = c("X", "X", "X", "X", "X", "X", "X", "X", 
      "X", "X", "X"), X2 = c("2670000", "3830000", "8680000", "10120000", 
      "11170079", "11810079", "12810079", "13530079", "14050079", "15330079", 
      "16810079"), X3 = c("3750000", "8680000", "10120000", "11130079", 
      "11810079", "12810079", "13530079", "14050079", "15330079", "16810079", 
      "17690079"), X4 = c("C", "E3", "E1-A", "D", "E3", "E2-A", "E3", 
      "E3", "A", "E2-A", "E2-A")), class = "data.frame", row.names = c(NA, 
      -11L))
      

      【讨论】:

        【解决方案6】:

        我做了一个这样的零碎代码,似乎可以工作:

        for(i in 1:nrow(df)){
            if(df[i,3]==df[i+1,2]){
                next
                x <- df[i,]
                y <- df[i+1,]
                i = i+1
            }
            test <- rbind(test,x,y)
        }
        
        test <- test[!duplicated(test),]
        
        v <- list()
        j <- 0
        for(i in 1:nrow(test)){
            if(test[i,3]!=test[i+1,2]){
                v[[i]] <- test[(j+1):i,4]
                j <- i
                next
            }
        }
        
        v <- v[!is.na(v)]
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-12-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多