【问题标题】:R For loop delete range of rows from one string to a second string in a columnR For循环删除列中从一个字符串到第二个字符串的行范围
【发布时间】:2016-09-25 04:50:59
【问题描述】:

我正在尝试从数据框中删除行序列,该序列以已知字符串开头,以已知字符串结尾,但中间行的内容和数量未知。我想在整个数据框上迭代它。

例如,如果数据框如下,我想从 StringA 的所有实例中删除行到 StringB(包括),但保留 StringB 后面的行直到 StringA 的下一次重复;对于下面的示例,即我想删除包含StringA,unknownC,unknownD,unknownS,StringB的行,但保留unknownK和unknownR,然后在StringA,unknownU,unknownP,StringB处继续删除,但保留unknownT。

Column 1  Column 2 
StringA     1
unknownC    9
unknownD   11
unknownS    5
StringB    7
unknownK    6
unknownR    1
StringA    76
unknownU    2
unknownP   41
StringB    3
unknownT    9

我试过df2 <- df[1:which(df[,1]=="StringA")-1,],这是不正确的,但我不知道还有什么其他方法可以尝试。提前感谢您的任何指导。

【问题讨论】:

  • 是否确定 stringA 总是与后续的 stringB 配对?是否也确定字符串 A 和 B 总是交替出现(例如,从不 A...A...B)?
  • 是的。它永远是 A...B,永远不会是 A..A...B

标签: r for-loop subset


【解决方案1】:

您可以尝试这样的事情,通过使用Map 函数构造要删除的索引:

indexToRemove <- unlist(Map(`:`, which(df$`Column 1` == "StringA"), 
                                 which(df$`Column 1` == "StringB")))

df[-indexToRemove, ]
   Column 1 Column 2
6  unknownK        6
7  unknownR        1
12 unknownT        9

数据

structure(list(`Column 1` = structure(c(1L, 3L, 4L, 8L, 2L, 5L, 
7L, 1L, 10L, 6L, 2L, 9L), .Label = c("StringA", "StringB", "unknownC", 
"unknownD", "unknownK", "unknownP", "unknownR", "unknownS", "unknownT", 
"unknownU"), class = "factor"), `Column 2` = c(1L, 9L, 11L, 5L, 
7L, 6L, 1L, 76L, 2L, 41L, 3L, 9L)), .Names = c("Column 1", "Column 2"
), class = "data.frame", row.names = c(NA, -12L))

【讨论】:

  • 嗯我喜欢这个策略 Psidom 的想法,但我不确定如何避免我在测试第一步时收到的这个警告: In mapply(FUN = f, ..., SIMPLIFY = FALSE) : 较长的参数不是较短长度的倍数
  • 看准我。太棒了
  • 不完全确定,但如果您的专栏类似于A...B...A...B...A...B..,那么这可能会发生。
  • 你是对的,它从一个 stringB 开始,删除了它,你的解决方案效果很好!谢谢你:)
【解决方案2】:

您可以使用for 循环。尽管这将比发布的矢量化解决方案慢,但它确实具有一些优点,即非常通用以适应类似的相关问题,并且对意外输入数据具有鲁棒性。

注意事项:

  1. 此方法对输入数据中的异常情况具有鲁棒性 - 它不依赖于始终交替且始终成对的 StringA...StringB 对,也不假定 StringA 始终出现在 StringB 之前。每次遇到 StringA 都会开始删除行,直到遇到 StringB。
  2. 不利的一面是,在非常大的数据帧上使用此方法可能会很慢,因为我们正在循环内增长数据帧(始终保证会减慢大型操作的速度)。

代码:

keep.line <- TRUE
out.df <- data.frame()

for (i in 1:NROW(my.df)) {
  if (my.df[i,]$Column1 == "StringA") keep.line <- FALSE
  if (keep.line) out.df <- rbind(out.df, my.df[i,])
  if (my.df[i,]$Column1 == "StringB") keep.line <- TRUE
}

out.df
##    Column1    Column2
##    unknownK  0.3679608
##    unknownR -0.8867749
##    unknownT  1.6277386

一些数据:

Column1 <-c( 
"StringA" ,    
"unknownC",    
"unknownD",   
"unknownS",   
"StringB" ,   
"unknownK",   
"unknownR",   
"StringA" ,   
"unknownU",   
"unknownP",   
"StringB" ,   
"unknownT")

my.df <- data.frame(Column1, Column2 = rnorm(12), stringsAsFactors = F)

【讨论】:

  • 非常感谢,我喜欢这种循环方式。
【解决方案3】:

使用@Psidom 的数据:

sel <- with(dat, 
  (cumsum(`Column 1`=="StringA") == cumsum(`Column 1`=="StringB"))
     &
  (!(`Column 1` %in% c("StringA","StringB")))
)
dat[sel,]

#   Column 1 Column 2
#6  unknownK        6
#7  unknownR        1
#12 unknownT        9

提供一些解释 - 这使用cumsum 来计算"StringA""StringB"Column 1 中出现的次数。如果数字匹配,则表示有 1 个A 和 1 个对应的B。如下面标记为= 的值

cumsum(dat$`Column 1`=="StringA")
#[1] 1 1 1 1 1 1 1 2 2 2 2 2
cumsum(dat$`Column 1`=="StringB")
#[1] 0 0 0 0 1 1 1 1 1 1 2 2
#            = = =       = =

删除Column 1%in% 目标StringA/B 字符串之一的情况将最终确定。

【讨论】:

  • 这看起来很有趣,但很难理解。您能否对其进行注释以解释其工作原理。
  • @dww - 添加解释/
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-23
  • 1970-01-01
  • 2020-02-14
相关资源
最近更新 更多