按第一列分组

按第一列分组

有一个具有以下模式的文件

a 12
a 13
a 15
a 14
b 5
b 6
c 2
c 5

我想按照第一列对这些进行分组,如下所示

a 12 13 15 14
b 5 6
c 2 5

请建议

答案1

假设数据按第一列排序(如问题中所示),使用 GNUdatamash按第一个空格分隔列进行分组并折叠第二列。

$ datamash -W groupby 1 collapse 2 <file
a       12,13,15,14
b       5,6
c       2,5

将结果中第一列后面的制表符以及逗号替换为空格,以得到所需的输出:

$ datamash -W groupby 1 collapse 2 <file | tr '\t,' '  '
a 12 13 15 14
b 5 6
c 2 5

如果输入未按第一列排序,则sort首先传递数据,或datamash与其-s( --sort) 选项一起使用。


您还可以使用米勒 ( mlr)和它的nest手术。使用此功能,您可以将第二列中的值“内爆”到第一列中每个唯一值的空格分隔列表中:

$ mlr --nidx nest --implode --values --across-records --nested-fs space -f 2 file
a 12 13 15 14
b 5 6
c 2 5

或者,--ivar在 miller 版本 5.5.0 或更高版本中使用简写(除了在 6.0.0 中,它是破碎的):

$ mlr --nidx nest --ivar space -f 2 file
a 12 13 15 14
b 5 6
c 2 5

数据不需要预先排序。

答案2

使用awk(如果输入文件按第一列排序):

awk '{ printf "%s", (NR==1 || pre!=$1? (NR>1? ORS:"")$1: "") OFS $2; pre=$1 }
END  { print "" }' infile

使用awk+ sort(如果输入文件未按第一列排序):

<infile sort|
    awk '{ printf "%s", (NR==1 || pre!=$1? (NR>1? ORS:"")$1: "") OFS $2; pre=$1 }
    END  { print "" }'

或者仅使用awk排序输入或未排序输入:

awk '{ grp[$1]= (grp[$1]==""?"":grp[$1] OFS) $2 }
END  { for(x in grp) print x, grp[x] }' infile

答案3

使用(以前称为 Perl_6)

raku -e 'lines.map(*.words).map(*.hash).classify( *.keys, :as{$_.values} ).put;'  

输入示例:

a 12
a 13
a 15
a 14
b 5
b 6
c 2
c 5

示例输出:

a   12 13 15 14
b   5 6
c   2 5

以下是编码的解决方案,Perl 编程语言家族的成员。上面的代码处理两列列表的(简单)情况(更通用的解决方案的说明,如下)。

对于更通用的解决方案(具有两列或更多列输入),请lines逐行读取,将words和中的元素分解为append散列%h,使用=>(粗箭头)散列构造函数来描绘键(左)和值(右)。注意,特殊的赋值运算符省去了我们首先单独.=声明的步骤。my %h;然后进行哈希classify,注意返回值(否则返回完整的键值对):%hkey:as

raku -e 'my %h.=append(.words[0] => .words[1..*]) for lines;  %h.classify(*.keys, :as{$_.values}).put;' 

在一个小数据集上运行上面的代码,例如warpbreaks来自 R 编程语言的数据(54 行,3 列),其中第二列引用两种类型的羊毛,“ A”和“ B”。

  • [输入文件如下: 使用 withwarpbreaks导出的数据集,后处理以删除双引号,以及将逗号转换为制表符]。Rwrite.csvrow.names=FALSE

工作 Raku 代码(和返回),如下。请注意如何使用 -ed非键 value列(或使用等连接):join"|"join(",")

~$ raku -e 'my %h.=append(.words[1] => .words[0,2].join("|") ) for lines.skip(1);  %h.classify(*.keys, :as{$_.values}).put;'  warpbreaks_no_quotes.tsv
A   26|L 30|L 54|L 25|L 70|L 52|L 51|L 26|L 67|L 18|M 21|M 29|M 17|M 12|M 18|M 35|M 30|M 36|M 36|H 21|H 24|H 18|H 10|H 43|H 28|H 15|H 26|H
B   27|L 14|L 29|L 19|L 29|L 31|L 41|L 20|L 44|L 42|M 26|M 19|M 16|M 39|M 28|M 21|M 39|M 29|M 20|H 21|H 24|H 17|H 13|H 15|H 15|H 16|H 28|H

https://stat.ethz.ch/R-manual/R-devel/library/datasets/html/warpbreaks.html
https://docs.raku.org/routine/classify
https://raku.org

答案4

使用 awk 的关联数组可能是一个更简单的选择:

$ awk '{k[$1]=k[$1]" "$2} END {for (i in k) print i k[i]}' infile 
a 12 13 15 14
b 5 6
c 2 5

相关内容