【问题标题】:Split column label by number of letters/characters in R按R中的字母/字符数拆分列标签
【发布时间】:2014-02-22 20:14:07
【问题描述】:

我有一个大型数据集,其中所有列标题都是单独的 IDS,每个 8 个字符的长度。我想将这些单独的 ID 分成 2 行,其中第一行 ID 包含前 7 个字符,第二行只包含最后一个字符。

当前数据集:

ID1:    Indiv01A    Indiv01B    Indiv02A    Indiv02B    Speci03A    Speci03B

目标数据集:

ID1:    Indiv01 Indiv01 Indiv02 Indiv02 Speci03 Speci03  
ID2:    A   B   A   B   A   B

我查看了有关拆分数据的其他帖子,但它们似乎都有一种独特的方式来分隔列名(即:有一个逗号分隔两个组件,或一个句点)。

这是我认为效果最好的代码,但我不知道如何将“7 个字符”编码为分割点,而不是逗号:

sapply(strsplit(as.character(d$ID), ",")

任何帮助将不胜感激。

【问题讨论】:

    标签: regex r split multiple-columns


    【解决方案1】:

    这是strsplit 解决方案的正则表达式。它在第 7 个和第 8 个字符之间分割字符串:

    ID1 <- c("Indiv01A", "Indiv01B", "Indiv02A", "Indiv02B", "Speci03A", "Speci03B")
    
    res <- strsplit(ID1, "(?<=.{7})", perl = TRUE)
    
    # [[1]]
    # [1] "Indiv01" "A"      
    # 
    # [[2]]
    # [1] "Indiv01" "B"      
    # 
    # [[3]]
    # [1] "Indiv02" "A"      
    # 
    # [[4]]
    # [1] "Indiv02" "B"      
    # 
    # [[5]]
    # [1] "Speci03" "A"      
    # 
    # [[6]]
    # [1] "Speci03" "B"  
    

    现在,您可以使用rbind 创建两列:

    do.call(rbind, res)
    #      [,1]      [,2]
    # [1,] "Indiv01" "A" 
    # [2,] "Indiv01" "B" 
    # [3,] "Indiv02" "A" 
    # [4,] "Indiv02" "B" 
    # [5,] "Speci03" "A" 
    # [6,] "Speci03" "B" 
    

    正则表达式模式的解释:

    (?<=.{7})
    

    (?&lt;=) 是一个(积极的)回顾。它匹配指定模式之前的任何位置。在这里,模式是.{7}。点 (.) 匹配任何字符。 {7} 表示 7 次。因此,正则表达式匹配前面正好有 7 个字符的位置。

    【讨论】:

    • +1 比我提供的要优雅得多。你介意解释一下你的模式吗?
    • @jbaums 我添加了解释。
    • 这个解释很好,很容易理解!我唯一的困难是我仍然无法将其应用于我的列标题或 data.frame 中的第一行。有什么想法吗?谢谢!
    • @user2296922 您可以使用colnames(dat) 获取列名,使用dat[1, ] 获取第一行,其中dat 是您的数据框的名称。
    • 我正在尝试创建 2 行,因为第一行都是列标题。我已经按照您在 colnames 的建议下布置的代码工作了;我只是无法将新列标题(2 行)链接回原始数据帧(FK1):> ID1 ID1 [1] "Year" "FK1A001A" "FK1A001B" "FK1A002A" “FK1A002B” “FK1A003A” [7] “FK1A003B” “FK1B006A” “FK1B006B” > ID1new do.call(rbind,ID1new ) 然后我坚持将 ID1new 与 ID1 合并。
    【解决方案2】:

    这是一个 gsubfn 解决方案:

    library(gsubfn)
    
    strapplyc(ID1, "(.*)(.)", simplify = cbind)
    

    给出这个矩阵:

         [,1]      [,2]      [,3]      [,4]      [,5]      [,6]     
    [1,] "Indiv01" "Indiv01" "Indiv02" "Indiv02" "Speci03" "Speci03"
    [2,] "A"       "B"       "A"       "B"       "A"       "B"    
    

    如果您想要两列(而不是两行),请使用 rbind 代替 cbind

    【讨论】:

    • 感谢您强调这个包 - 以前没有遇到过!
    【解决方案3】:

    有几种方法可以解决这个问题。

    提取最终字符

    首先,substr:

    new.vec <- sapply(old.vec, function(x) substr(x, nchar(x), nchar(x)))
    

    或者,sub:

    new.vec <- sub('.*(.)', '\\1', old.vec)
    

    其中old.vec 是要拆分的字符串向量。

    出于兴趣,后一个选项使用 regular expression 转换为:“捕获(用括号括起来表示)跟随零个或多个其他字符 (.*) 的单个字符 (.),并替换与捕获的内容匹配 (\\1)”。有关详细信息,请参阅 ?gsubhere

    以上选项允许不同的字符串长度。但是,如果您 总是 想要在 7 个字符后拆分,并且字符串的第二部分总是只有一个字符,那么以下应该可以工作:

    new.vec <- substr(old.vec, 8, 8)
    

    (已编辑以包含提取字符串第一部分的方法。)

    提取所有最后一个字符

    过程类似。

    new.vec <- sapply(old.vec, function(x) substr(x, 1, nchar(x) - 1))
    
    new.vec <- sub('(.*).', '\\1', old.vec)
    
    new.vec <- substr(old.vec, 1, 7)
    

    【讨论】:

    • 感谢您的快速回复!我已经尝试了选项 1 和 3,但由于试图指示要更改的行而遇到了困难。数据集中的第一行(“年份”)包含各个 ID。我尝试了以下代码,输出如下。 > FK1split FK1split [1] "1" "1" "2" "2" "2 " "2" "2" "2" "2" "2" "2" "2" "2" "2" > FK1split FK1split [1] "" " """"""""""""""""""""""""" 有关如何轻松指示 data.frame 的哪一行应该拆分的任何想法?
    • 不要在第一个版本中将x 更改为71 - 您的更改只是提取FK1$Year 的每个元素的第一个字符。另外,请理解substr(FK1$Year, 8, 8) 是说要提取FK1$Year 的每个元素的第 8 到第 8(即,仅第 8)字符。看起来FK1$Year 可能没有 8 个字符。相应地调整88(它们指示从哪里开始和结束提取)。顺便说一句,将代码括在反引号中(通常在波浪号 ~ 键上)以格式化 cmets 中的代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-06
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 2013-03-12
    • 1970-01-01
    • 2020-07-21
    相关资源
    最近更新 更多