从文件中随机选取行而不用Unix啜食它
我有一个10 ^ 7行文件,我想从文件中随机select1/100行。 这是我所拥有的AWK代码,但是它可以在手边浏览所有的文件内容。 我的电脑内存不能处理这样的诽谤。 还有其他的方法吗?
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
如果你有这么多的线,你确定你只需要1%或统计估计就足够了?
在第二种情况下,在每条线上随机select1%…
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)'
(更简单的forms,来自评论):
perl -ne 'print if (rand() < .01)' your_file.txt
我在Gawk写了这个确切的代码 – 你很幸运。 这只是部分原因,因为它保留了input顺序。 可能会有性能提升。
这个algorithm是正确的,不用提前知道input大小。 我在这里贴了一个罗塞塔石 。 (我没有发布这个版本,因为它做了不必要的比较。)
原始线程: 提交给您的审查 – 随机抽样在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 -vn=[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
如果内存pipe理是由GNU shuf命令不恰当地完成的,我会感到惊讶。
我不知道awk ,但是有一个很好的技术来解决你所描述的问题的更一般的版本,在一般情况下, 如果rand <0.01,比文件返回行中的行要快得多的方法,所以如果你打算完成上述诸多(数千,数百万)的任务,这可能是有用的。 它被称为油藏采样 , 这个页面有一个相当好的解释,适用于你的情况。
如何从大量(未知数量)的样本中均匀采样N个元素的问题被称为油藏采样 。 (如果你喜欢algorithm问题,不用花费几分钟的时间试图解决它,而无需阅读Wikipedia上的algorithm。)
一个networkingsearch“水库采样”将会发现很多的实现。 这里是Perl和Python代码实现你想要的, 这里是另一个堆栈溢出线程讨论它。
你可以做两遍:
- 通过文件运行一次,只是为了计算有多less行
- 随机select要打印的行的行号,将它们存储在已sorting的列表(或集合)中
- 再次运行该文件,并在选定的位置select线条
Python中的示例:
fn = '/usr/share/dict/words' from random import randint from sys import stdout count = 0 with open(fn) as f: for line in f: count += 1 selected = set() while len(selected) < count//100: selected.add(randint(0, count-1)) index = 0 with open(fn) as f: for line in f: if index in selected: stdout.write(line) index += 1
而不是等到最后随机挑选1%的行,在“/ ^ $ /”中每100行执行一次。 那样,你一次只能容纳100条线。
如果目的只是为了避免内存耗尽,而文件是一个常规文件,则不需要执行油藏采样。 如果你在文件中做了两遍,可以知道文件中的行数,一行就可以得到行数(例如wc -l
),一行select样本:
file=/some/file awk -v percent=0.01 -vn="$(wc -l < "$file")" ' BEGIN {srand(); p = int(n * percent)} rand() * n-- < p {p--; print}' < "$file"