是否可以使用任何Linux命令来采样文件的子集?例如,一个文件包含一百万行,而我们只想从该文件中随机抽取一千行。
对于随机而言,我的意思是每条线都有相同的概率被选择,并且所选择的线都不是重复的。
head
并tail
可以选择文件的一个子集,但不能随机选择。我知道我总是可以编写python脚本来这样做的,但是我想知道是否有用于此用法的命令。
是否可以使用任何Linux命令来采样文件的子集?例如,一个文件包含一百万行,而我们只想从该文件中随机抽取一千行。
对于随机而言,我的意思是每条线都有相同的概率被选择,并且所选择的线都不是重复的。
head
并tail
可以选择文件的一个子集,但不能随机选择。我知道我总是可以编写python脚本来这样做的,但是我想知道是否有用于此用法的命令。
Answers:
该shuf
命令(coreutils的一部分)可以执行以下操作:
shuf -n 1000 file
至少对于现在的非古代版本(在2013年的提交中添加),它将在适当的时候使用存储库采样,这意味着它不应该耗尽内存并使用快速算法。
sort
是,它在同一部分中,并且显然不需要排序的输入。
shuf
是在version中引入coreutils的6.0 (2006-08-15)
,无论您相信与否,某些合理常见的系统(尤其是CentOS 6.5)都没有该版本:-|
shuf -n
至少在输入大于8K时进行油藏采样,这是他们确定的大小更好的基准。参见源代码(例如,在github.com/coreutils/coreutils/blob/master/src/shuf.c#L46)。很抱歉这个答案很晚。显然,这是6年前的新功能。
如果文件很大(这是取样的常见原因),则会发现:
shuf
耗尽内存$RANDOM
如果文件超过32767行,则无法正常使用如果您不需要“完全” n条采样线,则可以像这样采样比率:
cat input.txt | awk 'BEGIN {srand()} !/^$/ { if (rand() <= .01) print $0}' > sample.txt
这使用恒定的内存,对文件的1%进行采样(如果您知道文件的行数,则可以调整此因子以对接近有限的行数进行采样),并且可以处理任何大小的文件,但不会返回精确的行数,只是一个统计比率。
注意:该代码来自:https : //stackoverflow.com/questions/692312/randomly-pick-lines-from-a-file-without-slurping-it-with-unix
$RANDOM
无法用于大于32767行的文件。“使用$RANDOM
无法到达整个文件” 的说法有点宽泛。
awk
与shuf
如果shuf -n
大文件上的技巧用完了内存,并且您仍然需要固定大小的示例,并且可以安装外部实用程序,请尝试以下示例:
$ sample -N 1000 < FILE_WITH_MILLIONS_OF_LINES
需要注意的是样本(示例中为 1000行)必须适合内存。
免责声明:我是推荐软件的作者。
/usr/local/bin
之前/usr/bin/
在他们的道路,提防MACOS带有一个内置的叫做调用堆栈采样sample
,这会产生完全不同的,在/usr/bin/
。
我不知道有任何一条命令可以完成您所要求的操作,但是我放了一个循环来完成您的工作:
for i in `seq 1000`; do sed -n `echo $RANDOM % 1000000 | bc`p alargefile.txt; done > sample.txt
sed
将在每1000个通行证中随机选择一条线。可能有更有效的解决方案。
$RANDOM
其范围在0到32767之间。因此,您将无法获得分布良好的行号。
您可以将以下代码保存在文件中(例如randextract.sh),并执行为:
randextract.sh file.txt
----开始文件----
#!/bin/sh -xv
#configuration MAX_LINES is the number of lines to extract
MAX_LINES=10
#number of lines in the file (is a limit)
NUM_LINES=`wc -l $1 | cut -d' ' -f1`
#generate a random number
#in bash the variable $RANDOM returns diferent values on each call
if [ "$RANDOM." != "$RANDOM." ]
then
#bigger number (0 to 3276732767)
RAND=$RANDOM$RANDOM
else
RAND=`date +'%s'`
fi
#The start line
START_LINE=`expr $RAND % '(' $NUM_LINES - $MAX_LINES ')'`
tail -n +$START_LINE $1 | head -n $MAX_LINES
----结束文件----
$RANDOM$RANDOM
不会在整个范围“ 0到3276732767”中生成随机数(例如,它将生成1000100000,但不会生成1000099999)。
如果您知道文件中的行数(例如1e6),则可以执行以下操作:
awk -v n=1e6 -v p=1000 '
BEGIN {srand()}
rand() * n-- < p {p--; print}' < file
如果没有,您可以随时做
awk -v n="$(wc -l < file)" -v p=1000 '
BEGIN {srand()}
rand() * n-- < p {p--; print}' < file
这将在文件中进行两次传递,但仍避免将整个文件存储在内存中。
与GNU相比的另一个优点shuf
是它保留了文件中各行的顺序。
请注意,它假定n
是文件中的行数。如果要打印p
出文件的第一 n
行(可能有更多行),则需要awk
在第n
th行停止,例如:
awk -v n=1e6 -v p=1000 '
BEGIN {srand()}
rand() * n-- < p {p--; print}
!n {exit}' < file
或像这样:
LINES=$(wc -l < file)
RANDLINE=$[ $RANDOM % $LINES ]
tail -n $RANDLINE < file|head -1
从bash手册页:
RANDOM每次引用此参数时,一个随机整数 生成介于0和32767之间的值。随机顺序 可以通过为RAN-分配一个值来初始化数字 DOM。如果未设置RANDOM,则会丢失其特殊属性- 领带,即使随后将其重置。
如公认的答案所述,GNU 很好地shuf
支持简单的随机采样(shuf -n
)。如果需要的采样方法超出eBay支持的方法shuf
,请考虑从eBay的TSV实用程序中获取tsv-sample。它支持几种其他采样模式,包括加权随机采样,伯努利采样和不同采样。性能类似于GNU (两者都非常快)。免责声明:我是作者。shuf