【问题标题】:Melt a table (data.frame) based on values of comma-separated character vector column根据逗号分隔的字符向量列的值融合表(data.frame)
【发布时间】:2012-09-19 16:56:12
【问题描述】:

我正在做一个实验,其中我有一些“区域”和一些相关的统计数据(实际上是许多其他统计数据和描述性列),以及一个逗号分隔的位于这些区域的基因列表。此列表的数量可变,并且可能不包含任何内容(“NA”)。

我怎样才能“融化”表a:

  region_id  statistic      genelist
          1        2.5       A, B, C
          2        0.5    B, C, D, E
          3        3.2          <NA>
          4        0.1          E, F

要创建另一个表,为基因列表中的每个基因创建一个单独的条目?即

   region_id statistic gene
           1       2.5    A
           1       2.5    B
           1       2.5    C
           2       0.5    B
           2       0.5    C
           2       0.5    D
           2       0.5    E
           3       3.2 <NA>
           4       0.1    E
           4       0.1    F

我猜有一种方法可以用 R/plyr 做到这一点,但我不确定如何。提前致谢。

编辑:

使用 R,您可以使用以下代码重新创建这些玩具矢量:

a <- structure(list(region_id = 1:4, statistic = c(2.5, 0.5, 3.2, 
0.1), genelist = structure(c(1L, 2L, NA, 3L), .Label = c("A, B, C", 
"B, C, D, E", "E, F"), class = "factor")), .Names = c("region_id", 
"statistic", "genelist"), class = "data.frame", row.names = c(NA, 
-4L))

b <- structure(list(region_id = c(1L, 1L, 1L, 2L, 2L, 2L, 2L, 3L, 
4L, 4L), statistic = c(2.5, 2.5, 2.5, 0.5, 0.5, 0.5, 0.5, 3.2, 
0.1, 0.1), gene = structure(c(1L, 2L, 3L, 2L, 3L, 4L, 5L, NA, 
5L, 6L), .Label = c("A", "B", "C", "D", "E", "F"), class = "factor")), .Names = c("region_id", 
"statistic", "gene"), class = "data.frame", row.names = c(NA, 
-10L))

【问题讨论】:

  • 该表格是制表符分隔的还是固定宽度的?
  • TLP - 抱歉。页面标题以“perl - Melt...”开头,因为 perl 是最常用的标签。我不知道这是如何工作的,所以我删除了 Perl 标签,看看它是否会变成“R”,然后我把它放回去了。当我看到你的回答时,我很感激。
  • 我明白了。好吧,在这种情况下,我会取消删除我的答案。

标签: perl r data.table plyr reshape


【解决方案1】:

data.table 的时间、内存和编码效率解决方案

library(data.table)
DT <- data.table(a)
DT[, list(statistic, 
          gene = unlist(strsplit(as.character(genelist), ', ' ))),
   by = list(region_id)]

或者您可以使用 nice 格式的列表 data.table 版本 >= 1.8.2

DTL <- DT[, list(statistic, 
         gene = strsplit(as.character(genelist), ', ' )),
    by = list(region_id)]

DTL
##    region_id statistic    gene
## 1:         1       2.5   A,B,C
## 2:         2       0.5 B,C,D,E
## 3:         3       3.2      NA
## 4:         4       0.1     E,F

在这种情况下,gene 是列表列表

DTL[region_id == 1,unlist(gene)]
## [1] "A" "B" "C"
DTL[region_id == 2,unlist(gene)]
## [1] "B" "C" "D" "E"
# or if the following is of interest
DTL[statistic < 2,unlist(gene)]
## [1] "B" "C" "D" "E" "E" "F"

【讨论】:

    【解决方案2】:

    只需拆分字段,然后拆分基因并为每个基因打印一行。您可以通过将&lt;DATA&gt; 替换为&lt;&gt; 并使用输入文件作为perl 脚本的参数来在脚本中进行尝试,例如perl script.pl input.txt.

    use strict;
    use warnings;
    
    while (<DATA>) {
        chomp;                                   # remove newline
        my ($reg, $stat, $gene) = split /\t/;    # split fields
        my @genes = split /,\s*/, $gene;         # split genes
        for (@genes) {
            local $\ = "\n";                 # adds newline to print
            print join "\t", $reg, $stat, $_;
        }
    }
    
    __DATA__
    region_id   statistic   genelist
    1   2.5 A, B, C
    2   0.5 B, C, D, E
    3   3.2 <NA>
    4   0.1 E, F
    

    输出:

    region_id       statistic       genelist
    1       2.5     A
    1       2.5     B
    1       2.5     C
    2       0.5     B
    2       0.5     C
    2       0.5     D
    2       0.5     E
    3       3.2     <NA>
    4       0.1     E
    4       0.1     F
    

    【讨论】:

      【解决方案3】:

      有几种方法可以做到这一点。这种方式可行,尽管可能有更好的方法......

      library(stringr) # for str_split
      join(subset(a, select=c("region_id", "statistic")), 
           ddply(a, .(region_id), summarise, gene=str_split(genelist, ",\\S*")[[1]]))
      

      需要加载 plyr 和 stringr。

      哦,这里有一个更好的方法:

      ddply(a, .(region_id), 
            function(x) data.frame(gene=str_split(x$genelist, ",\\S*")[[1]], 
                                   statistic=x$statistic))
      

      【讨论】:

      • 谢谢。我的表实际上有很多统计数据和其他描述性列。有没有办法根据指定的列(“genelist”)来做到这一点,而不必明确说明我想要哪些其他列?
      • 看来您的第一个答案可以通过将 subset() 替换为 a[ ,-which(names(a)=="genelist")] 来工作
      • 是的,虽然我更喜欢语法a[,names(a)!='genelist']
      【解决方案4】:

      这是一种无需任何库的方法:

      data<-cbind(region_id=1:4, statistic=c(2.5, 0.5, 3.2, 0.1), genelist=c("A, B, C", "B, C, D, E", NA, "E, F"))
      
      do.call(rbind, 
              apply(data, 1, 
                    function(r) do.call(expand.grid, 
                                        c(unlist(r[-3]), 
                                          strsplit(r[3], ", ")))))
      

      输出:

            region_id statistic genelist
      1          1       2.5        A
      2          1       2.5        B
      3          1       2.5        C
      4          2       0.5        B
      5          2       0.5        C
      6          2       0.5        D
      7          2       0.5        E
      8          3       3.2     <NA>
      9          4       0.1        E
      10         4       0.1        F
      

      【讨论】:

        【解决方案5】:

        这是另一个使用plyr的单行代码

        ddply(a, .(region_id), transform, gene = str_split(genelist, ',')[[1]])
        

        【讨论】:

          【解决方案6】:

          Perl 解决方案:

          #!/usr/bin/perl
          <>;
          print "region_id\tstatistic\tgene\n";
          while(<>) {
            chomp;
            my ($reg, $stat, $genes) = split /\s+/, $_, 3;
            foreach my $gene (split /,\s*/, $genes) {
               print "$reg\t$stat\t$gene\n";
            }
          }
          

          只需通过此脚本将原始文件通过管道传输到输出文件中。

          目前输出值是制表符分隔的,没有右刷新,但如果确实需要,您可以修复它。

          【讨论】:

          • /\s+/ 上拆分没有意义。如果要概括,只需使用' ',但在这种情况下,我认为/\t/ 更可取。
          猜你喜欢
          • 1970-01-01
          • 2013-07-14
          • 1970-01-01
          • 1970-01-01
          • 2018-11-11
          • 2013-05-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多