代码之家  ›  专栏  ›  技术社区  ›  Darren Tsai

连接字符串中的自定义因子级别

  •  3
  • Darren Tsai  · 技术社区  · 6 年前

    我有一个因子变量,它由两个子环组成,用a分隔 _ ,像 string1_string2 . 我想分别设置前缀(“string1”)和后缀(“string2”)的因子级别,然后为连接的字符串定义一组整体的因子级别。此外,第一个子串和第二个子串中级别的优先级可能有所不同。


    我想要实现的一个小例子:

    # reproducible data
    
    x <- factor(c("DBO_A", "PH_A", "COND_A", "DBO_B", "PH_B", "COND_B", "DBO_C", "PH_C", "COND_C"))
    
    [1] DBO_A  PH_A   COND_A DBO_B  PH_B   COND_B DBO_C  PH_C   COND_C
    Levels: COND_A COND_B COND_C DBO_A DBO_B DBO_C PH_A PH_B PH_C
    

    如果我不定义因子水平,它们将按字母顺序排列。现在我想在 γ 分离器,例如

    1. PH &; COND &; DBO 在左侧(左侧)。
    2. B &; A &; C 在右侧(右侧)。

    此外,我想指定哪一侧(左或右)优先于另一侧。根据哪一侧具有优先级,级别的总体顺序将有所不同:

    (1)如果LHS的水平是先决条件:

    [1] DBO_A  PH_A   COND_A DBO_B  PH_B   COND_B DBO_C  PH_C   COND_C
    Levels: PH_B PH_A PH_C COND_B COND_A COND_C DBO_B DBO_A DBO_C
    

    (2)如果RHS的等级是先决条件:

    [1] DBO_A  PH_A   COND_A DBO_B  PH_B   COND_B DBO_C  PH_C   COND_C
    Levels: PH_B COND_B DBO_B PH_A COND_A DBO_A PH_C COND_C DBO_C
    

    现在我只想像这样解决它 factor(x, levels = c(xx, xx, ...)) 但是我的水平比上面的节目高,所以这看起来很荒谬。

    注: 我不想更改数据的顺序,只更改级别的顺序。

    4 回复  |  直到 6 年前
        1
  •  2
  •   akrun    6 年前

    我们可以使用 base R 这样做。使用 sub 删除中的子字符串 levels 矢量的,与 match 通过检查自定义顺序中的值来创建数字索引,重新分配 水平 factor 通过 order 按照顺序 水平 基于 比赛 ING指数

    i1 <- match(sub("_.*", "", levels(x)), c("PH", "COND", "DBO"))
    i2 <- match(sub(".*_", "", levels(x)), c("B", "A", "C"))
    factor(x, levels = levels(x)[seq_along(levels(x))[order(i1, i2)]])
    

    对于第二种情况,只需在 秩序

    factor(x, levels = levels(x)[seq_along(levels(x))[order(i2, i1)]])
    

    对于重复使用,可以用函数包装

    f1 <- function(vec, lvls1, lvls2, flag = "former") {
       i1 <- match(sub("_.*", "", levels(vec)), lvls1)
       i2 <- match(sub(".*_", "", levels(vec)), lvls2)
    
       if(flag == 'former') {
         factor(vec, levels = levels(vec)[seq_along(levels(vec))[order(i1, i2)]])
       } else {
         factor(vec, levels = levels(vec)[seq_along(levels(vec))[order(i2, i1)]])
    
       }
    
    
    }
    
    f1(x, c("PH", "COND", "DBO"), c("B", "A", "C"))
    #[1] DBO_A  PH_A   COND_A DBO_B  PH_B   COND_B DBO_C  PH_C   COND_C
    #Levels: PH_B PH_A PH_C COND_B COND_A COND_C DBO_B DBO_A DBO_C
    
    
    f1(x, c("PH", "COND", "DBO"), c("B", "A", "C"), flag = "latter")
    #[1] DBO_A  PH_A   COND_A DBO_B  PH_B   COND_B DBO_C  PH_C   COND_C
    #Levels: PH_B COND_B DBO_B PH_A COND_A DBO_A PH_C COND_C DBO_C
    
        2
  •  2
  •   Rui Barradas    6 年前

    使用CRAN包 forcats 您可以组合一个因素列表。下面的函数期望作为输入2个向量, prefix suffix ,按您想要的顺序排列。
    论证 sep = "_" 将其默认设置为问题中的分隔符。如果需要,可以传递另一个分隔符。

    library(forcats)
    
    custom_fct <- function(prefix, suffix, sep = "_"){
      lst <- lapply(prefix, function(p){
        f <- paste(p, suffix, sep = sep)
        factor(f, levels = f)
      })
      fct_c(!!!lst)
    }
    
    x <- c("PH", "COND", "DBO")
    y <- c("B", "A", "C")
    
    custom_fct(x, y)
    

    编辑。

    看到这个问题的另一种方法,我只在op的注释之后才理解,就是有一个输入数据向量 x 将其强制为因子和2个向量,其中一个是前缀,另一个是后缀。下面的函数创建这样一个向量,不需要外部包。

    custom_fct2 <- function(x, prefix, suffix, sep = "_"){
      lst <- lapply(prefix, function(p){
        paste(p, suffix, sep = sep)
      })
      factor(x, levels = unlist(lst))
    }
    
    x <- c("DBO_A", "PH_A", "COND_A", "DBO_B",
           "PH_B", "COND_B", "DBO_C", "PH_C", "COND_C")
    a <- c("PH", "COND", "DBO")
    b <- c("B", "A", "C")
    
    custom_fct2(x, a, b)
    #[1] DBO_A  PH_A   COND_A DBO_B  PH_B   COND_B DBO_C  PH_C  
    #[9] COND_C
    #9 Levels: PH_B PH_A PH_C COND_B COND_A COND_C DBO_B ... DBO_C
    
        3
  •  2
  •   Henrik plannapus    6 年前

    使用 data.table 便利功能 tstrsplit setorderv .

    为子字符串创建(任意)列名的向量( cols <- c("V1", "V2") )将矢量转换为 数据表 ( d <- data.table(x) )将向量拆分为两列( (cols) := tstrsplit(x, split = "_") )设置子字符串的因子级别( factor(V1, levels = l1) )按第一个子串、第二个子串或第二个子串和第一个子串对数据排序。( setorderv(d, if(prec == 1) cols else rev(cols)) )使用data.table中的有序列“x”作为向量“x”的因子级别( factor(x, levels = d$x) )

    library(data.table)
    
    f <- function(x, l1, l2, prec){
      cols <- c("V1", "V2")
      d <- data.table(x)
      d[ , (cols) := tstrsplit(x, split = "_")]
      d[ , `:=`(
        V1 = factor(V1, levels = l1),
        V2 = factor(V2, levels = l2))]
      setorderv(d, if(prec == 1) cols else rev(cols))
      factor(x, levels = d$x)
    }
    
    # First substring has precedence
    f(x, l1 = c("PH", "COND", "DBO"), l2 = c("B", "A", "C"), prec = 1)
    # [1] DBO_A  PH_A   COND_A DBO_B  PH_B   COND_B DBO_C  PH_C   COND_C
    # Levels: PH_B PH_A PH_C COND_B COND_A COND_C DBO_B DBO_A DBO_C
    
    # Second substring has precedence
    f(x, l1 = c("PH", "COND", "DBO"), l2 = c("B", "A", "C"), prec = 2)
    # [1] DBO_A  PH_A   COND_A DBO_B  PH_B   COND_B DBO_C  PH_C   COND_C
    # Levels: PH_B COND_B DBO_B PH_A COND_A DBO_A PH_C COND_C DBO_C
    

    base 或者,以类似的方式,但将子字符串放在矩阵中。使用标准regex(参见 here )获取子字符串。转换为因子并设置级别。创建列索引( i <- c(1, 2, 1)[prec:(prec + 1)] )“x”的顺序级别( as.character(x)[order(m[ , i[1]], m[ , i[2]])]) )

    f2 <- function(x, l1, l2, prec){
      m <- cbind(factor(sub("_.*", "", x), l1), factor(sub(".*_", "", x), l2))
      i <- c(1, 2, 1)[prec:(prec + 1)]
      factor(x, levels = as.character(x)[order(m[ , i[1]], m[ , i[2]])])}
    
    f2(x, l1, l2, prec = 1)
    # [1] DBO_A  PH_A   COND_A DBO_B  PH_B   COND_B DBO_C  PH_C   COND_C
    # Levels: PH_B PH_A PH_C COND_B COND_A COND_C DBO_B DBO_A DBO_C
    
    f2(x, l1, l2, prec = 2)
    # [1] DBO_A  PH_A   COND_A DBO_B  PH_B   COND_B DBO_C  PH_C   COND_C
    # Levels: PH_B COND_B DBO_B PH_A COND_A DBO_A PH_C COND_C DBO_C
    
        4
  •  -1
  •   Joseph Clark McIntyre    6 年前

    怎么样啊

    x <- with(expand.grid(x = c("DBO", "PH", "COND"), y = c("A", "B", "C")),
              factor(paste(x, y, sep = "_"), levels = paste(x, y, sep = "_")))
    

    你不需要写出每一个可能的层次,只需要写出一方和另一方的层次。