我们希望在行和列上对文本文件进行子集化,其中从文件中读取行和列编号。排除标题(第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(列)进行子集化,以使数据更易于管理(小)作为其他统计软件(如r)的输入。
系统:
$ 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>
答案 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]