【问题标题】:Uniqing a delimited file based on a subset of fields根据字段子集对分隔文件进行唯一化
【发布时间】:2017-05-09 12:11:41
【问题描述】:

我有如下数据:

1493992429103289,207.55,207.5
1493992429103559,207.55,207.5
1493992429104353,207.55,207.5
1493992429104491,207.6,207.55
1493992429110551,207.55,207.5

由于最后两列的性质,它们的值在一天中会发生变化,并且它们的值会定期重复。通过对我想要的输出(如下)中概述的方式进行分组,我可以查看每次它们的值发生变化时(第一列中的以诺时间)。有没有办法实现如下所示的所需输出:

1493992429103289,207.55,207.5
1493992429104491,207.6,207.55
1493992429110551,207.55,207.5

所以我通过后两列合并数据。但是,合并并不是完全唯一的(从 207.55 可以看出,207.5 正在重复)

我试过了:

uniq -f 1

但是输出只给出了第一行并且没有通过列表继续

下面的 awk 解决方案不允许再次输出之前发生的事件,因此给出了输出(在 awk 代码下方):

awk '!x[$2 $3]++'

1493992429103289,207.55,207.5
1493992429104491,207.6,207.55

我不希望按后两列对数据进行排序。但是,由于第一个是纪元时间,所以可能按第一列排序。

【问题讨论】:

  • 如果最后一列可以重复?您认为它还能如何分组?
  • 您的预期输出有何意义?没有任何逻辑可以适合您得出预期输出的方式
  • 由于最后两列的性质,它们的值全天都在变化,并且它们的值会定期重复。通过对所需输出中概述的方式进行分组,我可以查看每次它们的值发生变化时(第一列中的以诺时间)
  • 将此作为问题的一部分进行更新

标签: linux bash shell awk uniq


【解决方案1】:

您不能用uniq 设置分隔符,它必须是空格。在tr 的帮助下,您可以

tr ',' ' ' <file | uniq -f1 | tr ' ' ','

1493992429103289,207.55,207.5
1493992429104491,207.6,207.55
1493992429110551,207.55,207.5 

【讨论】:

    【解决方案2】:

    您可以使用如下Awk 语句,

    awk 'BEGIN{FS=OFS=","} s != $2 && t != $3 {print} {s=$2;t=$3}' file
    

    根据需要生成输出。

    1493992429103289,207.55,207.5
    1493992429104491,207.6,207.55
    1493992429110551,207.55,207.5
    

    想法是将第二列和第三列的值分别存储在变量st中,并且仅当当前行是唯一时才打印的内容.

    【讨论】:

    • 您好 Inian,感谢您的解决方案。不幸的是,它并没有解决我的问题。它将重复最后两列中的值,例如:1493992429103289,207.55,207.5 1493992429104353,207.55,207.5 1493992429104491,207.6,207.55 N.B.,我只是使用上面的值来向您展示我的意思(在我的大文件中)相邻行中的第二列值,它可能使用我在问题中提供的数据快照起作用)
    • @vinayman:我们只能根据您提供的样本信息提供答案。如果您的输入有错综复杂的地方,您应该事先告知我们。现在改变它没有意义。
    【解决方案3】:

    我找到了一个没有 Inian 优雅但满足我的目的的答案。 由于我的第一列总是以微秒为单位的 enoch 时间,并且不会增加或减少字符,因此我可以使用以下 uniq 命令:

    uniq -s 17
    

    【讨论】:

      【解决方案4】:

      您可以尝试手动(通过循环)将当前行与上一行进行比较。

      previous_line=""
      # start at first line
      i=1
      
      # suppress first column, that don't need to compare
      sed 's@^[0-9][0-9]*,@@' ./data_file > ./transform_data_file
      
      # for all line within file without first column
      for current_line in $(cat ./transform_data_file)
      do 
        # if previous record line are same than current line
        if [ "x$prev_line" == "x$current_line" ]
        then
          # record line number to supress after
          echo $i >> ./line_to_be_suppress
        fi
      
        # record current line as previous line
        prev_line=$current_line
      
        # increment current number line
        i=$(( i + 1 ))
      done
      
      # suppress lines
      for line_to_suppress in $(tac ./line_to_be_suppress) ; do sed -i $line_to_suppress'd' ./data_file ; done
      
      rm line_to_be_suppress
      rm transform_data_file
      

      【讨论】:

        【解决方案5】:

        由于您的第一个字段似乎有 18 个字符的固定长度(包括, 分隔符),您可以使用uniq-s 选项,这对于较大的文件会更优化:

        uniq -s 18 file
        

        给出这个输出:

        1493992429103289,207.55,207.5
        1493992429104491,207.6,207.55
        1493992429110551,207.55,207.5
        

        来自man uniq

        -f 数

        在进行比较时忽略每个输入行中的前 num 个字段。 字段是由空格与相邻字段分隔的非空白字符串。 字段编号是基于一的,即第一个字段是字段一。

        -s 字符

        在进行比较时忽略每个输入行中的第一个 chars 字符。 如果与 -f 选项一起指定,则后面的第一个字符 第一个 num 字段将被忽略。字符编号是从一开始的, 即,第一个字符是第一个字符。

        【讨论】:

          猜你喜欢
          • 2015-04-13
          • 1970-01-01
          • 1970-01-01
          • 2015-04-02
          • 1970-01-01
          • 2013-08-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多