Perl 比较单个文件中的多个列,并计算这些列中某个值的出现次数

Perl 比较单个文件中的多个列,并计算这些列中某个值的出现次数,perl,awk,sed,grep,Perl,Awk,Sed,Grep,我有一个21列的文件 Name Grade1 Grade2 Grade3 Grade4 Grade5 .... Grade20 Zoe 60 70 NA NA NA 67 现在,我只想保留超过5个等级为NA的名字某些名称包含NA。 我知道awk可以做这项工作。但我不知道如何在不单独比较的情况下比较所有列 我试过: more input_file.txt | awk 'total

我有一个21列的文件

Name    Grade1    Grade2   Grade3   Grade4   Grade5  .... Grade20
Zoe       60        70      NA       NA        NA            67
现在,我只想保留超过5个等级为NA的名字某些名称包含NA。

我知道awk可以做这项工作。但我不知道如何在不单独比较的情况下比较所有列

我试过:

    more input_file.txt | awk 'total = count20[$2,$3,$4,$5,$6,$7,$8,$9,$10,
    $11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21]++, if (($2 == "NA" || $3 == "NA" || 
    $4 == "NA" || $5 == "NA" || $6 == "NA" || $7 == "NA" || $8 == "NA" || $9 == "NA" 
    || $10 == "NA" || $11 == "NA" || $12 == "NA" || $13 == "NA" || $14 == "NA" || 
    $15 == "NA" || $16 == "NA" || $17 == "NA" || $18 == "NA" || $19 == "NA" || 
    $20 == "NA" || $21 == "NA") && total > 4) { print $1"\t"$2"\t"$3"\t"$4"\t"$5"\t"
    $6"\t"$7"\t"$8"\t"$9"\t"$10"\t"$11"\t"$12"\t"$13"\t"$14"\t"$15"\t"$16"\t"$17"\t"
    $18"\t"$19"\t"$20"\t"$21 }' > test.txt
它不起作用,我不知道为什么或者如何更有效地完成它


编辑:更准确地说,想要的输出是一个文件,其中包含名称和学生的所有列,超过5列包含NA。

awk

$ awk -F'NA' 'NF>5'

假设您的名称列不包含NA作为子字符串。使用NA作为字段分隔符并对字段进行计数,如果有5个以上的字段意味着至少有5个NAs,则默认操作是打印行,因此无需指定。这将为您提供具有5个或更多NA的记录,如果您希望严格超过5个,请将阈值更改为6。

awk
拯救

$ awk -F'NA' 'NF>5'

假设您的名称列不包含NA作为子字符串。使用NA作为字段分隔符并对字段进行计数,如果有5个以上的字段意味着至少有5个NAs,则默认操作是打印行,因此无需指定。这将为您提供具有5个或更多NA的记录,如果您希望严格大于5,请将阈值更改为6。

此命令将至少六次打印包含
NA的所有行:

grep -E '(NA.*){6}' infile
如果学生的名字也包含
NA
,则可能会出错。要解决这个问题,您可以使用

grep -E '^[^[:blank:]]+[[:blank:]]+(NA.*){6,}' infile

仅在第一列之后计算
NA

此命令至少六次打印包含
NA
的所有行:

grep -E '(NA.*){6}' infile
如果学生的名字也包含
NA
,则可能会出错。要解决这个问题,您可以使用

grep -E '^[^[:blank:]]+[[:blank:]]+(NA.*){6,}' infile

它只在第一列之后计算NA。

这里有一个使用awk的基本方法:

awk '{ count = 0; for (i = 2; i <= NF; ++i) if ($i == "NA") ++count } count > 5' file
awk'{count=0;for(i=2;i=5)文件

这将循环遍历每个字段并检查它是否等于“NA”。如果是,它将添加到该记录的总计数中。当计数大于5时,将打印记录。

以下是使用awk执行此操作的基本方法:

awk '{ count = 0; for (i = 2; i <= NF; ++i) if ($i == "NA") ++count } count > 5' file
awk'{count=0;for(i=2;i=5)文件

这将循环遍历每个字段并检查它是否等于“NA”。如果是,它将添加到该记录的总计数中。当计数大于5时,将打印记录。

它被标记为
perl
,因此perl回答:

perl -ne 'print if (grep /^NA$/, split ) > 5'
其中:

  • 逐行迭代
  • 空格分开了吗
  • greps仅为
    NA
  • 如果计数为
    >5,则打印

它被标记为
perl
,因此perl回答:

perl -ne 'print if (grep /^NA$/, split ) > 5'
其中:

  • 逐行迭代
  • 空格分开了吗
  • greps仅为
    NA
  • 如果计数为
    >5,则打印

带有GNU awk的单词边界:

awk -F'\\<NA\\>' 'NF>6'
对于字段之间的任何类型的空格,使用任意POSIX awk:

awk -F'[[:space:]]NA([[:space:]]|$)' 'NF>6'

GNU awk用于单词边界:

awk -F'\\<NA\\>' 'NF>6'
对于字段之间的任何类型的空格,使用任意POSIX awk:

awk -F'[[:space:]]NA([[:space:]]|$)' 'NF>6'

在Perl中,只需计算等于
NA

perl -ne 'print if 5 <= grep { $_ eq "NA" } split' test.txt

perl-ne'如果在perl中打印5,只需计算等于
NA

perl -ne 'print if 5 <= grep { $_ eq "NA" } split' test.txt


perl-ne'print if 5我不确定“
awk
to the rescue”是一个适当的详细答案,尤其是当OP已经尝试使用awk时!我不确定“
awk
to the rescue”这是一个适当详细的答案,特别是当OP已经尝试使用awk时!这是唯一一个学生“NAthalie Miller”和5个NAs不是假阳性的;)@BenjaminW。这是唯一一个…:-)。我想现在唯一的选择是它看起来最不可怕:)@TomFenech我加了$1=“”跳过第一列(这样条件就不会影响名称),但现在它不会输出名称。我是否应该添加一些内容来输出所有列?我只是从第二个字段循环,这样第一个字段就不会影响计数。此外,正在测试字段是否与“NA”完全匹配。这是唯一一个允许学生使用的字段“NAthalie Miller”和5个NAs不是假阳性;)@BenjaminW。这是唯一一个…:-)。我想现在,唯一的原因是它看起来最不吓人:)@TomFenech我加了$1=“”,跳过第一列(这样条件就不会影响名称)但是现在它不输出名称。我是否应该添加一些内容来输出所有列?我只是从第二个字段循环,这样第一个字段就不会影响计数。此外,正在测试这些字段是否与“NA”完全匹配"。我想你可以用
-a
而不是调用
-split
。我想你可以用
-a
而不是调用
split
。有没有理由更喜欢调用
split
而不是使用
-a
-F
呢?@TomFenech:只是在这种情况下节省很少,而且对那些人来说也不太清楚对于的内容不完全熟悉的se。我在我的回答中添加了一个使用autosplit的版本。是否有理由更喜欢调用
split
,而不是使用
-a
@F
?@TomFenech:只是在这种情况下节省很少,对于那些对的内容不完全熟悉的人来说,这就不那么清楚了。我添加了一个使用autosplit到我的答案的版本是这样的,使用您的第二种方法,使NA最多出现6次(而不是至少出现6次)?它不起作用。它只输出整个文件。在这种情况下,我只想输出包含最大6次NA的行。@snowy_squirrel啊,对。要说“6次或更少”,您可以使用与“7次或更多”相同的模式,但使用
-v
grep-vE'^[^[:blank:]+[:blank:]+[:blank:]+(N