如果标题后的所有条目都是特定字符串,则删除列

如果标题后的所有条目都是特定字符串,则删除列

我有一个如下所示的数据文件

Sample1 Sample2 Sample3 Sample4 Sample5 Sample6
./.       ./.      ./.    ./.     ./.    ./.
./.       ./.      ./.    ./.     A/G    ./.
./.       ./.      ./.    ./.     ./.    ./.
A/A       A/A      A/G    ./.     ./.    ./.

我想删除仅包含 ./ 的所有列。留给我

Sample1 Sample2 Sample3 Sample5
  ./.     ./.     ./.     ./.
  ./.     ./.     ./.     A/G
  ./.     ./.     ./.     ./.
  A/A     A/A     A/G     ./.

我觉得这是一个 sed 或 awk 命令,但感谢任何帮助。

答案1

使用awk

awk -v na="./." '
BEGIN{OFS=FS}
NR==FNR && NR>1 {
  for(i=1;i<=NF;i++){if($i!=na){s[i]=1}}
}
NR!=FNR {
  for(l in s){true} 
  for(i in s){if (i!=l){printf "%s"OFS,$i} else {printf "%s\n",$i}}
}
' file file

如果您的文件是制表符分隔的,您可能需要更改BEGIN{OFS=FS}为。BEGIN{OFS=FS="\t"}

解释:

  • 浏览文件两次(awk ... file file
  • 第一次(NR==FNR为了NR>1排除标题)您验证列 ( i) 中至少有一个值不是na="./.",将列号保存在变量中s
  • 第二次 ( ),对于保存在打印列值NR!=FNR中的每一列。 s(第一个循环让您知道要打印的最后一列(保存在变量中l,因此您可以在打印OFS或之间做出决定\n。)

输出:

Sample1 Sample2 Sample3 Sample5
./. ./. ./. ./.
./. ./. ./. A/G
./. ./. ./. ./.
A/A A/A A/G ./.

如果您的文件是制表符分隔的,则输出会更好一点,如果不是,您可以添加|column -t.然后它看起来像这样:

Sample1  Sample2  Sample3  Sample5
./.      ./.      ./.      ./.
./.      ./.      ./.      A/G
./.      ./.      ./.      ./.
A/A      A/A      A/G      ./.

答案2

$ perl -F'\t' -lane '
   push @{$AoA[$.-1]}, map { push @Cols2Keep, $_ if $. > 1 && $F[$_] ne "./." && !$seen{$_}++; $F[$_] } 0 .. $#F}
   {print join "\t", @{$AoA[$_]}[sort { $a <=> $b } @Cols2Keep] for 0 .. $#AoA
' file.tsv

结果:

Sample1 Sample2 Sample3 Sample5
./.     ./.     ./.     ./.
./.     ./.     ./.     A/G
./.     ./.     ./.     ./.
A/A     A/A     A/G     ./.

在职的:

  • array of array @AoA,将文件存储在由记录号索引的数组中$.。因为$.从 1 开始,而数组Perl0-indexed我们标准化的$.
  • non ./.数组 @Cols2keep 存储在其中找到元素的列号。我们也不希望其中有重复项,因此我们通过散列的方式对其进行重复数据删除,%seen散列的键是这些列号。限制是我们跳过检查第一条记录。

相关内容