如果包含超过特定数量的非数值,则删除该行

Delete the row if it contains more than specific number of non numeric values

我有一个大的 (2GB) 逗号分隔的文本文件,其中包含一些来自传感器的数据。有时传感器关闭并且没有数据。如果每行中的 No DataOffany non-numeric 值超过指定数量,我想删除这些行;不包括 header。我只对从第 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

这里第一行是 header,我想保持原样。但是我想从第 3 列开始删除那些具有 2 个或超过 2 个 No DataOff 或任何 columns/fields 中的任何 non numeric 字段的行。换句话说,具有 4 个或五个中的文本字段的行。在示例中,上面第 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 [=12=]
}' testfile.txt

这里,我只考虑No DataOff。我如何计算所有 non-numeric 个字符串。如果我将 if 语句更改为

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

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

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

awk -F, ' ~ "[0-9]"' data.csv

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

grep ',[0-9]' data.csv
awk -F, '
    {   nonnum = 0;
        for (i = 3; i <= NF; i++) { 
            if ($i ~ /[^.0-9]/) {
                nonnum++;
                if(nonnum >= 2) { next; }
            }
        }
    } 1' infile > outfile

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

使用静态字符串:

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

即。将当前记录 [=13=] 复制到临时变量 a,使用 gsubprint 计算 OffNo Data 的出现次数 if count小于2。输出:

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=[=12=]) && gsub(/,[^\.,0-9]+/,"",a)<3' file

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

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

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

使用交替删除具有 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。我们用 (?<=,|^)(?=,|$) 包围它;这些是与字符串的 , 或开头(或结尾)匹配的零宽度后视和前视。这确保我们只匹配整个字段。由于我们想多次匹配一个字段,我们将所有内容都放在量化的 (...){2,} 中,我们还添加了一个 .* 来说明字段之间的内容。

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

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

FPAT 指定一种模式,描述文本行中的字段是什么。它是一个 GNU 扩展。将其设置为静态字符串 No Data 允许我们使用 NF<2.

简单地检查字段计数
$ 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,即打印 header
  • (grep {!/\d/} @F[2..$#F]) < 2 如果第 3 列中的 non-numeric 字段数量少于两个则打印。该条件只是检查数字是否不存在

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