sed 或 awk:按 2 列分组并获取另一列的最后一个值

sed or awk : group by 2 columns and get the last value of another column

我需要快速处理一个包含大量冗余的文本文件。 我可以使用 python,但我认为最简单和最快的方法是在 unix shell.

中使用 awk、sed 或 perl

数据有 3 列,我需要按(唯一的)第 1 列和第 3 列分组,然后获取第 2 列的最后一个值

1,2,3
a 1 A
a 2 A
a 3 A
b 2 C
b 3 C
b 3 D
c 1 C
c 1 D
c 2 D

结果应该是这样的:

1,2,3
a A 3
b C 3
b D 3
c C 1
c D 2

考虑到您的 Input_file 与所示示例相同,并且在第一个和第三个字段的排序方法中,以下内容可能会对您有所帮助。

awk '
FNR==1{
  print;
  next
}
!a[,]++{
  if(a[prev]){
    print prev,a[prev]};
  a[,]=
}
{
  prev= FS 
}
END{
  if(a[prev] && prev){
    print prev,a[prev]
}}
' SUBSEP=" "   Input_file

输出如下:

1,2,3
a A 3
b C 3
b D 3
c C 1
c D 2

你可以使用这个awk:

awk '{key= FS } !(key in arr){a[++n]=key} {arr[key]=}
END{for (i=1; i<=n; i++) print a[i], arr[a[i]]}' file

1,2,3
a A 3
b C 3
b D 3
c C 1
c D 2

有一个专门用于此类任务的工具 - datamash:

部分解决方案,没有 header:

datamash -W -t' ' --header-in -g 1,3 last 2 < input.txt

为了简化演示代码,放弃了 header 1,2,3,因为它有另一个字段分隔符,而不是其他行,这使任务复杂化。

说明

  • -W, --whitespace - 使用空格(一个或多个空格 and/or 制表符)作为字段分隔符。

  • -t, --field-separator=X - 使用 X 而不是 TAB 作为字段分隔符。

  • --header-in - 第一个输入行是列 headers(在我们的例子中,我们这样做只是为了省略 header)。
  • -g, --group=X[,Y,Z] - 通过字段分组 X,[Y,Z].
  • last - 组的最后一个值。

输出

a A 3
b C 3
b D 3
c C 1
c D 2

完整的解决方案,其中保留了 header:

cat <(head -n 1 input.txt) <(tail -n +2 input.txt | datamash -W -t' ' -g 1,3 last 2)

输出

1,2,3
a A 3
b C 3
b D 3
c C 1
c D 2

使用 tac 和 GNU sort

$ tac Group_Data | sort -u -k1,1 -k3 | awk '{if (NR == 1) print [=10=]; else print ,,}'
1,2,3
a A 3
b C 3
b D 3
c C 1
c D 2

IMO python 词典和强大的 sorted 功能使 python 与任何其他类似解决方案一样快速且具有竞争力,特别是如果您要使用 python进一步处理下游的数据。下面的项目通过使用它们作为 Python 字典的键进行分组,然后按第一个和第二个元素排序。

>>> groupdict = {}
>>> with open("Group_Data") as fp:
...     for ii, lines in enumerate(fp):
...         if ii == 0:
...             header = lines.rstrip()
...         else:
...             fields = lines.split()
...             groupdict[(fields[0],fields[2])] = fields[1]
... 
>>> groupdict
{('b', 'C'): '3', ('a', 'A'): '3', ('c', 'D'): '2', ('c', 'C'): '1', ('b', 'D'): '3'}
>>> for ii,elem in enumerate(sorted(groupdict.items(), key = lambda x : (x[0],x[1]))):
...     if ii == 0:
...         print header
...     key, value = elem
...     print key[0],key[1],value
... 
1,2,3
a A 3
b C 3
b D 3
c C 1
c D 2