我有一个10 ^ 7行文件,其中我想从文件中随机选择1/100行.这是我所拥有的AWK代码,但它会预先包含所有文件内容.我的PC内存无法处理这样的问题.还有其他办法吗?
awk 'BEGIN{srand()} !/^$/{ a[c++]=$0} END { for ( i=1;i<=c ;i++ ) { num=int(rand() * c) if ( a[num] ) { print a[num] delete a[num] d++ } if ( d == c/100 ) break } }' file
cadrian.. 86
如果你有很多行,你确定你想正是 1%或统计估计就足够了?
在第二种情况下,每行只需1%随机...
awk 'BEGIN {srand()} !/^$/ { if (rand() <= .01) print $0}'
如果你想要标题行加上随后的行样本,请使用:
awk 'BEGIN {srand()} !/^$/ { if (rand() <= .01 || FNR==1) print $0}'
@Steven:理论上是的,几乎没有.对于OP的1000万行文件(由泊松分布给出),不打印行的概率大约为10 ^ -43430.通过猜测第一次尝试,相当于破解144*千字节*加密密钥. (10认同)
@Steven:不.算法需要正确*足够*.在这种情况下,它是正确的. (8认同)
好的,但是,正确的_algorithm_需要是正确的,而不是"非常可能是正确的".恕我直言. (4认同)
此外,对于OP的情况(10 ^ 7行),所选元素数量在10,000的1%内的可能性为99.8%. (3认同)
请注意,此算法会偏向较长的线条.如果要对此进行校正,则必须对采样进行加权(例如,采样线经常延长2倍的采样线).确切地说,如何做到这一点留给读者练习(提示:建立线长的经验分布). (2认同)
这里提到的正则表达式是否匹配每一行?如果是这样,你是否只是这样做,因为这是一种避免在每一行上进行for循环的聪明方法? (2认同)
Bill.. 53
你使用awk,但我不知道是否需要它.如果不是,这里有一个简单的方法来做w/perl(并且不将整个文件加载到内存中):
cat your_file.txt | perl -n -e 'print if (rand() < .01)'
(更简单的形式,来自评论):
perl -ne 'print if (rand() < .01)' your_file.txt
@Steven,阅读原帖.他的文件有10 ^ 7行.他没有获得输出的几率是.99 ^ 100000000.说这是不可接受的是荒谬的.如果您担心这种程度的错误,则不应使用计算机.由于光线射线,你更有可能得到不正确的输出. (17认同)
这个版本(第二个例子)很快疯狂.我刚刚在SSD上运行了一个15GB的文件.有一个150MB的文件.尼斯. (3认同)
Steven Huwig.. 19
我在Gawk写了这个确切的代码 - 你很幸运.部分原因是它保留了输入顺序.可能会有性能增强.
该算法在不事先知道输入大小的情况下是正确的.我在这里贴了一块玫瑰石.(我没有发布这个版本,因为它进行了不必要的比较.)
原帖:提交给你的评论 - 在awk随机抽样.
# Waterman's Algorithm R for random sampling # by way of Knuth's The Art of Computer Programming, volume 2 BEGIN { if (!n) { print "Usage: sample.awk -v n=[size]" exit } t = n srand() } NR <= n { pool[NR] = $0 places[NR] = NR next } NR > n { t++ M = int(rand()*t) + 1 if (M <= n) { READ_NEXT_RECORD(M) } } END { if (NR < n) { print "sample.awk: Not enough records for sample" \ > "/dev/stderr" exit } # gawk needs a numeric sort function # since it doesn't have one, zero-pad and sort alphabetically pad = length(NR) for (i in pool) { new_index = sprintf("%0" pad "d", i) newpool[new_index] = pool[i] } x = asorti(newpool, ordered) for (i = 1; i <= x; i++) print newpool[ordered[i]] } function READ_NEXT_RECORD(idx) { rec = places[idx] delete pool[rec] pool[NR] = $0 places[idx] = NR }
ashawley.. 16
这适用于大多数GNU/Linux机器.
$ shuf -n $(( $(wc -l < $file) / 100)) $file
如果GNU shuf命令不恰当地完成内存管理,我会感到惊讶.
如果你有很多行,你确定你想正是 1%或统计估计就足够了?
在第二种情况下,每行只需1%随机...
awk 'BEGIN {srand()} !/^$/ { if (rand() <= .01) print $0}'
如果你想要标题行加上随后的行样本,请使用:
awk 'BEGIN {srand()} !/^$/ { if (rand() <= .01 || FNR==1) print $0}'
你使用awk,但我不知道是否需要它.如果不是,这里有一个简单的方法来做w/perl(并且不将整个文件加载到内存中):
cat your_file.txt | perl -n -e 'print if (rand() < .01)'
(更简单的形式,来自评论):
perl -ne 'print if (rand() < .01)' your_file.txt
我在Gawk写了这个确切的代码 - 你很幸运.部分原因是它保留了输入顺序.可能会有性能增强.
该算法在不事先知道输入大小的情况下是正确的.我在这里贴了一块玫瑰石.(我没有发布这个版本,因为它进行了不必要的比较.)
原帖:提交给你的评论 - 在awk随机抽样.
# Waterman's Algorithm R for random sampling # by way of Knuth's The Art of Computer Programming, volume 2 BEGIN { if (!n) { print "Usage: sample.awk -v n=[size]" exit } t = n srand() } NR <= n { pool[NR] = $0 places[NR] = NR next } NR > n { t++ M = int(rand()*t) + 1 if (M <= n) { READ_NEXT_RECORD(M) } } END { if (NR < n) { print "sample.awk: Not enough records for sample" \ > "/dev/stderr" exit } # gawk needs a numeric sort function # since it doesn't have one, zero-pad and sort alphabetically pad = length(NR) for (i in pool) { new_index = sprintf("%0" pad "d", i) newpool[new_index] = pool[i] } x = asorti(newpool, ordered) for (i = 1; i <= x; i++) print newpool[ordered[i]] } function READ_NEXT_RECORD(idx) { rec = places[idx] delete pool[rec] pool[NR] = $0 places[idx] = NR }
这适用于大多数GNU/Linux机器.
$ shuf -n $(( $(wc -l < $file) / 100)) $file
如果GNU shuf命令不恰当地完成内存管理,我会感到惊讶.
我不知道awk,但是有一个很好的技术可以解决你所描述的问题的更一般版本,并且在一般情况下,如果rand <0.01,它比文件返回行中的for行要快得多方法,因此如果您打算执行上述许多(数千,数百万)次的任务,它可能会很有用.它被称为水库采样,该页面对其适用于您的情况的版本有很好的解释.
如何从大群体(未知大小)中均匀地采样N个元素的问题被称为水库采样.(如果您喜欢算法问题,请花几分钟尝试解决它,而无需在维基百科上阅读算法.)
对"水库采样"进行网络搜索会发现很多实施方案.这是实现你想要的Perl和Python代码,这是另一个讨论它的Stack Overflow线程.