在Python中随机文本文件缩减(text file reduction with randomiz

2019-10-17 18:35发布

我解决了在bash下面的问题,但我觉得这是相当低效的速度很慢给予我需要减少文件的大小。 希望某人有一个想法如何做同样在Python,并希望加快速度。

原来的问题是减少非常大的文本文件(50-60万线,制表符分隔列)。 其中一列被视为一个关键,即我们确定有多少行了一个独特的密钥是在该文件中,然后随机选择其中的一个百分比(例如总数的四分之一,如果减少了75%),以追加到一个新的文件,将让我们的结果。 我们继续走通过按键的其余部分,随机化,然后将含有相同的百分比每一个独特的密钥的所有线路。 如果还原无法做到的 - 我们只是承载所有的线条到生成的文件。

正如我所说,我的bash脚本工作得很好,但它是缓慢的,串在一起的各种AWK和grep结构。 从各方面来看,巨蟒应以更优雅的方式,在不损害记忆太多(再次,我们面对的是50多个亿行的文件在这种情况下)处理这个。 任何建议/技巧将是有益的! 谢谢!

Answer 1:

简单的解决办法是排序键列例如,文件排序由所述第二柱制表符分隔输入:

#!/bin/bash
printf "a\tz\nb\ty\nc\tx" | sort -k 2 -t $'\t'

进而解决检索的随机行的每个唯一键,其中同键所有的线条都与相邻的约束,对于每一个独特的密钥至少一行应保留25%的简单的问题:

#!/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和打印减小的输出到标准输出输入排序。 它要求只存储在内存线在时间。 它是一个单通算法(O(N))。



Answer 2:

因为你的问题是模糊的,我会给予了很高的水平解决方案

  1. 不读内存中的整个文件fileObj.read()fileObj.readlines()而不是通过文件迭代for line in fileObj 。 为什么? 这将是内存friednly
  2. 创建基于列表队列的实现

     class Queue(object): def __init__(self, max_size): self.queue = [] self.max_size = max_size def __getitem__(self, index): if 0 <= index < max_size: return self.queue[index] else: raise IndexError def __iter__(self): return iter(self.queue) def push(seq): if isinstance(seq, Iterable): if len(self.queue) + len(seq) > self.max_size: raise Full self.queue = seq else: if len(self.queue) + 1 > self.max_size: raise Full self.queue.append(seq) def pop(): if self.queue: return self.queue.pop(0) 
  3. 创建队列的字典具有MAXSIZE = 2 *所选项目的百分比

就像是

    PCT_SELECTED = 100
    MAXSIZE = 2 * PCT_SELECTED
    KEY_START = 10
    KEY_STOP = 15 
    from collection import defaultdict
    queue_dict = defaultdict(Queue(MAXSIZE))
  1. 放入队列元素在非阻挡FASION
  2. 如果队列已满,它会引发异常Full ,在这种情况下,你随机选择从队列中元素的50%,并放弃休息。

就像是

    with open("your-file") as fin:
        for line in fin:
            key = line[KEY_START: KEY_STOP]
            try:
                queue_dict[key].push(line)
            except Full:
                queue_dict[key] = random.sample(queue_dict[key], PCT_SELECTED)
  1. 通过字典最后迭代,并随机地修剪出队列

     queue_dict = {key: random.sample(value, PCT_SELECTED) for key, value in queue_dict.items()} 
  2. 现在,您可以通过词典]读取和写入文件。



Answer 3:

对于大量项目只是选择75%的可以通过检查每一个随机数来完成。

import random

with open('input') as f:
for line in f:
    if random.random() < 0.75:
        print line

如果你需要从每个键(即使它只有两行)保证至少一个项目:

import random
keys = set()

with open('input') as f:
    for line in f:
        columns = line.split('\t')
        key = columns[0]

        if not key in keys:
           print line
           keys.add(key)
           continue

        if random.random() < 0.75:
            print line


文章来源: text file reduction with randomization in Python