首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在范围内使用索引键的awk

在范围内使用索引键的awk
EN

Stack Overflow用户
提问于 2014-06-12 19:17:41
回答 3查看 990关注 0票数 1

我有一个awk脚本,通常使用外部变量$a并行运行。

代码语言:javascript
运行
复制
 awk -v a=$a '$4>a-5 && $4<a+5 {print $10,$4}' INFILE 

当然,使用数组会运行得更快,所以我尝试这样做,让它做同样的事情(在LISTFILE中的$2是INFILE中$4的搜索值。

代码语言:javascript
运行
复制
 awk 'FNR==NR{a[$2]=($2-5);next}$4 in a{if ($4>a[$4] && $4<a[$4]+10 {print} LISTFILE INFILE

当然,这不起作用,因为awk扫描到键,然后开始测试if语句,因此只找到下游范围。不幸的是,这不是一个连续的列表,所以通常没有$2-5值,否则我会使用它作为数组的关键。

显然,我知道如何使用awk和bash的组合来实现这一点,但是我想知道是否有一个awk唯一的解决方案。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-06-14 02:17:00

我的第一个答案回答了实际的问题,并修复了awk脚本。但也许我没有抓住重点。如果您想要速度,并且不介意更多地使用您的多核处理器,您可以使用GNU并行。下面是一个实现,它将同时启动4个作业:

代码语言:javascript
运行
复制
awk_cmd='$4 > var - 5 && $4 < var + 5 { print $10, $4 }'

parallel -j 4 "awk -v var={} '$awk_cmd' INFILE" :::: LISTFILE

正如您所看到的,这将最多同时读取INFILE四次。这个答案,在调整了作业数量之后,应该提供与您使用shell描述的并行实现非常相似的性能。因此,您可能希望将LISTFILE拆分为较小的块,并将awk_cmd设置为我在前面的答复中发布的命令。可能有一种处理输入的最佳方法,但这在很大程度上取决于INFILE的大小和LISTFILE中元素的数量。HTH。

测试:

创建LISTFILE

代码语言:javascript
运行
复制
paste - - < <(seq 16) > LISTFILE

创建INFILE

代码语言:javascript
运行
复制
awk 'BEGIN { for (i=1; i<=9999999; i++) { print i, i, i, int(i * rand()), i, i, i, i, i, i } }' > INFILE

结果:

TEST1:

代码语言:javascript
运行
复制
time awk 'FNR==NR { a[$2]; next } { for (i in a) { if ($4 > i - 5 && $4 < i + 5) { print $10, $4 } } }' LISTFILE INFILE >/dev/null

real    0m45.198s
user    0m45.090s
sys     0m0.160s

TEST2:

代码语言:javascript
运行
复制
time for i in $(seq 1 2 16); do awk -v var="$i" '$4 > var - 5 && $4 < var + 5 { print $10, $4 }' INFILE; done >/dev/null

real    0m55.335s
user    0m54.433s
sys     0m0.953s

TEST3:

代码语言:javascript
运行
复制
awk_cmd='$4 > var - 5 && $4 < var + 5 { print $10, $4 }'

time parallel --colsep "\t" -j 4 "awk -v var={2} '$awk_cmd' INFILE" :::: LISTFILE >/dev/null

real    0m28.190s
user    1m42.750s
sys     0m1.757s

我对 的回答:

1:

The awk1 script does not run much faster than the awk script.

在我看来,节省15%的时间是相当重要的。

I suspect because it scans the LISTFILE for every line in the INFILE.

是的,基本上。awk1脚本只循环一次INFILE

So number of lines scanned using the array with for (i in a) = NR(INFILE)*NR(LISTFILE).

关。但是不要忘记,通过使用数组,我们实际上删除了LISTFILE中的任何重复值。

This is the same number of lines you would scan by going through the INFILE repeatedly with the bash script.

因此,只有当LISTFILE不包含重复项时,此语句才为真。即使LISTFILE从来不包含任何陷阱,也最好避免多次读取单个文件。

2:

Running awk and awk2 in a different folder produced different results (where my 4 min result came from versus the ~2 min result here, not sure what the difference is because they are next door in the parent directory.

什么四分钟的结果?当对这类事情进行基准测试时,您应该停止将输出写入磁盘。如果您的机器在运行测试时有一些后台进程,那么您的结果只会以磁盘的写入速度出现偏差。使用/dev/null代替。

3:

Awk and Awk2 are essentially the same. Any idea why awk2 runs faster?

如果将管道移到sortuniq,您将更好地了解时差在哪里。您会发现,执行$4 > i - 5 && $4 < i + 5与执行$4 < i + 5 && $4 > i - 5完全不同。如果awkout.txtawkout.txt相同,则需要花费时间处理副本。

4:

这里发布的第二个命令避免了这个测试:$4 > i - 5 && $4 < i + 5。我不认为光是这一点就能保证运行时有90%的改进。有些东西闻起来不对劲。您介意重新运行您的测试,写到/dev/null,并张贴LISTFILEINFILE的内容吗?如果这两个文件是机密的,您能否提供一些示例文件,其中包含等于原件的内容量?

其他想法:

在我看来,类似于这样的东西也是可行的:

代码语言:javascript
运行
复制
awk 'FNR==NR { for (i=$2-4;i<$2+5;i++) a[i]; next } $4 in a { b[$10,$4] } END { print length b }' LISTFILE INFILE
票数 1
EN

Stack Overflow用户

发布于 2014-06-12 23:10:41

看起来,您只需要将LISTFILE的键添加到数组中,然后,在处理INFILE (逐行)时,使用'if‘语句测试数组中的每个键。您可以使用以下构造或类似的结构来完成此操作:

代码语言:javascript
运行
复制
for (i in a) { print i, a[i] }

以下是一些未经测试的代码,可以帮助您入门。请注意,我没有为我的键分配任何值:

代码语言:javascript
运行
复制
awk 'FNR==NR { a[$2]; next } { for (i in a) { if ($4 > i - 5 && $4 < i + 5) { print $10, $4 } } }' LISTFILE INFILE
票数 1
EN

Stack Overflow用户

发布于 2014-06-18 21:34:34

斯蒂夫上面的答案是这个问题的正确答案。下面是数组和非数组处理问题的方法的比较。

我创建了一个测试程序来查看两种不同的场景和每个场景的结果。测试程序代码如下:

代码语言:javascript
运行
复制
echo time for bash

time for line in `awk '{print $2}' $1` ; do awk -v a=$line '$4>a-5&&$4<a+5{print $4,$10}' $2 ; done | sort | uniq -c > bashout.txt

echo time for awk
time awk 'FNR==NR{a[$2]; next}{for (i in a) {if ($4>i-5&&$4<i+5) print $10,$4}}' $1 $2 |sort | uniq -c > awkout.txt

echo time for awk2

time awk 'FNR==NR{a[$2]; next}{for (i in a) {if ($4<i+5&&$4>i-5) print $10,$4}}' $1 $2 |sort | uniq -c > awk2out.txt

echo time for awk3
time awk '{a=$2;b=$1;for (i=a-4;i<a+5;i++) print b,i}' $1 > LIST2;time awk 'FNR==NR{a[$2];next}$4 in a{print $10,$4}' LIST2 $2 | sort | uniq -c > awk3out.txt

这是输出:

代码语言:javascript
运行
复制
time for bash
real    2m22.394s
user    2m15.938s
sys     0m6.409s

time for awk
real    2m1.719s
user    2m0.919s
sys     0m0.782s

time for awk2
real    1m49.146s
user    1m47.607s
sys     0m1.524s

time for awk3
real    0m0.006s
user    0m0.000s
sys     0m0.001s

real    0m12.788s
user    0m12.096s
sys     0m0.695s

4.意见/问题

  1. awk1脚本运行速度并不比awk脚本快得多。我怀疑是因为它扫描了INFILE中的每一行。使用for ( in )= NR(INFILE)*NR(LISTFILE)的数组扫描的行数。这与通过使用bash脚本反复遍历INFILE而扫描的行数相同。
  2. 在不同的文件夹中运行awk和awk2会产生不同的结果(在这里,我的4分钟结果来自于~2分钟的结果,因为它们位于父目录的隔壁),所以不确定它们有什么区别。
  3. Awk和Awk2本质上是一样的。知道为什么awk2运行得更快吗?
  4. 从LISTFILE中生成一个扩展的LIST2,并使用它作为数组使程序运行得更快,而代价是增加内存占用。考虑到我所看到的清单是多么的小(只有200-300长),这似乎是一条路,甚至比平行地做这件事还要多。
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24192269

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档