簡単な解決策は、キー列でファイルを並べ替えることです。たとえば、タブで区切られた入力を2番目の列で並べ替えます。
#!/bin/bash
printf "a\tz\nb\ty\nc\tx" | sort -k 2 -t $'\t'
次に、一意のキーごとにランダムな行の25%を取得するという単純な問題を解決します。この場合、キーが等しいすべての行が隣接しており、一意のキーごとに少なくとも1つの行を保持する必要があります。
#!/usr/bin/env python
import random
import sys
from itertools import chain, groupby
def choose_random(iterator, fraction, random=random.random):
"""Lazy analog of:
L = list(iterator)
k = int(len(L) * fraction + .5) or 1 # get at least one
result = random.sample(L, k)
Note: this function doesn't randomize the order of elements
that would require to keep selected elements in memory
and number of output elements is not exactly k
"""
# always yield at least one item if input is not empty
item = next(iterator)
it = (x for x in chain([item], iterator) if random() < fraction)
for x in chain([next(it, item)], it):
yield x
def getkey(line):
return line.split("\t")[1] # 2nd column
for key, group in groupby(sys.stdin, key=getkey):
sys.stdout.writelines(choose_random(group, fraction=0.25))
注:入力ファイルの最後の行には改行が含まれている必要があります。含まれていない場合、最後の行を選択すると出力が破損します。
スクリプトは、stdinで(キー列によって)ソートされた入力を受け入れ、縮小された出力をstdoutに出力します。一度に1行だけメモリに保存する必要があります。これはシングルパスアルゴリズム(O(n))です。