【问题标题】:Selecting specific rows of a tab-delimited file using bash (linux)使用 bash (linux) 选择制表符分隔文件的特定行
【发布时间】:2023-03-31 07:10:01
【问题描述】:

我的目录中有很多 txt 制表符分隔的文件,有几行和几列,例如

File1
Id    Sample   Time ...  Variant[Column16] ...
1     s1       t0        c.B481A:p.G861S
2     s2       t2        c.C221C:p.D461W
3     s5       t1        c.G31T:p.G61R
File2
Id    Sample   Time ...  Variant[Column16] ...
1     s1       t0        c.B481A:p.G861S
2     s2       t2        c.C21C:p.D61W
3     s5       t1        c.G1T:p.G1R

我正在寻找的是创建一个新文件:

  • 所有不同的变体 uniq
  • 重复的变体数量
  • 和文件位置

即:

NewFile
Variant             Nº of repeated       Location
c.B481A:p.G861S     2                    File1,File2
c.C221C:p.D461W     1                    File1
c.G31T:p.G61R       1                    File1
c.C21C:p.D61W       1                    File2
c.G1T:p.G1R         1                    File2

我认为在 bash 中使用带有 awk sort 和 uniq 的基本脚本会起作用,但我不知道从哪里开始。或者如果使用 Rstudio 或 python(3) 更容易,我可以尝试。

谢谢!!

【问题讨论】:

  • 我不确定第 3 列(作为一个简单的衬线,虽然你可以找到一种方法来做到这一点,我敢肯定)这样的事情可能会起作用:stackoverflow.com/questions/25652252/… For 1 and 2 你可以使用:awk '{print $4 }' file1.txt > combined.txtawk '{print $4 }' file2.txt >> combined.txt 然后sort combined.txt | uniq -c

标签: r linux bash


【解决方案1】:

纯粹的 bash。需要 4.0+ 版本

# two associative arrays
declare -A files
declare -A count

# use a glob pattern that matches your files
for f in File{1,2}; do
    {
        read header
        while read -ra fields; do
            variant=${fields[3]}        # use index "15" for 16th column
            (( count[$variant] += 1 ))
            files[$variant]+=",$f"
        done
    } < "$f"
done

for variant in "${!count[@]}"; do
    printf "%s\t%d\t%s\n" "$variant" "${count[$variant]}" "${files[$variant]#,}"
done

输出

c.B481A:p.G861S 2   File1,File2
c.G1T:p.G1R 1   File2
c.C221C:p.D461W 1   File1
c.G31T:p.G61R   1   File1
c.C21C:p.D61W   1   File2

输出行的顺序是不确定的:关联数组没有特定的顺序。

【讨论】:

  • 太棒了!但我不熟悉第一行,declare -A,我应该在那里写路径吗?如果我有超过 2 个文件?
  • @WindSur 您使用第 6 行中的 File{1,2} 模式获取文件,因此在您的情况下,这可能只是 **.txt
【解决方案2】:

我认为纯 bash 会很难,但每个人都有一些 awk :D

awk 'FNR==1{next}
{
  ++n[$16];
  if ($16 in a) {
    a[$16]=a[$16]","ARGV[ARGIND]
  }else{
    a[$16]=ARGV[ARGIND]
  }
}
END{
printf("%-24s %6s    %s\n","Variant","Nº","Location");
for (v in n) printf("%-24s %6d    %s\n",v,n[v],a[v])}' *

【讨论】:

  • 这就是我要找的东西,但我不确定为什么它也会打印出带有数字而不是变体的行
  • @WindSur 如果文件与您所说的完全一样,这不应该发生,我对其进行了测试。可能存在空行或缺少变体的行或完全不同格式的行。如果这些行实际上仅由制表符分隔,而不是由空格分隔,则应使用awk -FS='\t'。要查看是否有空行,请使用awk '/^${print ARGV[ARGIND], FNR}' *,更好的是您应该上传两个文件,以便我们可以重现该行为。
  • 我试过了,但我在这里没有看到任何文件:link 在这种情况下,如果我们从 0 开始,variant 列是第 10 位或第 9 位
  • 如果您的文件包含第 10 列中的变体,您需要将 $16 替换为 $10 。 awk 'FNR==1{next}{++n[$10];if ($10 in a) {a[$10]=a[$10]","ARGV[ARGIND]}else{a[$10]=ARGV[ARGIND]}}END{printf("%-24s %6s %s\n","Variant","Nº","Location");for (v in n) printf("%-24s %6d %s\n",v,n[v],a[v])}' *
  • 是的,我当然做到了,但它不适用于这些文件。所有文件都是制表符分隔的,所以我写了awk '-FS='\t'{++n[$10];if ($10 in a) {a[$10]=a[$10]","ARGV[ARGIND]}else{a[$10]=ARGV[ARGIND]}}END{printf("%-24s %6s %s\n","Variant","Nº","Location");for (v in n) printf("%-24s %6d %s\n",v,n[v],a[v])}' *?但不起作用
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多