在bash中的已排序类别中随机抽取唯一样本

在bash中的已排序类别中随机抽取唯一样本,bash,sorting,unix,random,command-line,Bash,Sorting,Unix,Random,Command Line,我有一个大的未排序的CSV文件(>4M记录)。每个记录都有一个类别,在前三列中进行了描述。记录的其余部分是地址数据,可能是唯一的,也可能不是唯一的 A, 1, c, address1 # the category for this record is A1t A, 1, c, address2 C, 3, e, address3 # the category for this record is C3e B, 2, a, address4 我想随机抽取每个类别中的唯一记录样本(因此类别A1

我有一个大的未排序的CSV文件(>4M记录)。每个记录都有一个类别,在前三列中进行了描述。记录的其余部分是地址数据,可能是唯一的,也可能不是唯一的

A, 1, c, address1  # the category for this record is A1t
A, 1, c, address2
C, 3, e, address3  # the category for this record is C3e
B, 2, a, address4
我想随机抽取每个类别中的唯一记录样本(因此类别
A1t
中有5条唯一记录,类别
C3e
中有5条唯一记录,等等)。我使用
sort
组合了一个部分解决方案。但是,它在每个类别中只提取一条非随机记录:

sort -u -t, -k1,3
有没有办法在每个类别中抽取几个随机样本记录


我认为一定有一种方法可以做到这一点,就是使用管道组合,
uniq
awk
shuf
,但我还没有弄明白。我更喜欢命令行解决方案,因为我想知道是否只使用bash就可以做到这一点。

如果我理解正确-简单但不是非常有效的bash解决方案

csvfile="./ca.txt"
while read -r cat
do
    grep "^$cat," "$csvfile" | sort -uR | head -5
done < <(cut -d, -f1-3 < "$csvfile" |sort -u)
csvfile=“./ca.txt”
而read-r猫
做
grep“^$cat”“$csvfile”| sort-uR | head-5

完成<灵感来自于在中使用
sort-R
。这是对
排序
的GNU扩展,因此它可能无法在非GNU系统上工作

在这里,我们使用sort对整个文件进行一次排序,非类别字段按随机顺序排序。由于类别字段是主键,因此结果按类别顺序排列,以下字段的顺序为随机顺序

从那里,我们需要找到每个类别的前五个条目。可能有更黑客的方法可以做到这一点,但我使用了一个简单的
awk
程序

sort -ut, -k1,3 -k4R "$csvfile" | awk -F, 'a!=$1$2$3{a=$1$2$3;n=0}++n<=5'

也可以将所有条目保留在awk中以避免排序,但这可能会慢得多,而且会占用大量内存。

谢谢!我没有意识到可以在特定行上使用
-R
选项。awk代码片段正是我想弄明白的。正如@rici提到的,
sort
-R
选项在mac OS X等非GNU系统上不可用。我下载了
coreutils
(通过自制,
brew安装coreutils
),它有一个与GNU
sort
等效的
gsort
命令,并包含
-R
选项。@gunnar:
sort-k4R
表示使用随机散列对字段从4到行尾进行排序。所以它不完全是一列。必须使用带分隔符的第一个键(
-k1,3
表示从字段1到字段3的末尾)。如果希望排序键是单个列,则需要将列号写入两次:
-k1,1
# Warning! Only slightly tested :)
sort -ut, "$csvfile" | awk -F, '
      function sample(){
        for(;n>5;--n)v[int(n*rand())+1]=v[n];
        for(;n;--n)print v[n]
      }
      a!=$1$2$3{a=$1$2$3;sample()}
      {v[++n]=$0}
      END      {sample()}'