我有一个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
答案 0 :(得分:86)
如果您有那么多行,您确定要完全 1%还是统计估算就足够了?
在第二种情况下,只需在每一行随机化1%......
awk 'BEGIN {srand()} !/^$/ { if (rand() <= .01) print $0}'
如果你想要标题行加上随后的行样本,请使用:
awk 'BEGIN {srand()} !/^$/ { if (rand() <= .01 || FNR==1) print $0}'
答案 1 :(得分:52)
您使用的是awk,但我不知道是否需要它。如果不是,这是一个简单的方法来做w / perl(并且不将整个文件加载到内存中):
cat your_file.txt | perl -n -e 'print if (rand() < .01)'
(更简单的形式,来自评论):
perl -ne 'print if (rand() < .01)' your_file.txt
答案 2 :(得分:19)
我在Gawk写了这个确切的代码 - 你很幸运。部分原因是它保留了输入顺序。可能会有性能增强。
该算法在不事先知道输入大小的情况下是正确的。我在这里发布了一个rosetta stone。 (我没有发布此版本,因为它进行了不必要的比较。)
原帖:Submitted for your review -- random sampling in 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
}
答案 3 :(得分:16)
这适用于大多数GNU / Linux机器。
$ shuf -n $(( $(wc -l < $file) / 100)) $file
如果GNU shuf命令不恰当地完成内存管理,我会感到惊讶。
答案 4 :(得分:5)
我不知道 awk ,但是有一种很好的技术可以解决你所描述的问题的更一般版本,而且在一般情况下它比它快得多。如果rand&lt; 用于文件返回行中的行0.01 方法,因此如果您打算执行上述许多(数千,数百万)次的任务,它可能会很有用。它被称为reservoir sampling,this page对其适用于您的情况的版本有很好的解释。
答案 5 :(得分:5)
如何从大群体(未知大小)中均匀地采样N个元素的问题称为Reservoir Sampling。 (如果您喜欢算法问题,请花几分钟尝试解决它,而无需在维基百科上阅读算法。)
“水库采样”的网络搜索将找到很多实现。 Here是实现你想要的Perl和Python代码,而here是另一个讨论它的Stack Overflow线程。
答案 6 :(得分:3)
你可以分两次通过:
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
答案 7 :(得分:2)
在这种情况下,使用class Test(Services):
def __init__(self):
Services.__init__(self)
def start(self):
print('start')
services_path = 'services'
s = 'test'
instance = getattr(importlib.import_module(services_path + '.' + s), s.title())
with ThreadPoolExecutor(max_workers=2) as executor:
tmp = executor.submit(instance.start)
print(tmp.result())
来获得精确 k 值的油藏采样是微不足道的,我很惊讶没有解决方案的建议。我不得不解决同样的问题,并编写了以下TypeError: start() missing 1 required positional argument: 'self'
程序进行抽样:
awk
然后找出 k 的内容必须单独完成,例如设置awk
答案 8 :(得分:1)
不要等到最后随机选择1%的行,而是在“/ ^ $ /”中每隔100行执行一次。这样,你一次只能容纳100行。
答案 9 :(得分:1)
如果目的只是为了避免内存耗尽,而且文件是常规文件,则无需实施水库采样。如果您在文件中执行两次传递,一个用于获取行数(如wc -l
),则可以知道文件中的行数,一个用于选择样本:
file=/some/file
awk -v percent=0.01 -v n="$(wc -l < "$file")" '
BEGIN {srand(); p = int(n * percent)}
rand() * n-- < p {p--; print}' < "$file"