【问题标题】:R separate lines into columns specified by start and endR将行分隔为由开始和结束指定的列
【发布时间】:2018-07-03 10:52:57
【问题描述】:

我想将由字符串组成的数据集拆分为由 start 和 end 指定的列。

我的数据集如下所示:

>head(templines,3)
[1] "201801 1  78"
[2] "201801 2  67"
[3] "201801 1  13"

我想通过使用数据字典指定我的列来拆分它:

>dictionary
col_name col_start col_end  
year      1         4  
week      5         6  
gender    8         8  
age       11        12  

这样就变成了:

year    week    gender    age
2018    01      1         78
2018    01      2         67
2018    01      1         13

实际上,数据来自长期运行的调查,一些列之间的空白代表不再收集的变量。它有很多变量,所以我需要一个可以扩展的解决方案。

tidyr::separate 中,您似乎只能通过指定要拆分的位置来拆分,而不是开始和结束位置。有没有办法使用开始/结束?

我想用read_fwf 来做这件事,但我似乎无法在我已经加载的数据集上使用它。我只能通过首先导出为 txt 然后从这个 .txt 中读取来使其工作:

write_lines(templines,"t1.txt")

read_fwf("t1.txt", 
     fwf_positions(start = dictionary$col_start,
                   end = dictionary$col_end,
                   col_names = dictionary$col_name)

是否可以在已加载的数据集上使用read_fwf

【问题讨论】:

    标签: r dplyr tidyr read.fwf


    【解决方案1】:

    直接回答您的问题:是的,可以将read_fwf 与已加载的数据一起使用。文档的相关部分是关于参数file的部分:

    Either a path to a file, a connection, or literal data (either a single string or a raw vector).
    ...
    Literal data is most useful for examples and tests. 
    It must contain at least one new line to be recognised as data (instead of a path).
    

    因此,您可以简单地折叠数据,然后使用read_fwf

    templines %>% 
      paste(collapse = "\n") %>% 
      read_fwf(., fwf_positions(start = dictionary$col_start,
                                end = dictionary$col_end,
                                col_names = dictionary$col_name))
    

    这应该扩展到多列,并且对于多行来说很快(在我的机器上,100 万行和四列大约需要半秒)。

    有一些关于解析失败的警告,但它们来自您的字典。如果您将最后一行更改为age, 11, 12,它将按预期工作。

    【讨论】:

      【解决方案2】:

      substring的解决方案:

      library(data.table)
      x <- transpose(lapply(templines, substring, dictionary$col_start, dictionary$col_end))
      setDT(x)
      setnames(x, dictionary$col_name)
      # > x
      #    year week gender age
      # 1: 2018   01      1  78
      # 2: 2018   01      2  67
      # 3: 2018   01      1  13
      

      【讨论】:

        【解决方案3】:

        这个怎么样?

        data.frame(year=substr(templines,1,4), 
                   week=substr(templines,5,6), 
                   gender=substr(templines,7,8), 
                   age=substr(templines,11,13))
        

        【讨论】:

        • 不幸的是,太简单了,对不起,我应该指定我需要一个可以扩展到数百个变量的解决方案......
        【解决方案4】:

        使用基础 R:

        m = list(`attr<-`(dat$col_start,"match.length",dat$col_end-dat$col_start+1))
        
        d = do.call(rbind,regmatches(x,rep(m,length(x))))
        
        setNames(data.frame(d),dat$col_name)
        
          year week gender age
        1 2018   01      1  78
        2 2018   01      2  67
        3 2018   01      1  13
        

        使用的数据:

        x = c("201801 1  78", "201801 2  67", "201801 1  13")
        
        dat=read.table(text="col_name col_start col_end  
                   year      1         4  
                   week      5         6  
                   gender    8         8  
                   age       11        13 ",h=T)
        

        【讨论】:

          【解决方案5】:

          我们可以使用来自tidyverseseparate

          library(tidyverse)
          data.frame(Col = templines) %>% 
                separate(Col, into = dictionary$col_name, sep= head(dictionary$col_end, -1))
          #  year week gender  age
          #1 2018   01      1   78
          #2 2018   01      2   67
          #3 2018   01      1   13
          

          convert = TRUE 参数也可以与separate 一起使用,以将数字列作为输出

          tibble(Col = templines) %>% 
             separate(Col, into = dictionary$col_name, 
                 sep= head(dictionary$col_end, -1), convert = TRUE)
          # A tibble: 3 x 4
          #   year  week gender   age
          #  <int> <int>  <int> <int>
          #1  2018     1      1    78
          #2  2018     1      2    67
          #3  2018     1      1    13
          

          数据

          dictionary <- structure(list(col_name = c("year", "week", "gender", "age"), 
          col_start = c(1L, 5L, 8L, 11L), col_end = c(4L, 6L, 8L, 13L
          )), .Names = c("col_name", "col_start", "col_end"),
           class = "data.frame", row.names = c(NA, -4L))
          
          templines <- c("201801 1  78", "201801 2  67", "201801 1  13")
          

          【讨论】:

            【解决方案6】:

            这是一个显式函数,它似乎按照您想要的方式工作。

            split_func<-function(char,ref,name,start,end){
              res<-data.table("ID" = 1:length(char))
              for(i in 1:nrow(ref)){
                res[,ref[[name]][i] := substr(x = char,start = ref[[start]][i],stop = ref[[end]][i])]
              }
              return(res)
            }
            

            我已经创建了和你一样的输入文件:

            templines<-c("201801 1  78","201801 2  67","201801 1  13")
            dictionary<-data.table("col_name" = c("year","week","gender","age"),"col_start" = c(1,5,8,11),
                                   "col_end" = c(4,6,8,13))
            #   col_name col_start col_end
            #1:     year         1       4
            #2:     week         5       6
            #3:   gender         8       8
            #4:      age        11      13
            

            至于参数,
            char - 具有要拆分的值的字符向量
            ref - 参考表或字典
            name - 参考中的列号包含您想要的列名的表
            start - 包含起点的参考表中的列号
            end - 包含停止点的参考表中的列号

            如果我对这些输入使用这个函数,我会得到以下结果:

            out<-split_func(char = templines,ref = dictionary,name = 1,start = 2,end = 3)
            
            #>out
            #   ID year week gender age
            #1:  1 2018   01      1  78
            #2:  2 2018   01      2  67
            #3:  3 2018   01      1  13
            

            我必须包含一个“ID”列来启动数据表并使这更容易。如果您想稍后删除它,您可以使用:

            out[,ID := NULL]
            

            希望这更接近您正在寻找的解决方案。

            【讨论】:

            • 谢谢,但这又不能扩展到数百个变量...有没有办法将开始和结束作为向量提供?
            • 您可以提供一个额外的数据集,就像您在此处显示的那样,用于获取我认为的开始值和结束值。
            • 好的,让我看一下,我会相应地编辑我的答案
            • 请检查更新后的解决方案是否效果更好。这是一个有趣的练习:)
            猜你喜欢
            • 1970-01-01
            • 2021-04-15
            • 1970-01-01
            • 1970-01-01
            • 2021-11-30
            • 1970-01-01
            • 1970-01-01
            • 2017-04-13
            • 2019-06-08
            相关资源
            最近更新 更多