【问题标题】:Delete the row if it contains more than specific number of non numeric values如果该行包含超过特定数量的非数值,则删除该行
【发布时间】:2017-12-08 18:22:12
【问题描述】:

我有一个大的 (2GB) 逗号分隔文本文件,其中包含来自传感器的一些数据。有时传感器关闭并且没有数据。如果每行中有超过指定数量的 No DataOffany non-numeric 值,我想删除这些行;不包括标题。我只对从第 3 列开始计数感兴趣。例如:我的数据如下:

Tag, Description,2015/01/01,2015/01/01 00:01:00,2015/01/01 00:02:00, 2015/01/01 00:02:00
1827XYZR/KB.SAT,Data from Process Value,2.1,Off,2.7
1871XYZR/KB.RAT,Data from process value,Off,No Data, No Data
1962XYMK/KB.GAT,Data from Process Value,No Data,5,3
1867XYST/KB.FAT,Data from process value,1.05,5.87,7.80
1871XKZR/KB.VAT,Data from process value,No Data,Off,2

这里的第一行是标题,我想保持原样。但我想从第 3 列开始删除任何列/字段中具有 2 个或多个 No DataOff 或任何 non numeric 字段的那些行。换句话说,具有 4 个或 5 个文本字段的行。在示例中,第 3 行和第 6 行以上有 2 个或多于 2 个 No DataOff 字段,我想删除它们。因此,我的首选输出是

Tag, Description,2015/01/01,2015/01/01 00:01:00,2015/01/01 00:02:00, 2015/01/01 00:02:00
1827XYZR/KB.SAT,Data from Process Value,2.1,Off,2.7
1962XYMK/KB.GAT,Data from Process Value,No Data,5,3
1867XYST/KB.FAT,Data from process value,1.05,5.87,7.80  

我可以针对特定情况使用循环执行此操作:

awk -F, '{ non_numeric=0;
  for(i=1;i<=NF;i++){
    if($i ~ // ) non_numeric++
  }
  if(non_numeric<2) print $0
}' testfile.txt

在这里,我只考虑No DataOff。如何计算所有非数字字符串。如果我将 if 语句更改为

if($i ~ /[^0-9]/ ) non_numeric++

它不起作用并且没有输出。另外,由于我使用的是循环,我认为它会很慢。我们能以某种方式加快速度吗?任何命令行解决方案都可以。

【问题讨论】:

  • 使用for() 循环计算数字字段的数量。循环完成后,如果它至少为 4,则打印该行。
  • 基本上,我只是重申了您的要求。将其转换为awk 代码应该很简单。请展示您尝试过的内容,以便我们帮助您修复它。我们不会为你做你的工作。
  • 示例数据中的第三行包含以下内容:No Data. No Data。这是一个错字吗? . 应该是 , 吗?
  • @Barmar 和 redneb 我添加了自己的工作并修复了数据中的错字。很抱歉没有保持离散。
  • 应该是两个或两个以上。我也更正了这个问题,很抱歉造成混乱。

标签: bash awk sed preprocessor gawk


【解决方案1】:
awk -F, '
    {   nonnum = 0;
        for (i = 3; i <= NF; i++) { 
            if ($i ~ /[^.0-9]/) {
                nonnum++;
                if(nonnum >= 2) { next; }
            }
        }
    } 1' infile > outfile

如果循环从未执行 next 以跳过当前行的剩余模式,则末尾的 1 将打印该行。

【讨论】:

【解决方案2】:

您可以使用grep

grep -vP '((?<=,|^)(No Data|Off)(?=,|$).*){2,}' input

Tag, Description,2015/01/01,2015/01/01 00:01:00,2015/01/01 00:02:00, 2015/01/01 00:02:00
1827XYZR/KB.SAT,Data from Process Value,2.1,Off,2.7
1962XYMK/KB.GAT,Data from Process Value,No Data,5,3
1867XYST/KB.FAT,Data from process value,1.05,5.87,7.80

解释:(No Data|Off)No DataOff 匹配。我们用(?&lt;=,|^)(?=,|$) 包围它;这些是与, 或字符串的开头(或结尾)匹配的零宽度后视和前瞻。这确保我们只匹配整个字段。由于我们想多次匹配一个字段,我们将所有内容都放在一个量化的(...){2,} 中,我们还添加了一个.* 来说明字段之间的内容。

【讨论】:

    【解决方案3】:

    使用 GNU awk,您可以使用这个好东西:

    awk 'NF<2' FPAT='No Data' file
    

    FPAT 指定了一种模式,用于描述什么是文本行中的字段。它是一个 GNU 扩展。将其设置为静态字符串No Data 允许我们使用NF&lt;2 简单地检查字段计数。

    【讨论】:

      【解决方案4】:

      使用静态字符串:

      $ awk '(a=$0) && gsub(/No Data|Off/,"",a)<2' file
      

      即。将当前记录$0 复制到临时变量a,如果计数小于2,则使用gsubprint 计算OffNo Data 的出现次数。输出:

      Tag, Description,2015/01/01,2015/01/01 00:01:00,2015/01/01 00:02:00, 2015/01/01 00:02:00
      1827XYZR/KB.SAT,Data from Process Value,2.1,Off,2.7
      1962XYMK/KB.GAT,Data from Process Value,No Data,5,3
      1867XYST/KB.FAT,Data from process value,1.05,5.87,7.80
      

      如果要匹配所有非数字字符串,请使用:

      awk 'NR==1 || (a=$0) && gsub(/,[^\.,0-9]+/,"",a)<3' file
      

      它输出第一条记录(NR==1)和少于三个非数字值的记录(第三个是,Data from process value)。

      【讨论】:

      • 谢谢,但是有没有办法,一个正则表达式也可以考虑其他字符串,我想在 gsub 中使用 /[^0-9.]/ 计算所有非数字的东西不起作用
      【解决方案5】:
      $ perl -F, -ane 'print if $. == 1 || (grep {!/\d/} @F[2..$#F]) < 2' ip.txt 
      Tag, Description,2015/01/01,2015/01/01 00:01:00,2015/01/01 00:02:00, 2015/01/01 00:02:00
      1827XYZR/KB.SAT,Data from Process Value,2.1,Off,2.7
      1962XYMK/KB.GAT,Data from Process Value,No Data,5,3
      1867XYST/KB.FAT,Data from process value,1.05,5.87,7.80
      
      • -F,, 上拆分输入行
      • $. == 1 如果行号是1,即打印标题
      • (grep {!/\d/} @F[2..$#F]) &lt; 2 如果第 3 列到末尾的非数字字段的数量少于两个,则打印。条件只是检查 digit 是否不存在

      可以根据需要轻松更改要检查的列和检查次数。例如:@F[3..$#F] 从第 4 列开始检查,&lt; 3 检查非数字字段的数量少于三个

      【讨论】:

        【解决方案6】:

        懒惰的方式:打印 iff 字段 3-5 至少包含一个数字字符:

        awk -F, '$3$4$5 ~ "[0-9]"' data.csv
        

        懒惰的方式(适用于您的示例数据):打印 iff 行包含一个逗号后跟一个数字字符:

        grep ',[0-9]' data.csv
        

        【讨论】:

          【解决方案7】:

          这可能对你有用(GNU sed):

          sed -r '/(.*No Data|.*Off){2}/d' file
          

          使用交替删除具有 2 个或更多指定字符串的行。

          【讨论】:

          • 有没有办法,一个正则表达式来考虑其他字符串,我想计算所有非数字的东西
          猜你喜欢
          • 2018-12-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-10-01
          相关资源
          最近更新 更多