在 bash 中比较两个大文件并寻找性能 [已关闭]

Comparing two large files in bash and looking for performance [closed]

提问人:Gery 提问时间:10/15/2023 最后编辑:Gery 更新时间:10/16/2023 访问量:152

问:


想改进这个问题吗?通过编辑这篇文章添加详细信息并澄清问题。

上个月关闭。

关于这个问题,我已经挠头了一段时间。我有两个文件:

base.csv

6517.49,450.0N,450.0N,-79.5664,-7.9726,280.434,-7.9726
6542.77,300.0N,300.0N,-79.5376,-8.0679,280.462,-8.0679
6530.41,300.0N,300.0N,-79.6216,-7.9845,280.378,-7.9845

tocompare.csv

7233.28,-81.59,-5.13513513514,278.41,-5.13513513514
7224.23,-81.59,-5.1951951952,278.41,-5.1951951952

并且两个文件的第一列值之间的百分比差异低于 10.5%(例如,第 1 行 -> in 与 in ),我需要输出两个文件的列。为了获得我使用的百分比差异(我使用以下值作为示例,因为它们只满足<两个值之间差异 10.5%)的条件:7233.28tocompare.csv6517.49base.csvawk

$ echo 6542.77 7224.23 | awk 'function abs(x){ return ((x < 0.0) ? -x : x) } { print (abs($1-$2)/$1)*100"%" }'
10.4155%

因此,在第一次比较运行中,我希望将 () 与 中第一列的所有值进行比较。因此,与第一列相比是 3 倍。然后,该过程再次开始,依此类推。7233.28tocompare.csvbase.csv7233.28base.csv7224.23

下面是第 1 次和第 2 次运行的结果,其中第 1 次运行不打印任何内容,并且在第 2 次运行后只出现一个结果(即。 与 ):6542.777224.23

2nd column base.csv   2nd column tocompare.csv   3rd column tocompare.csv
300.0N                -81.59                     -5.1951951952

我怀疑的是性能,因为原始文件非常大。例如,基于这两个 CSV 文件,将有 6 行比较(有 3 行,有 2 行)。原始文件分别有 300 行和 20 000 行(因此,有 600 万次比较)。但是,由于条件不满足,我希望不会打印任何内容,这支持计算的时间和性能。base.csvtocompare.csv

那么,nice是完成这项任务的正确工具吗?如果是这样,我该如何比较这两个文件以获得预期的输出文件?awk

任何提示将不胜感激,

Linux Bash 性能 CSV AWK

评论

1赞 Peter - Reinstate Monica 10/15/2023
编译后的程序有可能表现得更好,特别是如果您可以将整个文件读入大数组(这对于这种小文件来说应该不是问题)并在不再次接触文件的情况下执行计算。
4赞 dawg 10/15/2023
@Gery:请更新输入整行的预期输出。如果太长,请缩短输入。只有 1 行示例太模棱两可。
3赞 Charles Duffy 10/16/2023
如果您的主要目标是性能,那么您实际上应该为此使用索引数据存储(类似于 SQLite),而不是对 CSV 文件进行操作。(即使您的数据都适合内存,SQLite 仍然可以更快,因为索引可用于仅隔离可能参与联接的数据,甚至根本不需要考虑其余部分)。
2赞 Charles Duffy 10/16/2023
(顺便说一句,虽然共识接近的原因是“需要细节或清晰度”,但我的投票是“太宽泛”;这个问题没有单一的狭隘、具体的技术问题,减少了其他有相同问题的人能够找到并从中学习答案的机会:绝大多数人使用 bash 脚本比较两个太慢的文件,因为不同/不相关的原因,其中一个文件太慢, 没有很好的方法来过滤搜索结果以找到这个问题及其答案)。
2赞 Charles Duffy 10/16/2023
(...特别是,bash 比较速度太慢的最常见情况是人们使用但应该使用 ,或者接受未排序的输入但需要使用排序的输入以允许使用类似 ;你有一个奇怪的案例,但并没有孤立出让它变得奇怪的事情,因此而是在没有写标题和描述的情况下寻求更广泛的指导,使问题及其答案对于陷入相同极端情况的人来说很容易找到)。diffcommjoin

答:

1赞 dawg 10/16/2023 #1

鉴于:

head *.csv
==> base.csv <==
6517.49,450.0N,450.0N,-79.5664,-7.9726,280.434,-7.9726
6542.77,300.0N,300.0N,-79.5376,-8.0679,280.462,-8.0679
6530.41,300.0N,300.0N,-79.6216,-7.9845,280.378,-7.9845

==> tocompare.csv <==
7233.28,-81.59,-5.13513513514,278.41,-5.13513513514
7224.23,-81.59,-5.1951951952,278.41,-5.1951951952

我能为你得出的最接近下一步的是这个 Ruby:

ruby -r csv -e 'BEGIN{opts={:converters => :numeric} }
base,tc=[0,1].map{|fn| CSV.parse(File.open(ARGV[fn]).read, **opts)}
idx={}
tc.each.with_index{|tc_row, i| 
    base.each.with_index{|base_row,j| idx[i]=j if (base_row[0]-tc_row[0]).abs/base_row[0]<0.105} 
}
idx.each{|k,v| puts "#{base[k][1]}\t#{tc[v][1]}\t#{tc[v][2]}" }

' base.csv tocompare.csv

指纹:

300.0N  -81.59  -5.1951951952 
3赞 markp-fuso 10/16/2023 #2

假设/理解:

  • 我们想找到两个文件的笛卡尔乘积的“百分比差”(即,所有第一列来自 + 所有第一列来自base.csvtocompare.csv)
  • 仅当“百分比差异”< 10.5 时生成 3 列输出

一个想法:awk

$ cat abs.awk

function abs(x) { return ((x < 0.0) ? -x : x) }

BEGIN   { FS = "," }

FNR==NR { col1[++cnt] = $1                                       # 1st file: save column 1
          col2[  cnt] = $2                                       # save column 2
          next
        }
        { for (i=1; i<=cnt; i++)                                 # 2nd file: loop through list of 1st columns from 1st file
              if (abs(col1[i]-$1)/col1[i] * 100 < 10.5)          # if calculation is less than 10.5 then ...
                 print col2[i], $2, $3                           # print 3-column output
        }

这将产生:

$ awk -f abs.awk base.csv tocompare.csv
300.0N -81.59 -5.1951951952

看看性能...

OP 提到实际文件是 300 行 () 和 20,000 行 ()。我复制了两个文件中的行,以达到所需的行数:base.csvtocompare.csv

$ wc -l base.300.csv tocompare.20K.csv
    300 base.300.csv
  20000 tocompare.20K.csv

针对较大文件对脚本进行计时:awk

$ time awk -f abs.awk base.300.csv tocompare.20K.csv > x

real    0m1.127s
user    0m1.094s
sys     0m0.032s

笔记:

  • i7-1260Prunning 和Ubuntu 22.04GNU awk 5.1.0
  • ~1.2 秒执行 600 万次操作abs() / comparison
  • 在之前对问题的编辑中,OP 提到了“摆脱值”和“跳过计算”,但我们没有得到关于这意味着什么的细节;如果 OP 有额外的逻辑可以让我们减少操作次数,那么就有可能将运行时间减少到当前 ~1.2 秒以下abs() / comparison

验证我们得到相同的结果:

$ sort -u x
300.0N -81.59 -5.1951951952

评论

0赞 Gery 10/16/2023
哇!感谢您@markp-fuso 和 @dawg 的简洁回答,我将坚持在这里的解决方案,我正在测试一些以前的 awk 调用,使用数组和其他一些变体,例如在我的旧帖子中(例如。stackoverflow.com/questions/63975887/...)但是您的解决方案要好得多@markp扶桑,并且对我来说有新技巧(例如 CNT),谢谢!awk