【问题标题】:Remove duplicates and take the latest data based on timestamp in a csv file using linux使用 linux 删除重复项并根据 csv 文件中的时间戳获取最新数据
【发布时间】:2015-01-14 12:42:41
【问题描述】:

我有一个巨大的 csv 文件(100,000 条记录),其中包含如下数据:

Col1  Col2       Date & Time 
a     xyz      Oct 31 2014 09:01 
b     xyz      Dec 12 2013 08:15 
a     xyz      Oct 30 2014 07:01 
c     xyz      Dec 26 2013 08:39 
a     xyz      Nov 12 2014 08:25 
c     xyz      Dec 12 2013 08:10 
b     xyz      Dec 12 2013 09:21 

我需要删除重复项并仅保留最新的数据(基于第三列 - 日期和时间)。所以输出应该是这样的

 Col1    Col2        Date & Time 
    a     xyz      Nov 12 2014 08:25 
    b     xyz      Dec 12 2013 09:21
    c     xyz      Dec 26 2013 08:39 

我尝试先对文件进行排序,然后删除重复项,但对于这个巨大的 csv 文件来说,这是失败的。有人可以帮忙吗?

附:在 col1 中,数据可以来自 a-z 多次。这里只是一个示例。

【问题讨论】:

  • 不清楚:列之间是否有任何特定的分隔符?一个标签也许?即:什么指示一列何时完成并开始下一列?
  • @fedorqui - 正如我所提到的,它是一个 CSV(逗号分隔)文件。因此,列将仅由“,”分隔...
  • 然后在我的回答中查看更新。
  • 您是要对第 1 列、第 2 列还是第 1 列和第 2 列进行重复数据删除?
  • @Sobrique 显然只是 col1。

标签: linux sorting unix csv awk


【解决方案1】:

让我们试试这个:

while IFS="," read -r a b c
do
  printf "%s %s %s %d\n" "$a" "$b" "$c" $(date -d"$c" +"%s")
done < file | \
awk '{it=$NF; NF--
      if (max[$1]<it) {max[$1]=it; res[$1]=$0}}
        END {for (i in max) print res[i]}'

这将最大日期存储在数组max[] 中,该数组由指示自 1970 年 1 月 1 日以来的秒数的临时 last 字段索引(之前使用 while read bash 创建)。处理完整个块后,在END{} 中打印结果。

返回:

a xyz Nov 12 2014 08:25
b xyz Dec 12 2013 09:21
c xyz Dec 26 2013 08:39

如果恰好是逗号分隔,请使用:

$ while IFS="," read -r a b c; do printf "%s,%s,%s,%d\n" "$a" "$b" "$c" $(date -d"$c" +"%s"); done < a | awk 'BEGIN{FS=OFS=","} {it=$NF; NF--
          if (max[$1]<it) {max[$1]=it; res[$1]=$0}}
            END {for (i in max) print res[i]}'
a,xyz,Nov 12 2014 08:25
b,xyz,Dec 12 2013 09:21
c,xyz,Dec 26 2013 08:39

【讨论】:

  • 这对于大文件来说会很慢。
  • 除了 a、b 和 c,我还有。这个我只是随机放在这里。在第 1 列中,有 a 到 z 数据。所以,我想这会失败!
  • @user3884944 那就不要让人们浪费时间并发布更具代表性的数据。
  • 我怎样才能把100,000条记录放在这里??我这里只是给出了一个示例,csv 文件可以包含 f 2 次,e 3 次等等......请原谅,如果我不够清楚!
  • @user3884944 不,你肯定根本不清楚。提供更通用的方法来了解您的字段的外观。不在 cmets 中,而是更新您的问题。
【解决方案2】:

您的流程分为 3 个步骤。 第一:

  • 提取关键字段。 (我会使用 perl 并拆分)。

  • 将日期解析为数字格式。你可以做某种 ISO 风格,例如2014-12-26 08:39 或将其转换为 Unix 'epoch' 时间。 (如果是 CSV,如果你真的想要的话,你可以通过 Excel 来处理它。)

  • 遍历您的输入,丢弃任何“旧”值。

因此请记住这一点 - 并假设因为您说的是“CSV”,您的意思是它实际上是逗号分隔的值。

#!/usr/bin/perl

use strict;
use warnings;

use Time::Piece;

my %most_recent;

my $header = <DATA>;

while ( my $line = <DATA> ) {
    chomp $line;
    my ( $col1, $col2, $date_and_time ) = split( /,\s*/, $line, 3 );
    $date_and_time =~ s/\s+$//g;

    my $time = Time::Piece ->  new -> strptime( $date_and_time, "%b %d %Y %H:%M" );

    if ( not defined $most_recent{$col1}{$col2}
        or $most_recent{$col1}{$col2} < $time )
    {
        $most_recent{$col1}{$col2} = $time;
    }
}

print "Most recent:\n";
foreach my $col1 ( keys %most_recent ) {
    foreach my $col2 ( keys %{ $most_recent{$col1} } ) {
        print "$col1, $col2, $most_recent{$col1}{$col2}, \n";
    }
}


__DATA__
Col1, Col2,       Date & Time 
a, xyz,      Oct 31 2014 09:01 
b, xyz,      Dec 12 2013 08:15 
a, xyz,      Oct 30 2014 07:01 
c, xyz,      Dec 26 2013 08:39 
a,     xyz,      Nov 12 2014 08:25 
c,     xyz,      Dec 12 2013 08:10 
b,     xyz,      Dec 12 2013 09:21 

这将 - 对于 Col1Col2 的每个唯一配对运行,为该配对选择最近的时间戳。

注意 - 在各个步骤(拆分和时间戳解析)中,空格会被丢弃。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-14
    • 1970-01-01
    相关资源
    最近更新 更多