我在bash中解决了以下问题,但考虑到我需要减少的文件大小,我觉得它效率很低而且非常慢。希望有人知道如何在Python中做同样的事情并希望加快速度。
最初的问题是减少非常大的文本文件(50-60万行,制表符分隔列)。 其中一列被视为一个键,即我们确定文件中有多少行具有唯一键,然后随机选择它们的百分比(例如,如果减少75%,则为总数的四分之一)以附加到一个新文件,将保持我们的结果。我们继续检查其余的密钥,随机化,然后将包含每个唯一密钥的所有行减少相同的百分比。如果无法进行缩减 - 我们只需将所有行传送到生成的文件中。
正如我所说,我的bash脚本工作得很好,但它很慢并且将各种awk和grep构造串起来。从各方面来看,Python应该以更优雅的方式处理这个问题而不会过多地损害内存(同样,在这种情况下我们处理的是5000多万行文件)。 任何建议/技巧都会有所帮助!谢谢!
答案 0 :(得分:2)
简单的解决方案是按键列对文件进行排序,例如,sort第二列的{{3}}制表符分隔输入:
#!/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上的排序(通过键列)输入,并将减少的输出打印到stdout。它一次只需要在内存中存储一行。它是单程算法(O(n))。
答案 1 :(得分:1)
因为你的问题很模糊,我会给出一个高水平的解决方案
fileObj.read()
或fileObj.readlines()
中的整个文件,而是遍历文件for line in fileObj
。为什么?这将是记忆油炸根据List
创建Queue实现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)
创建一个队列字典,其中maxsize = 2 *所选项目的百分比
像
这样的东西 PCT_SELECTED = 100
MAXSIZE = 2 * PCT_SELECTED
KEY_START = 10
KEY_STOP = 15
from collection import defaultdict
queue_dict = defaultdict(Queue(MAXSIZE))
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)
最后遍历字典并随机删除队列
queue_dict = {key: random.sample(value, PCT_SELECTED) for key, value in queue_dict.items()}
现在您可以阅读dictinary并写入文件。
答案 2 :(得分:0)
对于大量项目,只需选择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