根据示例代码将第三列中的不同术语合并为一行

根据示例代码将第三列中的不同术语合并为一行

我有一些包含三列的基因组数据。第 1 列是样本,第 2 列是癌症类型,第 3 列是机制。每个样本在第三行中都有一个或多个分配给它的基因机制(例如6个ecDNA)。目前,每个样本的每种机制有多行(例如,样本 x 有 4 行“6 ecDNA”机制,5 行“5 删除”机制,其中每一行对应于该样本发现的一个遗传畸变)。

我想知道是否有一种方法可以在一行中显示每个样本的数据中的所有类型的机制

(例如癌症缩写机制示例)

     x        Bladder     6 ecDNA, 5 deletion

有没有办法将样本的机制类型压缩为一行?编辑:这是 OG 数据集的代表性表格,其中包含示例样本“x”和“y”。

样本,癌症缩写,机制

 x,       Bladder,         6 ecDNA

 x,       Bladder,         6 ecDNA

 x,       Bladder,         5 deletion

 x,       Bladder,         5 deletion

 y,       Osteosarcoma,    4 transposition

 y,       Osteosarcoma,    4 transposition

y,       Osteosarcoma,    5 deletion

y,       Osteosarcoma,    6 ecDNA

....

答案1

#!/usr/bin/perl
use strict;

my %rec;

while(<>) {
  chomp;
  s/^\s+|\s*$//;
  next if m/^$/;

  my ($c1,$c2,$c3) = split /\s{2,}|\t+/;

  $rec{$c1}->{$c2}->{$c3} = 1;
}

# find the largest widths (lengths) of the first two columns, minimum 6 for each
my $l1=6;
my $l2=6;
foreach my $k1 (keys %rec) {
  $l1 = length($k1) if (length($k1) > $l1);
  foreach my $k2 (keys %{ $rec{$k1} }) {
    $l2 = length($k2) if (length($k2) > $l2);
  }
};

# use that to build a format string
my $fmt = "%-${l1}s\t%-${l2}s\t%s\n";

printf $fmt, "Sample", "Cancer", "Mechanisms";

foreach my $k1 (sort keys %rec) {
  foreach my $k2 (sort keys %{ $rec{$k1} }) {
    printf $fmt, $k1, $k2, join(", ",sort keys %{ $rec{$k1}->{$k2} });
  };
}

此 Perl 脚本读取每个输入行,删除任何前导或尾随空格,并跳过空行。我不确定输入文件是否只有一个或多个分隔每一列的选项卡,或者多个空白字符 - 我编写它是为了应对其中之一。

它构建了一个名为 %rec 的数据结构来保存每条记录。数据结构是多级哈希(关联数组),其中前两级是前两列(Sample 和 Cancer)的名称,第三级包含 Mechanisms。

即 %rec 数据结构最终看起来像这样:

%rec = {
  x => { Bladder => { "5 deletion" => 1, "6 ecDNA" => 1 } },
  y => { Osteosarcoma => { "4 transposition" => 1, "5 deletion" => 1, "6 ecDNA" => 1 }, },
}

有关 perl 数据结构的更多信息,请参阅man perldsc或。perldoc perldsc另请参阅man perlreftut有关制作和使用此类数据结构的简短教程。0

一旦它完成读取整个输入文件,就打印每条记录的摘要行(在计算出每列的宽度之后)。输出是制表符分隔的。

将其另存为,例如combine.pl,并使其可执行chmod +x

$ chmod +x ./combine.pl
$ ./combine.pl input.txt
Sample  Cancer          Mechanisms
x       Bladder         5 deletion, 6 ecDNA
y       Osteosarcoma    4 transposition, 5 deletion, 6 ecDNA

答案2

datamash是一款简单的工具。不过,需要删除空行,并多注意数据输入格式。

sed '/^$/d' infile | datamash -W -g 1,2 unique 3,4

表示根据第 1 列和第 2 列进行分组,然后将第 3 列和第 4 列折叠为逗号分隔列表,但仅显示唯一值。-W表示使用空格(而不是制表符)作为字段分隔符。sed只是删除空行。使用将创建此类输出的输入文件:

Sample  Cancer  Abbrev  Mechanism
x   Bladder 5,6 deletion,ecDNA
y   Osteosarcoma    4,5,6   deletion,ecDNA,transposition

请注意 Abbrev 和 Mechanism 如何单独分组,并注意它们彼此独立排序(即4deletion是第一个,您的输入有4as transposition) - 这来自unique需要排序的操作。对于您请求的确切输出,这可以工作:

awk 'BEGIN {OFS="\t"} $0 != "" {print $1,$2,$3" "$4}' infile |
datamash -g 1,2 unique 3

Sample  Cancer  Abbrev Mechanism
x   Bladder 5 deletion,6 ecDNA
y   Osteosarcoma    4 transposition,5 deletion,6 ecDNA

这里awk用于将字段制表符分隔开,但只需在字段 3 和 4 之间保留一个空格即可将字段 3 和 4 合并为一个 - 这种方式datamash将它们视为一个整体,因为默认情况下它通过制表符作为分隔符。awk还在这里筛选出空行。

答案3

使用磨坊主,如果您的输入是 CSV,您可以运行

mlr --c2t -N nest --implode --values --across-records --nested-fs "," -f 3 then clean-whitespace input.csv

具有

x Bladder      6 ecDNA,6 ecDNA,5 deletion,5 deletion
y Osteosarcoma 4 transposition,4 transposition,5 deletion,6 ecDNA

我使用的输入文件

x,Bladder,6 ecDNA
x,Bladder,6 ecDNA
x,Bladder,5 deletion
x,Bladder,5 deletion
y,Osteosarcoma,4 transposition
y,Osteosarcoma,4 transposition
y,Osteosarcoma,5 deletion
y,Osteosarcoma,6 ecDNA

如果你想要唯一的值,命令是

mlr --c2p -N uniq -a then  nest --implode --values --across-records --nested-fs "," -f 3 then clea
n-whitespace input.csv

答案4

使用awk

假设第三个和第四个字段具有固定值,即6在第三个字段中始终ecDNA在第四个字段中具有固定值,这应该有效:

awk 'BEGIN{FS=",";OFS="\t"; printf "Sample\tCancer\tMechanisms\n"}
NF{len=split(ar[$1 OFS $2], a, " ");   
for(i=1;i<=len;i++) if (a[i] == $4) $4="";
if ($4) ar[$1 OFS $2]=(ar[$1 OFS $2] ? ar[$1 OFS $2]  ", " : "") $3 " " $4}
END{for(j in ar) print j,ar[j]}' input

在上面的命令中,FS=","因为列是用逗号分隔的,并且OFS="\t"因为我们想要制表符分隔的列。

相关内容