我正在研究文本解析算法(开源方项目)。对于任何建议,我都非常感激。
我有一个制表符分隔的txt文件,该文件按第一列(下面的示例数据集)排序。此列中存在重复条目。 最后,我想使用散列指向所有具有相同键(第一列值)的值。如果出现新密钥,则将对哈希的内容进行序列化,保存等,然后清除该密钥以填充它。因此,我的目标是只有一把钥匙出现。因此,如果我有N个唯一键,我希望N个哈希值分别指向它们各自的条目。数据集虽然是GB的大小和内存堆不会有太大的帮助,因此我的理由是为每个密钥创建一个哈希并逐个处理。
A ... 23.4421 A ... -23.442 A ... 76.2224 B ... 32.1232 B ... -23.001 C ... 652.123 ...
所以在上面的数据集片段中,我希望有一个'A'的哈希值(指向它的3个相应项目)。读取“B”时,序列化“A”哈希并清除哈希内容。重复“B”直到数据集结束。
我的伪代码如下:
declare hash for item in the dataset: key, value = item[0], item[1:] if key not in hash: if hash.size is 0: // pertains to the very first item hash.put(key, value) else: clear hash // if a new key is read but a diff. key is present. else: hash.put(key, value) // key already there so append it.
如果对如何有效实施上述算法存在任何建议,我将非常感激。此外,如果我的哈希推理/方法效率不高或者可以提出改进,我会非常感激。我的目标是最终创建内存中的哈希,直到出现新密钥。
谢谢,
第
答案 0 :(得分:1)
使用itertools.groupby,将文件作为迭代器传递:
from itertools import groupby
from cStringIO import StringIO
sourcedata = StringIO("""\
A ... 23.4421
A ... -23.442
A ... 76.2224
B ... 32.1232
B ... -23.001
C ... 652.123""")
# or sourcedata = open("zillion_gigabyte_file.dat")
for key,lines in groupby(sourcedata, key=lambda s:s.split()[0]):
accum = [float(s.split()[2]) for s in lines]
print key, accum
groupby非常智能和高效,一次只在内存中保存很少的数据,在最后一刻之前保持纯粹的迭代器形式。你所描述的哈希并且一次只保留一个内存中的所有内容,而且已经在groupby中为你完成了。
答案 1 :(得分:0)
您可以为第一列中的每个键打开anydbm(2.x)或dbm(3.x),并以列的值命名。这非常简单 - 我不确定问题是什么。
你也可以使用像我的cachedb模块这样的东西,让它弄清楚某些东西是否是“新的”:http://stromberg.dnsalias.org/~strombrg/cachedb.html我在两个项目中都使用过它,两者都有很好的效果。
无论哪种方式,你都可以使你的密钥只是由换行符或空值或其他东西分隔的ascii浮点数列表。
答案 2 :(得分:0)
您没有明确说明您提供的排序数据是否典型,或者相同的密钥是否可以在整个文件中穿插其他密钥,这确实对算法产生了根本性的影响。我从您的示例代码中推断出它们将以任意顺序出现。
您也没有说您打算如何提取数据。这可能很重要 - 存储数据有许多不同的方法,应用程序可以是确定访问时间的关键功能。因此,您可能需要考虑使用各种不同的存储类型。如果您不知道如何使用最终结构,则以下建议可能不合适。
由于数据是浮点数,因此您可能需要考虑使用the shelve module来维护简化的字符串再次键入的浮点数列表。这具有以下优点:自动处理与外部存储器的所有酸洗和去除液体。如果您需要提高速度,请考虑使用更有效的酸洗协议(shelve.open()
未使用的参数之一)。
# Transform the data:
# note it's actually more efficient to process line-by-line
# as you read it from a file - obviously it's best to try
# to avoid reading the whole data set into memory at once.
data = """\
A ... 23.4421
A ... -23.442
A ... 76.2224
B ... 32.1232
B ... -23.001
C ... 652.123"""
data = [(k, float(v))
for (k, _, v) in
[_.split() for _ in data.splitlines()]]
# create a shelve
import shelve
shelf = shelve.open("myshelf", "c")
# process data
for (k, v) in data:
if k in shelf:
# see note below for rationale
tmp = shelf[k]
tmp.append(v)
shelf[k] = tmp
else:
shelf[k] = [v]
# verify results
for k in shelf.keys():
print k, shelf[k]
您可能想知道为什么我不会在已经看到密钥的情况下使用shelf[k].append(v)
。这是因为只有键分配的操作才会触发检测值的变化。您可以阅读搁置模块文档以获取更多详细信息,并了解如何使用二进制pickle格式。
另请注意,由于shelve.open()
的“c”参数,此程序每次运行时都会重新创建工具架。