更快速地查找按时间限制的重复项

时间:2008-08-08 23:46:31

标签: performance unix awk aix

在没有PERL的AIX的计算机中,如果记录具有相同的ID并且在四个小时之间注册,则需要过滤将被视为重复的记录。

我使用AWK实现了此过滤器并且运行良好,但我需要更快的解决方案:

# Generar lista de Duplicados
awk 'BEGIN {
FS="," 
}
/OK/ { 
    old[$8] = f[$8];
    f[$8] = mktime($4, $3, $2, $5, $6, $7); 
    x[$8]++;
}
/OK/ && x[$8]>1 && f[$8]-old[$8] 

Any suggestions? Are there ways to improve the environment (preloading the file or someting like that)?

The input file is already sorted.

With the corrections suggested by jj33 I made a new version with better treatment of dates, still maintaining a low profile for incorporating more operations:

awk 'BEGIN { FS=","; SECSPERMINUTE=60; SECSPERHOUR=3600; SECSPERDAY=86400; split("0 31 59 90 120 151 181 212 243 273 304 334", DAYSTOMONTH, " "); split("0 366 731 1096 1461 1827 2192 2557 2922 3288 3653 4018 4383 4749 5114 5479 5844 6210 6575 6940 7305", DAYSTOYEAR, " "); } /OK/ { old[$8] = f[$8]; f[$8] = mktime($4, $3, $2, $5, $6, $7); x[$8]++; } /OK/ && x[$8]>1 && f[$8]-old[$8] 2 ) && ( ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0) ) ) { d2m = d2m + 1; } d2y = DAYSTOYEAR[ y - 1999 ]; return ss + (mm*SECSPERMINUTE) + (hh*SECSPEROUR) + (d*SECSPERDAY) + (d2m*SECSPERDAY) + (d2y*SECSPERDAY); } '

6 个答案:

答案 0 :(得分:3)

这听起来像是实际数据库的工作。即使像SQLite这样的东西也可能在这里帮助你。我看到的最大问题是你在“4小时内”的定义。这是一个滑动窗口问题,这意味着你不能简单地将所有数据量化为4小时段...你必须分别为每个其他元素计算所有“附近”元素。啊。

答案 1 :(得分:1)

如果您的数据文件包含所有记录(即它包含文件中没有重复ID的记录),您可以对其进行预处理并生成仅包含具有重复(ID)的记录的文件。

如果是这种情况会减少您需要使用AWK程序处理的文件大小。

答案 2 :(得分:1)

输入文件是如何排序的?比如,cat file | sort,或通过单个特定字段或多个字段排序?如果有多个字段,哪些字段和什么顺序?看来小时字段是24小时制,而不是12小时,对吗?所有日期/时间字段都是零填充的(将是上午9点是“9”还是“09”?)

如果不考虑性能,您的代码看起来就像月份边界有问题,因为它假设所有月份都是30天。以2008-05-31 / 12:00:00和2008-06-01:12:00:00的两个日期为准​​。这些是相隔24小时,但您的代码为两者生成相同的时间代码(63339969600)

答案 3 :(得分:1)

我认为你需要考虑闰年。我没有做数学计算,但我认为在闰年期间,对于2月29日的硬编码,2月29日中午和3/1中午的比较会产生与以前相同的重复时间戳。 。虽然看起来你没有像那样实现它。他们实现它的方式,我认为你仍然有问题,但它是在$ leapyear的12/31和$ leapyear + 1的1/1之间。

我认为如果您的代码必须处理处理它们的时区,您可能会在时间更改期间发生一些冲突。

该文件似乎并没有以任何有用的方式排序。我猜这个字段$ 1是某种状态(你正在检查的“OK”)。所以它按记录状态排序,然后是DAY,然后是MONTH,YEAR,HOURS,MINUTES,SECONDS。如果它是年,月,日,我认为可能会有一些优化。仍然可能,但我的大脑现在朝着不同的方向前进。

如果有少量重复键与总行数成比例,我认为最好的办法是将awk脚本工作的文件减少到只重复键(如David said)。您还可以预处理文件,以便只显示/ OK /行。我想我会用一个管道执行此操作,其中第一个awk脚本仅打印具有重复ID的行,第二个awk脚本基本上是上面的那个,但优化为不查找/ OK /并且知道任何键存在是重复密钥。

如果你提前知道所有或大多数行都会有重复键,那么可能不值得搞乱。我会咬紧牙关并用C语言编写代码。更多代码行,比awk脚本快得多。

答案 4 :(得分:1)

在许多unixen上,您可以按特定列或字段排序。因此,通过按ID排序文件,然后按日期排序,您不再需要保留上次查看每个ID时的关联数组。所有上下文都按文件的顺序存在。

在我的Mac上,它有GNU排序,它是:

sort -k 8 < input.txt > output.txt

对ID字段进行排序。您也可以通过说(例如)8,3而不是2个字段对第二个字段进行排序。因此,unix样式的time_t时间戳在文件中可能不是一个坏主意 - 它很容易排序,并为您节省了所有这些日期计算。另外,(至少在GNU awk中),有一个mktime function可以从组件中为你创建time_t。

答案 5 :(得分:1)

@ AnotherHowie,我认为可以使用sort和uniq完成整个预处理。问题是OP的数据似乎是逗号分隔的,并且(Solaris 8)uniq不允许您以任何方式指定记录分隔符,因此没有超级干净的方法来使用标准的unix工具进行预处理。我不认为它会更快,所以我不会查找确切的选项,但你可以做类似的事情:

cut -d, -f8 <infile.txt | sort | uniq -d | xargs -i grep {} infile.txt >outfile.txt

这不是很好,因为它为包含重复键的每一行执行grep。您可以按下uniq输出到单个正则表达式以提供给grep,但只有当OP将包含可疑重复键的行的预期比率发布到文件中的总行时,才能知道好处。