【问题标题】:filter rows of dataframe based on an ordered vector of characters根据字符的有序向量过滤数据帧的行
【发布时间】:2017-08-17 13:10:19
【问题描述】:

不确定我的问题是否重复,但在 stackoverflow 中搜索并没有产生任何可能的解决方案。

我有以下数据框

num   char  
1     A  
2     K  
3     I  
4     B  
5     I  
6     N  
7     G  
8     O  
9     Z  
10    Q 

我只想在 char 列中选择那些形成单词 BINGO(按该顺序)的行,从而生成以下数据框:

num char  
4     B  
5     I  
6     N  
7     G  
8     O 

任何帮助将不胜感激。

【问题讨论】:

  • 这些总是单个字符吗?似乎将它们粘贴到字符串中并执行正则表达式或简单的字符串匹配以找到正确的索引可能更容易。
  • 嗨 MrFlick,是的,这些总是单个字符。但是,我不明白如何实施您建议的解决方案...
  • 您可能还会发现类似的post 很有帮助。

标签: r string vector dataframe filter


【解决方案1】:

一种选择是使用zoo::rollapply

library(zoo)
bingo = c("B", "I", "N", "G", "O")    # the pattern you want to check

# use rollapply to check if the pattern exists in any window
index = which(rollapply(df$char, length(bingo), function(x) all(x == bingo)))

# extract the window from the table
df[mapply(`:`, index, index + length(bingo) - 1),]

#  num char
#4   4    B
#5   5    I
#6   6    N
#7   7    G
#8   8    O

【讨论】:

  • 谢谢。这正是我所需要的!
【解决方案2】:

这是一个使用递归函数的解决方案 - BINGO 的字母不需要连续,但它们确实需要按顺序排列。

df <- data.frame(num=1:10,char=c("A","K","I","B","I","N","G","O","Z","Q"),stringsAsFactors = FALSE)

word<-"BINGO"

chars<-strsplit(word,"")[[1]]

findword <- function(chars,df,a=integer(0),m=0){ #a holds the result so far on recursion, m is the position to start searching
  z <- m+match(chars[1],df$char[(m+1):nrow(df)]) #next match of next letter
  if(!is.na(z)){      
    if(length(chars)==1){
      a <- c(z,a)
    } else {
      a <- c(z,Recall(chars[-1],df,a,max(m,z))) #Recall is function referring to itself recursively
    }
    return(a) #returns row index numbers of df
  } else {
    return(NA)
  }
}

result <- df[findword(chars,df),]

【讨论】:

  • 这比我的解决方案 +1 快 50% 且更优雅
  • 谢谢。可能值得在函数中检查is.na(z),以避免在找不到该单词时发生严重的崩溃。在上面编辑。
【解决方案3】:

我第一次跑得太快了,但根据你给出的例子,我认为这可以工作:

filter(df[which(df$char == "B"):dim(df)[1],], char %in% c("B","I","N","G","O"))

【讨论】:

  • 我收到了您的解决方案的此错误消息:Error in filter(df[which(df$char == "B"):dim(df)[1], ], char %in% c("B", : 'filter' is longer than time series
【解决方案4】:

我猜没有人喜欢循环,但这在 base 中是有可能的:

char <- c("A", "K", "I", "B", "I", "N", "G", "O", "Z", "Q")
num <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
df <- data.frame(num, char)

word <- "BINGO"
index <- NULL
for(z in 1:nrow(df)){
  if(substr(word, 1,1) == as.character(df[z,2])){
    index <- c(index, z) 
    word <- substr(word, 2, nchar(word))    
  }
}

df[index,]

【讨论】:

    【解决方案5】:
    d = data.frame(num=1:15, char=c('A', 'K', 'I', 'B', 'I', 'N', 'G', 'O', 'Z', 'Q', 'B', 'I', 'N', 'G', 'O'))
    w = "BINGO"
    N = nchar(w)
    char_str = paste(d$char, sep='', collapse='')
    
    idx = as.integer(gregexpr(w, char_str)[[1]]) 
    idx = as.integer(sapply(idx, function(i)seq(i, length=N)))
    d[idx, ]
    
       num char
    4    4    B
    5    5    I
    6    6    N
    7    7    G
    8    8    O
    11  11    B
    12  12    I
    13  13    N
    14  14    G
    15  15    O
    

    【讨论】:

    • 我不明白你的例子。如何使用gregexpr() 函数选择行?如果你能给出一个小小的解释就好了。
    • 看起来不错。但是当没有顺序时它不起作用。试试这个d = data.frame(num=1:15, char=c('A', 'K', 'I', 'B', 'N', 'I', 'G', 'O', 'Z', 'Q', 'X', 'I', 'N', 'G', 'O'))
    • OP 需要有序匹配。
    • 用我之前发布的命令的数据框试试你的脚本。我的意思是,当 BINGO 一词尚未在数据框中排序时,您的解决方案将不起作用。
    • 我知道,这就是重点。否则很容易:subset(d, char %in% c('B', 'I', ...))
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-14
    • 2023-01-03
    • 2022-08-17
    相关资源
    最近更新 更多