【问题标题】:Removing lines with the first and the last occurence of a column value删除第一次和最后一次出现列值的行
【发布时间】:2016-01-27 19:38:16
【问题描述】:

我有以下文件

    ID      Score    Other
    ABR     0.98     NBNMSB
    BCG     0.76     NBNMSB
    CVD     0.6      NBNMSB
    BCG     0.9      VSCVA
    CVD     0.56     VSCVA
    ABR     0.9      VSCVA
    CVD     0.7      BAVSC
    BCG     0.4      BAVSC
    ABR     0.5      BAVSC
    AAC     0.1      BAVSC
    ABR     0.8      NBNMSB
    BCG     0.6      NBNMSB
    CVD     0.3      NBNMSB
    BCG     0.7      VSCVA
    CVD     0.0      VSCVA
    ABR     0.1      VSCVA
    CVD     0.5      BAVSC
    BCG     0.8      BAVSC
    ABR     1.0      BAVSC

我想排除第 3 列中第一次和最后一次出现的值,这样我得到的输出如下:

ID      Score    Other
BCG     0.76     NBNMSB
CVD     0.56     VSCVA
BCG     0.4      BAVSC
ABR     0.5      BAVSC
BCG     0.6      NBNMSB
CVD     0.0      VSCVA
BCG     0.8      BAVSC

【问题讨论】:

  • 如果您将建议的输出通过过滤器反馈回来,您会期望什么(即,如果第 3 列中只有两个特定值,那么从字面上删除第一个和最后一个匹配项实际上会删除所有出现)?

标签: awk sed find-occurrences


【解决方案1】:

在 awk 你可以试试这个

awk 'NR==1
     {last[NR%3]=$3;lastLine[NR%3]=$0;}
     last[(NR-1)%3]==last[(NR-2)%3] && 
           last[(NR-1)%3]==last[NR%3]{print lastLine[(NR-1)%3]}' test

产生预期的输出:

ID      Score    Other
BCG     0.76     NBNMSB
CVD     0.56     VSCVA
BCG     0.4      BAVSC
ABR     0.5      BAVSC
BCG     0.6      NBNMSB
CVD     0.0      VSCVA
BCG     0.8      BAVSC

说明
1. NR==1 简单打印第一行。
2.{last[NR%3]=$3;lastLine[NR%3]=$0;}将最后两行和当前行存储在一个数组中(lastLine)。
3. 通过last[(NR-1)%3]==last[(NR-2)%3] && last[(NR-1)%3]==last[NR%3],我们检查第三列的最后一行是否与当前行具有相同的值,倒数第二行是否与最后一行具有相同的值(即它们在第三列中是否都具有相同的值)。在这种情况下,我们打印最后一行。

【讨论】:

  • 如果不是太麻烦,能不能破码? TIA
  • 我已经提供了解释。你是这个意思吗?
  • 是的,如果我有 4 列而不是 3 列,我该怎么做,例如我的格式为 abc 1234 2345 NBNMSB 等等作为行,我如何调整上面的代码以获得唯一第 4 列,我尝试将 3 替换为 4,还有什么需要更改的吗?
  • 在这种情况下,只需将每个 $3 替换为 $4。不要更改%3——这对于仅比较最后三行是必要的(即,正确丢弃指定列值的第一次和最后一次出现。)
  • 我做了这样的事情:awk 'NR==1{last[NR%4]=$4;lastLine[NR%4]=$0;}last[(NR-1)%4] ==last[(NR-2)%4]&&last[(NR-1)%4]==last[NR%4]{print lastLine[(NR-1)%4]}' 文件,但它没有' t吐出任何结果,我希望我做对了。
【解决方案2】:

如果您有tac(或gtac),您可以删除第一个实例、反转文件、删除第一个(实际上是最后一个)实例并最后一次翻转文件。

$ awk '$3==p;{p=$3}' file1 | tac | awk '$3==p;{p=$3}' | tac
BCG     0.76     NBNMSB
CVD     0.56     VSCVA
BCG     0.4      BAVSC
ABR     0.5      BAVSC
BCG     0.6      NBNMSB
CVD     0.0      VSCVA
BCG     0.8      BAVSC

编辑

这是一个更灵活的版本。只需将c 的初始值设置为所需的列:

使用第 3 列:

 c=3 && awk -v c=$c '$c==p;{p=$c}' file1 | tac | awk -v c=$c '$c==p;{p=$c}' | tac

使用第 4 列:

 c=4 && awk -v c=$c '$c==p;{p=$c}' file1 | tac | awk -v c=$c '$c==p;{p=$c}' | tac

【讨论】:

  • 另外,如果我有 4 列而不是 3 列,我该如何处理,例如,我的格式为 abc 1234 2345 NBNMSB,以此类推,我如何调整上面的代码以获得唯一的 4第列,我尝试将 3 替换为 4,还有什么需要更改的吗?
  • $3 全局更改为$4 将是唯一必要的更改。
  • 优秀。另请参阅修改以获得更灵活的版本。
【解决方案3】:

另一个更简单的awk是:

awk 'NR == 1; prev != $3 {prev = $3; line = 0; next} 
{if (line) print line; line = $0}' foo.txt | column -t

你会得到

ID   Score  Other
BCG  0.76   NBNMSB
CVD  0.56   VSCVA
BCG  0.4    BAVSC
ABR  0.5    BAVSC
BCG  0.6    NBNMSB
CVD  0.0    VSCVA
BCG  0.8    BAVSC

它的作用是将第 3 列和行存储在名为 prevline 的变量中,如果它们不是第一次出现和最后一次出现,则将它们打印出来。

请注意,与使用 tac 和多次遍历相比,这仅需要 1 次遍历文件。

【讨论】:

    【解决方案4】:

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

    sed -r '1p;$!N;/(\S+)\n.*\1$/!d;P;D' file
    

    打印第一行(标题行)。一次读取两行,如果这两行没有相同的第三列,请将它们都删除。否则打印第一行并附加下一行并重复。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-04
      • 2021-08-29
      • 2016-08-25
      • 2019-12-07
      • 2022-11-22
      • 1970-01-01
      相关资源
      最近更新 更多