是否有任何 Linux 命令可以用来对文件的子集进行采样?例如,一个文件包含一百万行,我们只想从该文件中随机采样一千行。
对于随机,我的意思是每条线都有相同的被选择概率,并且选择的线都不是重复的。
head
并tail
可以选择文件的子集,但不是随机的。我知道我总是可以编写一个 python 脚本来执行此操作,但只是想知道是否有用于此用途的命令。
答案1
命令shuf
(coreutils 的一部分)可以执行此操作:
shuf -n 1000 file
至少现在非古代版本(添加在从2013年开始承诺),这将在适当的时候使用水库采样,这意味着它不应该耗尽内存并且正在使用快速算法。
答案2
如果你有一个很大文件(这是取样的常见原因)您会发现:
shuf
耗尽内存$RANDOM
如果文件超过 32767 行,则无法正常使用
如果您不需要“精确”n 条采样线你可以采样比例像这样:
cat input.txt | awk 'BEGIN {srand()} !/^$/ { if (rand() <= .01) print $0}' > sample.txt
这用途持续记忆,对文件的 1% 进行采样(如果您知道文件的行数,则可以调整此系数以对接近有限的行数进行采样),并且适用于任何尺寸但它的文件将不会返回一个精确的行数,只是一个统计比例。
答案3
与 @Txangel 的概率解决方案类似,但速度快了 100 倍。
perl -ne 'print if (rand() < .01)' huge_file.csv > sample.csv
如果您需要高性能、精确的样本大小,并且愿意接受文件末尾的样本间隙,则可以执行类似以下操作(从 1m 行文件中采样 1000 行):
perl -ne 'print if (rand() < .0012)' huge_file.csv | head -1000 > sample.csv
..或者确实链接第二个示例方法而不是head
.
答案4
当我想保留标题行并且样本可以是文件的近似百分比时,我喜欢使用 awk。适用于非常大的文件:
awk 'BEGIN {srand()} !/^$/ { if (rand() <= .01 || FNR==1) print > "data-sample.txt"}' data.txt