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

我有时间序列数据,我想找到所有相互匹配的行,但值可以不同(匹配到第一个选项卡)!你可以看到下面的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       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
    中,则将此行与
    文件1
    中的相应行一起打印出来。这两行由tab-
    |
    -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
    标记
第三个sed命令详细说明:

  • $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
    
  • 使用日期列(所有时间均为0:00)连接文件(这将使用对应键合并两个文件的所有列,但我们提供了一个输出模板,用于将第一个文件中的列写入其他位置,并将第二个文件中的列写入其他位置(但只有具有匹配键的行将以结果fl.txt和fr.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
    和文件2
    10-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