有一个具有以下模式的文件
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
,注意返回值(否则返回完整的键值对):%h
key
:as
raku -e 'my %h.=append(.words[0] => .words[1..*]) for lines; %h.classify(*.keys, :as{$_.values}).put;'
在一个小数据集上运行上面的代码,例如warpbreaks
来自 R 编程语言的数据(54 行,3 列),其中第二列引用两种类型的羊毛,“ A
”和“ B
”。
- [输入文件如下: 使用 with
warpbreaks
导出的数据集,后处理以删除双引号,以及将逗号转换为制表符]。R
write.csv
row.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