【问题标题】:Custom factor levels in a concatenated string连接字符串中的自定义因子级别
【发布时间】:2019-06-01 12:49:33
【问题描述】:

我有一个因子变量,它由两个由_ 分隔的子字符串组成,例如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 在左侧 (LHS)。
  2. B A C 在右侧 (RHS)。

此外,我想指定哪一边,LHS 或 RHS,优先于另一边。根据优先级的不同,级别的整体顺序会有所不同:

(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, ...))这样解决它,但是我的关卡比上面显示的要多,所以这看起来很荒谬。

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

【问题讨论】:

    标签: r refactoring


    【解决方案1】:

    如何接近类似的东西

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

    您不需要写出所有可能的关卡,只需写出一侧和另一侧的关卡即可。

    【讨论】:

    • 你能说怎么不吗?鉴于您使用 expand.grid 的方式,粘贴将首先按 x 排序,然后按 y 排序,这就是我理解您需要的方式。
    • 重点不是我如何产生我的x。这只是一个可重复的数据。请阅读我的预期输出。
    【解决方案2】:

    使用 CRAN 包forcats,您可以组合一系列因素。下面的函数需要输入 2 个向量,prefixsuffix,按照您想要的顺序。
    参数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
    

    【讨论】:

    • x 在我的问题中是给定的数据。我想要的是如何使用x 来获取两个预期的数据。
    • @DarrenTsai 我不明白,x 是给的,是的,y 不是?你想从x 得到y?我的答案中的功能与您在问题中描述的一样。
    • 是的,太好了!!非常感谢。
    • 但是我怎样才能得到我的第二个预期输出呢? custom_fct2(x, b, a) ?
    【解决方案3】:

    我们可以使用base R 来执行此操作。使用sub 删除向量的levels 中的子字符串,使用match 通过检查自定义顺序中的那些值来创建数字索引,通过ordering 重新分配factorlevels基于matching索引的向量的levels序列

    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)]])
    

    对于第二种情况,只需反转order中的索引

    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
    

    【讨论】:

    • 嘿,你的方法很棒,但是你改变了我的原始数据。您可以比较我的预期数据和您的输出。
    • @DarrenTsai 对不起,我忘了检查输出。感谢您指出了这一点。修复它
    【解决方案4】:

    使用data.table 便利函数tstrsplitsetorderv

    为子字符串 (cols &lt;- c("V1", "V2")) 创建一个包含(任意)列名的向量。将向量转换为data.table (d &lt;- 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 的替代方案,类似的,但将子字符串放在矩阵中。使用标准正则表达式(参见例如here)来获取子字符串。转换为因子并设置水平。创建列索引 (i &lt;- 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
    

    【讨论】:

    • 这是我需要的。非常感谢您的回答和对我的问题的修改。
    • 我明白了。它按我的意愿工作。我非常感谢你的好意。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-09
    • 1970-01-01
    • 2014-12-05
    • 1970-01-01
    • 2013-11-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多