Unix:查找两个时间序列中都有时间戳的所有行?
我有时间序列数据,我想找到所有相互匹配的行,但值可以不同(匹配到第一个选项卡)!你可以看到下面的vimdiff,在这里我想去掉只发生在其他时间序列上的天数 我正在寻找最简单的unix工具来实现这一点 时间体验和 简单示例 输入 输出Unix:查找两个时间序列中都有时间戳的所有行?,unix,vim,awk,sed,vimdiff,Unix,Vim,Awk,Sed,Vimdiff,我有时间序列数据,我想找到所有相互匹配的行,但值可以不同(匹配到第一个选项卡)!你可以看到下面的vimdiff,在这里我想去掉只发生在其他时间序列上的天数 我正在寻找最简单的unix工具来实现这一点 时间体验和 简单示例 输入 输出 让我们考虑一下这些示例输入文件: $ cat file1 10-Apr-00 00:00 0 20-Apr-00 00:00 7 $ cat file2 10-Apr-00 00:00 7 21-Apr-00
让我们考虑一下这些示例输入文件:
$ cat file1
10-Apr-00 00:00 0
20-Apr-00 00:00 7
$ cat file2
10-Apr-00 00:00 7
21-Apr-00 00:00 3
要合并具有相同日期的行,请执行以下操作:
$ awk 'NR==FNR{a[$1]=$0;next;} {if ($1 in a) print a[$1]"\t||\t"$0;}' file1 file2
10-Apr-00 00:00 0 || 10-Apr-00 00:00 7
解释
NR==FNR{a[$1]=0;next;}
是到目前为止读取的行数,NR
是到目前为止从当前文件读取的行数。因此,当FNR
时,我们仍在读取第一个文件。如果是这样,将整行NR==FNR
保存在第一个字段$0
下的数组$1
中,该字段是日期。然后,跳过其余命令并跳到下一行a
如果我们到达这里,那么我们正在读取第二个文件,if($1 in a)打印一个[$1]“\t | | \t”$0
。如果此行的第一个字段file2
是我们在$1
中已经看到的日期,换句话说,如果文件1
中,则将此行与$1在a
中的相应行一起打印出来。这两行由tab-文件1
-tab分隔|
file2
中选择日期也在file1
中的行,则可以简化代码:
$ awk 'NR==FNR{a[$1]++;next;} {if ($1 in a) print;}' file1 file2
10-Apr-00 00:00 7
或者更简单一点:
$ awk 'NR==FNR{a[$1]++;next;} ($1 in a)' file1 file2
10-Apr-00 00:00 7
按照@Masi的要求,我尝试使用sed解决方案 我的第一次尝试使用了两次传球;第一步将
file1
转换为sed脚本,在第二步中用于过滤file2
sed 's/\([^ \t]*\).*/\/^\1\t\/p;t/' file1 > sed1
sed -nf sed1 file2 > out2
对于大的输入文件,这是s-l-o-w;对于file2
中的每一行,sed必须处理与file1
中的行数相等的模式量。我没有做任何分析,但是如果时间复杂性是二次的,我也不会感到惊讶
我第二次尝试合并和排序这两个文件,然后扫描所有行以搜索对。它以线性时间运行,因此速度要快得多。请注意,此解决方案将破坏文件的原始顺序;按字母顺序排序不能很好地使用此日期符号。提供具有不同日期格式(y-m-d)的文件是解决此问题的最简单方法
sed 's/^[^ \t]\+/&@1/' file1 > marked1
sed 's/^[^ \t]\+/&@2/' file2 > marked2
sort marked1 marked2 > sorted
sed '$d;N;/^\([^ \t]\+\)@1.*\n\1@2/{s/\(.*\)\n\(.*\)/\2\n\1/;P};D' sorted > filtered
sed 's/^\([^ \t]\+\)@2/\1/' filtered > out2
说明:
- 在第一个命令中,
将s/^[^\t]\+/&@1/
附加到每个日期。这使得可以合并文件,在排序时保持相同的日期,并且仍然能够区分不同文件中的行@1
- 第二个命令对
执行相同的操作;显然,它有自己的标记file2
@2
命令合并两个文件,将相等的日期分组在一起sort
- 第三个sed命令返回
中日期也出现在file2
中的所有行file1
- 第四个sed命令从输出中删除
标记@2
禁止不适当地打印最后一行$d
读取另一行输入并将其追加到模式空间中已存在的行N
匹配来自不同文件但日期相同的两行/^\([^\t]\+\)@1.*\n\1@2/
启动命令组{
交换模式空间中的两行s/\(.*)\n\(.*)/\2\n\1/
打印图案空间中的第一行P
结束命令组}
从图案空间中删除第一行D
坏消息是,即使是第二种方法也比@John1024提出的awk方法慢。Sed从未被设计成一个合并工具。awk和awk都不是,但awk的优点是能够将整个文件存储在字典中,这使得@John1024的解决方案非常快。字典的缺点是内存消耗。对于巨大的输入文件,我的解决方案应该具有优势。有一个相对未知的unix命令join。它可以连接键列上的已排序文件 为了在您的上下文中使用它,我们遵循以下策略(left.txt和right.txt是您的文件):
sort left_with_lns.txt -k 2 > sl.txt
sort right_with_lns.txt -k 2 > sr.txt
sort -n fl |cut -f 2- > left_filtered.txt
sort -n fr.txt | cut -f 2- > right_filtered.txt
使用的工具:剪切、连接、nl、排序。基本上很简单:将两个数据序列都转换,在第一行中找到唯一的条目,再转换回来,从两行中删除唯一的日期--done!最简单的工具是什么?我很困惑。你说你的输入是
10-Apr-00 00:00 0 10-Apr-00:00 7
但是你的屏幕截图显示了两个不同的Tfiles@AdamSmith文件1:10-Apr-00 00:00 0
和文件210-Apr-00 00:00 7
,现在清楚了吗?我在那里添加了一个分隔符| |以使它更清楚。@Shelleter排除,因为4月20日与4月21日的答案不匹配!还有其他工具可以更好地进行合并吗?我的兴趣特别是对于大型输入文件。没有他使用join命令,该命令使用
nl left.txt > left_with_lns.txt
nl right.txt > right_with_lns.txt
sort left_with_lns.txt -k 2 > sl.txt
sort right_with_lns.txt -k 2 > sr.txt
join -j 2 -t $'\t' -o 1.1 1.2 1.3 1.4 sl.txt sr.txt > fl.txt
join -j 2 -t $'\t' -o 2.1 2.2 2.3 2.4 sl.txt sr.txt > fr.txt
sort -n fl |cut -f 2- > left_filtered.txt
sort -n fr.txt | cut -f 2- > right_filtered.txt