2016-04-10 29 views
1

输入数据是基于如下柱2排序:打印是在指定值范围内基于特定的列作为AWK对所有行

1 100 
1 101 
1 200 
3 360 
4 800 
4 950 
4 952 

随着例如数据所需的输出是:

1 200 3 360 
4 800 4 950 
4 800 4 952 

也就是说,如果有第2列中的值在范围内的行:value2大于value1 + 100 & & value2小于value1 + 200。

我的尝试是:

awk 'BEGIN{FS="\t"; PREVLOC=$2; PREVLINE=$0}{ if($2>PREVLOC+200 || $2<PREVLOC+100 {PREVLOC=$2; PREVLINE=$0;} else {print PREVLINE"\t"$0; PREVLOC=$2; PREVLINE=$0;} }' inputfile 

节省了前行和前行2列到了比较变量。但是,它并不适用于所有情况。用示例数据,它不会打印最后一对。如果它们之间存在一行,其第二列值例如是0,则它将不输出800-950对。 890.

目前,我已经解决了这个问题,在完全不同的方式在bash有:

`while read var1 var2; do stuff with vars in awk; done<inputfile` 

但它是非常缓慢的。任何帮助深表感谢。

回答

1

我不知道这对你有多大的改进,因为它仍然是一个O(n^2)算法,但它都是awk,值得一试。

有两个通行证。 NR==FNR块是第一遍,并将整个文件读入内存(如果文件非常大,另一个可能的问题,如果你担心性能,我猜它会相当大)。对于每一行,我们在第二遍中存储要测试的范围。

第二遍按行逐行扫描每个完整的范围集合以找到与条件匹配的范围。

请务必注意,如图所示,在调用awk时,需要在命令行上提供两次输入文件。

$ cat input.txt 
1 100 
1 101 
1 200 
3 360 
4 800 
4 950 
4 952 

$ cat b.awk 
# first pass, load array with ranges 
NR==FNR {range[$0] = ($2 + 100) ":" ($2 + 200); next} 

# Here we process the file for the second time, looping through 
# all ranges for every line of input 
{ 
    for (i in range) { 
     split(range[i], r, ":") 
     if ($2 > r[1] && $2 < r[2]) { 
      print i, $0 
     } 
    } 
} 

$ awk -f b.awk input.txt input.txt 
1 200 3 360 
4 800 4 950 
4 800 4 952 
+0

嗨,非常感谢。这正是我所期待的。我的文件每行大约有100行,并且可以有多达1000行(当然并行处理它们)。实际数据在更多领域更复杂。除了while循环外,唯一的解决方法是创建所有可能的连接对,然后通过awk,例如, ('awk'{print 1'\ t“$ 0}'inputfile)<(awk'{print 1'\ t”$ 0}'inputfile)的示例数据'join -1 1 -2 1 -t $' | awk'BEGIN {OFS = FS =“\ t”} {if($ 5> $ 3 + 100 && $ 5 <$ 3 + 200)print $ 2,$ 3,$ 4,$ 5}' – 5heikki

+0

太棒了! http://backreference.org/2010/02/10/idiomatic-awk/中的“双文件处理”部分对这种使用awk来处理这些自连接问题的方式给出了更多的解释(当然这两个文件是相同的文件)。 – jas