【问题标题】:Removing many entries from a vector of strings by name按名称从字符串向量中删除许多条目
【发布时间】:2018-11-18 14:09:59
【问题描述】:

我想删除具有 500 个列名的向量中的大约 100 个条目,然后使用该向量将(预测)矩阵 m 的行归零。

作为我的数据框的一个非常简单的例子:

A 1 2 3
B 1 2 3
C 1 2 3
D 1 2 3
E 1 2 3
F 1 2 3
G 1 2 3
H 1 2 3
I 1 2 3
J 1 2 3

首先我将列名放入一个向量中:

x <- colnames(df) # x <- c("A","B","C","D","E","F","G,"H","I","J")

假设我想删除 B 直到 D、F 和 G 直到 I(实际上是大约 100 个变量散布在向量上,我不知道它们的索引)。我想做类似的事情:

*remove <- c(B:D, F, G:I)* # This does now work obviously
x [! x %in% remove]

这会给我留下一个向量x,如下所示:

A
E
J

这个向量表示需要设置为零的行名(和列名,因为它是一个预测矩阵):

m[x,] <- 0

创建以下输出:

  A B C D E F G H
A 1 0 1 0 1 0 1 0
B 0 0 0 0 0 0 0 0
C 0 0 0 0 0 0 0 0
D 0 0 0 0 0 0 0 0
E 1 0 1 0 1 0 1 0
F 1 0 1 0 1 0 1 0
G 0 0 0 0 0 0 0 0
H 0 0 0 0 0 0 0 0
I 0 0 0 0 0 0 0 0
J 1 0 1 0 1 0 1 0

如何从所有变量名的向量中删除这 100 个变量名,并使用该向量来引用矩阵的列名?

【问题讨论】:

  • 使用位置
  • 我能以某种方式确立他们的立场吗?
  • 让我自动思考
  • 您提供了一个示例输入。您能否编辑您的帖子以包含预期输出的示例?
  • 当您第一次表示行时,您现在说的是列。这令人困惑。你能用实际矩阵(或代表性样本)的dput() 输出做一个可重现的例子吗?

标签: r string vector


【解决方案1】:

有趣的用例。我们可以设计一个函数,帮助您以您希望的通用方式执行此操作。


注意:

我使用了低于 b/c 的数据框,我认为最初没有(或者我只是错过了)矩阵提及,现在各种问题编辑会混淆列名和行名。 所以你应该从下面关注的位是:

# get the terms of the formula
trms <- terms(remove_spec)

# get each element (will be each group separated by `+`
elements <- attr(trms, "term.labels")

# adding in assertions to validate `col` is in `xdf` and that only
# the restricted syntax is used in the formula and that it's valid 
# is up to the OP

# now, find the positions of all those strings
unlist(lapply(elements, function(y) {
  if (grepl(":", y)) {
    rng <- strsplit(y, ":")[[1]]
    which(x[,col] == rng[1]) : which(x[,col] == rng[2])
  } else {
    which(x[,col] == y)
  }
}), use.names = FALSE) -> to_exclude

因为我现在已经完成了这个 q(并且行名是如此 1980 年代:-)。请注意答案末尾的警告。

其他人可以随意在 OP 用例的实际矩阵答案中使用它。


我们将制作一些模拟数据(如果您想要更大的示例,我可以将示例放大):

library(dplyr) # mostly for saner data frame constructor & printing

set.seed(2018-11-18)

data_frame(
  cat = LETTERS,
  val1 = sample(100, length(cat), replace = TRUE),
  val2 = sample(100, length(cat), replace = TRUE),
  val3 = sample(100, length(cat), replace = TRUE)
) -> xdf

xdf
## # A tibble: 26 x 4
##    cat    val1  val2  val3
##    <chr> <int> <int> <int>
##  1 A        87    98     5
##  2 B        30    69    39
##  3 C        87     1    32
##  4 D        65    46    87
##  5 E         4    69     6
##  6 F        53    20    31
##  7 G        43    51    84
##  8 H        27    43    65
##  9 I        27     9    10
## 10 J        10    94    11
## # ... with 16 more rows

tibble 打印是 def >> 基本打印 IMO,但我离题了)。

现在,您想使用字符串来指定单个元素和范围,并确定在幕后做什么。我们需要一个函数来实现,我们可以利用一个特殊的 R 类——forumla——来帮助实现更紧凑的语法。也就是说,能够调用这样的函数不是很好吗:

remove_rows(xdf, cat, ~B:C+F+G:I)

这将在xdfcat列中寻找“B”的范围:“C”,找到“F”的位置,然后找到“G”的范围":"I" 并返回一个排除了那些的数据框?是的,是的。所以,让我们构建它吧!

#' @param x data frame
#' @param col bare column name to use for the comparison
#' @param formula restricted operators are `:` for range and `+` for additing selectors
remove_rows <- function(x, col, remove_spec) {

  # this is pure convenience we could just as easily have forced folks 
  # to pass in a string (and we can modify it to handle both)
  col <- as.character(substitute(col)) 

  # get the terms of the formula
  trms <- terms(remove_spec)

  # get each element (will be each group separated by `+`
  elements <- attr(trms, "term.labels")

  # adding in assertions to validate `col` is in `xdf` and that only
  # the restricted syntax is used in the formula and that it's valid 
  # is up to the OP

  # now, find the positions of all those strings
  unlist(lapply(elements, function(y) {
    if (grepl(":", y)) {
      rng <- strsplit(y, ":")[[1]]
      which(x[,col] == rng[1]) : which(x[,col] == rng[2])
    } else {
      which(x[,col] == y)
    }
  }), use.names = FALSE) -> to_exclude

  # and get rid of those puppies
  x[-to_exclude,]

}

现在我们可以实事求是了:

remove_rows(xdf, cat, ~B:C+F+G:I)
## # A tibble: 20 x 4
##    cat    val1  val2  val3
##    <chr> <int> <int> <int>
##  1 A        87    98     5
##  2 D        65    46    87
##  3 E         4    69     6
##  4 J        10    94    11
##  5 K        37    86    52
##  6 L        89    64    44
##  7 M        61    10    28
##  8 N        79    52    89
##  9 O        71    33    77
## 10 P        45    33    77
## 11 Q        56    97    29
## 12 R        10    28    39
## 13 S        25     7    71
## 14 T        86    57    51
## 15 U        92     2    15
## 16 V        25    36    12
## 17 W        90    78    10
## 18 X        20    82    90
## 19 Y        39    84    13
## 20 Z        43    93    18

函数的命名很糟糕,因此您可能想要更改它,并且您确实应该添加一些参数检查和验证,但我相信这可以满足您的需求(假设您真的确定数据帧的顺序是您认为的顺序)。

此外,这是不完美的,因为字符串被限制为公式(其中一个限制是它们不能以没有反引号的数字开头)。但是,您没有提供真实字符串的样本。

【讨论】:

    【解决方案2】:

    我使用 hrbrmstr 的答案和长期的解决方法让它工作。如果有人能告诉我如何减少混乱,请告诉我。

    # Copy prediction matrix and turn it into a dataframe for the "remove rows" function
    varlist <- m
    varlist <- as.data.frame(varlist)
    
    # Create a column called "cat" with the rownames for the "remove rows" function
    varlist$cat = rownames(varlist)
    # Use the function to remove the rows from the copied df
    varlist <- remove_rows(varlist, cat, ~B:C+F+G:I)
    # Only keep the "cat" column and turn it into a vector
    varlist <- varlist$cat
    varlist <- varlist[['cat']]
    # Copy prediction matrix and use "varlist" to put the correct rows to zero.
    m_reduced <- m
    m_reduced[ ,varlist] <- 0
    

    如果有人能告诉我如何清理这个怪物,我会非常高兴。

    【讨论】:

      【解决方案3】:

      这是我的方式:

      remove<-function(lets_to_be_removed,names){
          letters_with_names<-1:length(LETTERS) # each value corresponds to a letter
          names(letters_with_names)<-LETTERS # the letters, for example: letters_with_name["A"]==1 is TRUE
          result<-integer()
          for(letters in lets_to_be_removed){
              #check if it is only one letter
              res <- if(length(letters) == 1) letters_with_names[letters] else letters_with_names[letters[1]]:letters_with_names[letters[2]] 
              result<- c(result,res)
          }
          names(result)<-LETTERS[result]
          result #return the indices of the letters
      }
      

      你可以这样调用它:

      letters <- list(c("B","D"),"F",c("G","I"))
      letters
      [[1]]
      [1] "B" "D" # B:D sequence
      [[2]]
      [1] "F" # only one letter
      [[3]]
      [1] "G" "I" # G:I sequence
      
      indices<-remove(letters,x)
      indices # named vector
      B C D F G H I 
      2 3 4 6 7 8 9
      
      x[ -indices ] # it is faster than [! x %in% indices] but if you want your method  then use [! x %in% names(indices)]
      [1] "A" "E" "J"
      

      一般来说,使用整数比字符索引更好更快。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-14
        • 2013-06-19
        • 1970-01-01
        • 2011-01-25
        • 1970-01-01
        相关资源
        最近更新 更多