随机从文件中选择行而不用Unix扼杀它

时间:2009-03-28 05:55:01

标签: linux unix awk random-sample file-processing

我有一个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

10 个答案:

答案 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 samplingthis 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"