【问题标题】:split characters into two variables in data frame将字符拆分为数据框中的两个变量
【发布时间】:2013-04-24 15:14:49
【问题描述】:

假设我有一个这样的变量向量:

>variable
[1] "A1" "A1" "A1" "A1" "A2" "A2" "A2" "A2" "B1" "B1" "B1" "B1"

我想把它转换成这样的数据框:

  treatment time
1         A    1
2         A    1
3         A    1
4         A    1
5         A    2
6         A    2
7         A    2
8         A    2
9         B    1
10        B    1
11        B    1
12        B    1

为此,我使用了 reshape2 的 colsplit 函数。它需要一个模式来分割字符串,但我很快意识到没有明显的模式来分割两个字符而没有任何空格。 我试了"",得到如下结果:

> colsplit(trialm$variable,"",names=c("treatment","time"))
   treatment time
1         NA   A1
2         NA   A1
3         NA   A1
4         NA   A1
5         NA   A2
6         NA   A2
7         NA   A2
8         NA   A2
9         NA   B1
10        NA   B1
11        NA   B1
12        NA   B1

我还尝试了后向或前瞻正则表达式:

>colsplit(trialm$variable,"(?<=\\w)",names=c("treatment","time"))
Error in gregexpr("(?<=\\w)", c("A1", "A1", "A1", "A1", "A2", "A2", "A2",  : 
  invalid regular expression '(?<=\w)', reason 'Invalid regexp'

但它给了我上述错误。我该如何解决这个问题?

【问题讨论】:

  • 看看strsplit。您的代码将类似于:trialm$treatment &lt;- sapply(strsplit(trialm$variable, ''), '[', 1)
  • 我知道这是旧的,但是 colsplit 函数使用的 str_split_fixed 现在写得不同了,所以代码可以正常工作。

标签: r reshape2


【解决方案1】:

更新:2017 年 12 月 24 日

在某个地方,“stringr”包(使用“reshape2”导入并负责使用colsplit 进行拆分)开始将“stringi”用于其几个功能。一些行为似乎因此而改变了。

使用当前的“reshape2”(和当前的“stringr”包),colsplit 按照您对代码的预期方式工作:

packageVersion("reshape2")
## [1] ‘1.4.3’
packageVersion("stringr")
## [1] ‘1.2.0’

colsplit(variable, "", names = c("treatment", "time"))
##    treatment time
## 1          A    1
## 2          A    1
## 3          A    1
## 4          A    1
## 5          A    2
## 6          A    2
## 7          A    2
## 8          A    2
## 9          B    1
## 10         B    1
## 11         B    1
## 12         B    1

原答案:2013 年 4 月 24 日

如果可以在您的“变量”中检测到一种模式,但没有可以使用的干净拆分字符,则添加一个 :)

library(reshape2)
variable <- c("A1", "A1", "A1", "A1", "A2", "A2", 
              "A2", "A2", "B1", "B1", "B1", "B1")
## Here, we add a "." between upper case letters and numbers
colsplit(gsub("([A-Z])([0-9])", "\\1\\.\\2", variable), 
         "\\.", c("Treatment", "Time"))
#    Treatment Time
# 1          A    1
# 2          A    1
# 3          A    1
# 4          A    1
# 5          A    2
# ::::: snip :::: #
# 11         B    1
# 12         B    1

其他选项:2017 年 12 月 23 日

我的“splitstackshape”包有一个名为NoSep 的单一用途非导出辅助函数,可用于此目的:

splitstackshape:::NoSep(variable)
##    .var .time_1
## 1     A       1
## 2     A       1
## 3     A       1
## 4     A       1
## 5     A       2
## ::: snip :::: #
## 11    B       1
## 12    B       1

“tidyverse”(特别是“tidyr”包)有几个方便的函数用于将值拆分到不同的列:separateextractseparatealready been demonstrated by jazzuro,但解决方案是针对这个特定问题的。此外,它通常与分隔符一起工作得更好。 extract 期望您为要捕获的组指定正则表达式:

library(tidyverse)
data.frame(variable) %>% 
  extract(variable, into = c("Treatment", "Time"), regex = "([A-Z]+)([0-9]+)")
#    Treatment Time
# 1          A    1
# 2          A    1
# 3          A    1
# 4          A    1
# 5          A    2
# ::::: snip :::: #
# 11         B    1
# 12         B    1

【讨论】:

    【解决方案2】:

    substr 是另一种方式。

    > variable <- c(rep("A1", 4), rep("A2", 4), rep("B1", 4))
    > data.frame(treatment=substr(variable, 1,1), time=as.numeric(substr(variable,2,2)))
       treatmen time
    1         A    1
    2         A    1
    3         A    1
    4         A    1
    5         A    2
    6         A    2
    7         A    2
    8         A    2
    9         B    1
    10        B    1
    11        B    1
    12        B    1
    

    【讨论】:

    • 哈! +1 如果您认为最好,我会删除它。
    • 但是如果某些变量是“AA1”和“A12”呢?这种方法不会成功。
    • 阿南达,您可以在示例中使用正则表达式将字母与数字分开,然后使用这两列查看您有多少离散类别。
    • 两列,不是“拖”列
    【解决方案3】:

    如果您使用矢量variable 创建数据框,您现在可以使用tidyr 包中的separate()

    mydf <- data.frame(variable = c(rep("A1", 4), rep("A2", 4), rep("B1", 4)),
                       stringsAsFactors = FALSE)
    
    separate(mydf, variable, c("treatement", "time"), sep = 1)
    
    #   treatement time
    #1           A    1
    #2           A    1
    #3           A    1
    #4           A    1
    #5           A    2
    #6           A    2
    #7           A    2
    #8           A    2
    #9           B    1
    #10          B    1
    #11          B    1
    #12          B    1
    

    【讨论】:

      【解决方案4】:

      你可以使用substr来拆分它:

      例如

      df <- data.frame(treatment =   substr(variable, start = 1, stop = 1),
                       time =        substr(variable, start = 2, stop = 2) )
      

      【讨论】:

        【解决方案5】:

        使用正则表达式的另一种解决方案

        require(stringr)
        variable <- c(paste0("A", c(rep(1, 4), rep(2, 3))),
                      paste0("B", rep(1, 4))
                      )
        
        data.frame(
            treatment = str_extract(variable, "[[:alpha:]]"),
            time = as.numeric(str_extract(variable, "[[:digit:]]"))
            )
        
        ##    treatment time
        ## 1          A    1
        ## 2          A    1
        ## 3          A    1
        ## 4          A    1
        ## 5          A    2
        ## 6          A    2
        ## 7          A    2
        ## 8          B    1
        ## 9          B    1
        ## 10         B    1
        ## 11         B    1
        

        【讨论】:

        • +1。我认为这比substr更安全,如果模式可辨别但没有可用的分割字符,则依此类推。
        【解决方案6】:

        data.table v1.9.5 中引入了新功能 tstrsplit()t 代表转置。这是用strsplit() 拆分字符向量然后转置它的结果。

        # dummy data
        library(data.table)
        dt <- data.table(var = c(rep("A1", 4), rep("A2", 4), rep("B1", 4)))
        

        使用tstrsplit()

        dt[, tstrsplit(var, "")]
        
            V1 V2
         1:  A  1
         2:  A  1
         3:  A  1
         4:  A  1
         5:  A  2
         6:  A  2
         7:  A  2
         8:  A  2
         9:  B  1
        10:  B  1
        11:  B  1
        12:  B  1
        

        是的,就是这么简单。 :-)

        【讨论】:

        • 这是新的!这是我今天要记在笔记本上的函数。
        【解决方案7】:

        您可以使用 substring() 创建向量,然后使用 data.frame 函数连接它们。

        yyy<-c("A1", "A1", "A1", "A1", "A2", "A2", "A2", "A2", "B1", "B1", "B1", "B1")
        
        treatment<-substring(yyy, 1,1)
        
        time<-as.numeric(substring(yyy,2,2))
        
        data.frame(treatment,time)
        

        【讨论】:

        • +1 用于意识到 timefactor 并且您希望它是 numeric 使用 as.numeric
        【解决方案8】:

        你可以使用strsplit

        df <- t(data.frame(strsplit(variable, "")))
        rownames(df) <- NULL
        colnames(df) <- c("treatment" , "time" )
        df
              treatment time
         [1,] "A"       "1" 
         [2,] "A"       "1" 
         [3,] "A"       "1" 
         [4,] "A"       "1" 
         [5,] "A"       "2" 
         [6,] "A"       "2" 
         [7,] "A"       "2" 
         [8,] "A"       "2" 
         [9,] "B"       "1" 
        [10,] "B"       "1" 
        [11,] "B"       "1" 
        [12,] "B"       "1" 
        

        您可以使用rbind 代替t,然后强制转换为data.frame,如下所示:

        setNames(as.data.frame(do.call(rbind, strsplit(variable, ""))), 
                 c("Treatment", "Time"))
        #    Treatment Time
        # 1          A    1
        # 2          A    1
        # 3          A    1
        # 4          A    1
        # 5          A    2
        # 6          A    2
        # 7          A    2
        # 8          B    1
        # 9          B    1
        # 10         B    1
        # 11         B    1
        

        【讨论】:

        • 或许足以满足 OP 的需求,但如果某些变量是“AA1”和“A12”呢?这种方法不会成功。
        【解决方案9】:

        根据@Justin 的评论,我建议这样做(使用v &lt;- c("A1", "B2")):

        > t(sapply(strsplit(v, ''), '[', c(1, 2)))
             [,1] [,2]
        [1,] "A"  "1" 
        [2,] "B"  "2" 
        

        `'[' 之后的向量从分割向量中选择项目。所以我只分裂了一次,保留了两个项目。如果您想保留所有项目,这可能会更容易:

        t(sapply(strsplit(v, ''), identity))
        

        【讨论】:

          猜你喜欢
          • 2020-04-11
          • 2020-12-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-04-22
          • 2019-10-12
          • 2021-12-11
          相关资源
          最近更新 更多