按行号和列号设置文件

时间:2016-11-28 10:28:20

标签: bash awk subset bioinformatics cut

我们希望在行和列上对文本文件进行子集化,其中从文件中读取行和列编号。排除标题(第1行)和rownames(第1列)。

inputFile.txt 制表符分隔的文本文件

header  62  9   3   54  6   1
25  1   2   3   4   5   6
96  1   1   1   1   0   1
72  3   3   3   3   3   3
18  0   1   0   1   1   0
82  1   0   0   0   0   1
77  1   0   1   0   1   1
15  7   7   7   7   7   7
82  0   0   1   1   1   0
37  0   1   0   0   1   0
18  0   1   0   0   1   0
53  0   0   1   0   0   0
57  1   1   1   1   1   1

subsetCols.txt 逗号分隔,没有空格,一行,数字有序。在实际数据中,我们有500K列,需要子集~10K。

1,4,6

subsetRows.txt 逗号分隔,没有空格,一行,数字有序。在实际数据中,我们有20K行,需要约为300的子集。

1,3,7

使用 cut awk 循环(Related post: Select rows using awk)的当前解决方案:

# define vars
fileInput=inputFile.txt
fileRows=subsetRows.txt
fileCols=subsetCols.txt
fileOutput=result.txt

# cut columns and awk rows
cut -f2- $fileInput | cut -f`cat $fileCols` | sed '1d' | awk -v s=`cat $fileRows` 'BEGIN{split(s, a, ","); for (i in a) b[a[i]]} NR in b' > $fileOutput

输出文件:result.txt

1   4   6
3   3   3
7   7   7

问题:
这个解决方案适用于小文件,对于50K行和200K列的较大文件,它需要太长时间,15分钟以上,仍在运行。我认为 cut 这些列工作正常,选择行是慢点。

有更好的方法吗?

真实输入文件信息:

# $fileInput:
#        Rows = 20127
#        Cols = 533633
#        Size = 31 GB
# $fileCols: 12000 comma separated col numbers
# $fileRows: 300 comma separated row numbers

有关该文件的更多信息:文件包含GWAS基因型数据。每行代表样本(个体),每列代表SNP。对于进一步的基于区域的分析,我们需要对样本(行)和SNP(列)进行子集化,以使数据更易于管理(小)作为其他统计软件(如)的输入。

系统:

$ uname -a
Linux nYYY-XXXX ZZZ Tue Dec 18 17:22:54 CST 2012 x86_64 x86_64 x86_64 GNU/Linux

更新@JamesBrown下面提供的解决方案混合了我系统中列的顺序,因为我使用的是不同版本的awk,我的版本是:GNU Awk 3.1.7 < / p>

5 个答案:

答案 0 :(得分:21)

即使在If programming languages were countries, which country would each language represent?,他们也会说......

  Awk:朝鲜。顽固地抵制变革,其用户似乎不自然地喜欢它,原因我们只能推测。

......每当你看到自己管道sed,cut,grep,awk等时,停下来对自己说: awk可以让它独自一人!

因此,在这种情况下,需要提取行和列(调整它们以排除标题和第一列),然后缓冲输出以最终打印它。

/guide/id

使用您的示例文件:

awk -v cols="1 4 6" -v rows="1 3 7" '
    BEGIN{
       split(cols,c); for (i in c) col[c[i]]  # extract cols to print
       split(rows,r); for (i in r) row[r[i]]  # extract rows to print
    }
    (NR-1 in row){
       for (i=2;i<=NF;i++) 
              (i-1) in col && line=(line ? line OFS $i : $i); # pick columns
              print line; line=""                             # print them
    }' file

使用示例文件和输入作为变量,用逗号分隔:

$ awk -v cols="1 4 6" -v rows="1 3 7" 'BEGIN{split(cols,c); for (i in c) col[c[i]]; split(rows,r); for (i in r) row[r[i]]} (NR-1 in row){for (i=2;i<=NF;i++) (i-1) in col && line=(line ? line OFS $i : $i); print line; line=""}' file
1 4 6
3 3 3
7 7 7

我很确定这会更快。例如,您可以检查Remove duplicates from text file based on second text file以查看某些基准,比较awk -v cols="$(<$fileCols)" -v rows="$(<$fileRows)" 'BEGIN{split(cols,c, /,/); for (i in c) col[c[i]]; split(rows,r, /,/); for (i in r) row[r[i]]} (NR-1 in row){for (i=2;i<=NF;i++) (i-1) in col && line=(line ? line OFS $i : $i); print line; line=""}' $fileInput awk相比的效果。

最佳,
金正恩

答案 1 :(得分:6)

Gnu awk 4.0或更高版本中的一个,因为列排序依赖于> $.fn [] for。行和列号从文件中读取:

PROCINFO["sorted_in"]

某些性能提升可能来自将$ awk ' BEGIN { PROCINFO["sorted_in"]="@ind_num_asc"; } FILENAME==ARGV[1] { # process rows file n=split($0,t,","); for(i=1;i<=n;i++) r[t[i]] } FILENAME==ARGV[2] { # process cols file m=split($0,t,","); for(i=1;i<=m;i++) c[t[i]] } FILENAME==ARGV[3] && ((FNR-1) in r) { # process data file for(i in c) printf "%s%s", $(i+1), (++j%m?OFS:ORS) }' subsetRows.txt subsetCols.txt inputFile.txt 1 4 6 3 3 3 7 7 7 处理块移至顶部空间1和2并在其末尾添加ARGV[3]

答案 2 :(得分:2)

不要从两个优秀的答案中拿走任何东西。仅仅因为这个问题涉及大量数据,我发布了2个答案的组合,以加快处理速度。

awk -v cols="$(<subsetCols.txt)" -v rows="$(<subsetRows.txt)" '
BEGIN {
   n = split(cols, c, /,/)
   split(rows, r, /,/)
   for (i in r)
      row[r[i]]
}
(NR-1) in row {
   for (i=1; i<=n; i++)
      printf "%s%s", $(c[i]+1), (i<n?OFS:ORS)
}' inputFile.txt

PS:这应该适用于较旧的awk版本或非gnu awk。

答案 3 :(得分:0)

我们可以改进@anubhava解决方案 摆脱每行搜索超过10k的值 通过利用输入已经排序的事实来查看我们是否在正确的行上

awk -v cols="$(<subsetCols.txt)" -v rows="$(<subsetRows.txt)" '
BEGIN {
   n = split(cols, c, /,/)
   split(rows, r, /,/)
   j=1;
}
(NR-1) == r[j] { 
   j++
   for (i=1; i<=n; i++)
      printf "%s%s", $(c[i]+1), (i<n?OFS:ORS)
}' inputFile.txt

答案 4 :(得分:-1)

Python有一个csv模块。您在列表中读取一行,将所需的列打印到标准输出,冲洗,清洗,重复。

这应该将列分为20,000到30,000。

import csv
with open('foo.txt') as f:
    gwas = csv.reader(f, delimiter=',', quoting=csv.QUOTE_NONE)
    for row in gwas:
        print(row[20001:30001]