Python:动态处理大型文档的行
我有一个文档看起来有点像这样:Python:动态处理大型文档的行,python,readlines,Python,Readlines,我有一个文档看起来有点像这样: key1 value_1_1 value_1_2 value_1_3 etc key2 value_2_1 value_2_2 value_2_3 etc key3 value_3_1 value_3_2 value_3_3 etc etc 其中每个键是一个字符串,每个值是一个浮点数,都用空格分隔。每一行都有数百个与之关联的值,并且有数十万行。每一行都需要以特定的方式进行处理,但由于我的程序只需要一小部分行的信息,因此立即处理每一行似乎是巨大的时间浪费。目前,我
key1 value_1_1 value_1_2 value_1_3 etc
key2 value_2_1 value_2_2 value_2_3 etc
key3 value_3_1 value_3_2 value_3_3 etc
etc
其中每个键
是一个字符串,每个值
是一个浮点数,都用空格分隔。每一行都有数百个与之关联的值,并且有数十万行。每一行都需要以特定的方式进行处理,但由于我的程序只需要一小部分行的信息,因此立即处理每一行似乎是巨大的时间浪费。目前,我只有一个未处理行的列表,并维护一个包含每个键的单独列表。当我需要访问一行时,我将使用键
列表查找我需要的行的索引,然后在行列表中的该索引处处理该行。我的程序可能会要求多次查找同一行,这将导致重复处理同一行,但仍然比从一开始就处理每一行要好
我的问题是,有没有更有效的方法来做我正在做的事情
(如果我需要澄清,请告诉我)
谢谢 首先,我会将您的行存储在目录中。这可能会使基于密钥的查找速度大大加快。制作这个dict可以像d=dict(文件中的行的line.split(“”,1)
一样简单。例如,如果关键帧具有固定的宽度,则只需对线进行切片,就可以进一步加快速度
接下来,如果行处理的计算量很大,可以缓冲结果。我曾经通过将一个dict
子类化来解决这个问题:
class BufferedDict(dict):
def __init__(self, file_obj):
self.file_dict = dict(line.split(' ', 1) for line in file_obj)
def __getitem__(self, key):
if key not in self:
self[key] = process_line(self.file_dict[key])
return super(BufferedDict, self).__getitem__(key)
def process_line(line):
"""Your computationally heavy line processing function"""
这样,如果调用my\u buffered\u dict[key]
,则仅当处理的版本还不可用时,才会处理该行。这里有一个类,它扫描文件并简单地缓存文件偏移量。仅当访问行的键时才处理行\uu getitem\uu
缓存已处理的行
class DataFileDict:
def __init__(self, datafile):
self._index = {}
self._file = datafile
# build index of key-file offsets
loc = self._file.tell()
for line in self._file:
key = line.split(None, 1)[0]
self._index[key] = loc
loc = self._file.tell()
def __getitem__(self, key):
retval = self._index[key]
if isinstance(retval, int):
self._file.seek(retval)
line = self._file.readline()
retval = self._index[key] = list(map(float, line.split()[1:]))
print("read and return value for {} from file".format(key))
else:
print("returning cached value for {}".format(key))
return retval
if __name__ == "__main__":
from io import StringIO
sample = StringIO("""\
A 1 2 3 4 5
B 6 7 8 9 10
C 5 6 7 8 1 2 3 4 5 6 7
""")
reader = DataFileDict(sample))
print(reader['A'])
print(reader['B'])
print(reader['A'])
print(reader['C'])
print(reader['D']) # KeyError
印刷品
read and return value for A from file
[1.0, 2.0, 3.0, 4.0, 5.0]
read and return value for B from file
[6.0, 7.0, 8.0, 9.0, 10.0]
returning cached value for A
[1.0, 2.0, 3.0, 4.0, 5.0]
read and return value for C from file
[5.0, 6.0, 7.0, 8.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]
Traceback (most recent call last):
File "C:/Users/ptmcg/.PyCharm2017.1/config/scratches/scratch.py", line 64, in <module>
print(reader['D']) # KeyError
File "C:/Users/ptmcg/.PyCharm2017.1/config/scratches/scratch.py", line 28, in __getitem__
retval = self._index[key]
KeyError: 'D'
读取并返回来自文件的值
[1.0, 2.0, 3.0, 4.0, 5.0]
从文件中读取并返回B的值
[6.0, 7.0, 8.0, 9.0, 10.0]
返回缓存的值
[1.0, 2.0, 3.0, 4.0, 5.0]
从文件中读取并返回C的值
[5.0, 6.0, 7.0, 8.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]
回溯(最近一次呼叫最后一次):
文件“C:/Users/ptmcg/.PyCharm2017.1/config/scratches/scratch.py”,第64行,在
打印(读卡器['D'])#键错误
文件“C:/Users/ptmcg/.PyCharm2017.1/config/scratches/scratch.py”,第28行,在u getitem中__
retval=自索引[键]
KeyError:'D'
您的dict理解将该行拆分两次,一次用于获取密钥,一次用于从该行中删除密钥以获取值,OP试图避免额外的工作。我想你可以用self.file\u dict={parts[0]:parts[1:]for line in file\u obj for parts in[line.split()]}
来解决这个问题,但这太难看了,我可能只会使用显式for循环。另外,由于您的类扩展了dict,那么代码可能会调用\uuuuuu setitem\uuuuuu
,这对这个应用程序来说并不太合适。关于\uuuuuu setitem\uuuuu
,您的具体观点是什么?谢谢你对理解的建议。你必须去掉line.split()
周围的方括号,才能让你的想法发挥作用,你还必须再次连接parts[1:]
才能将行作为值返回。如果你想让其余的行仍然作为单个字符串连接,那么将其更改为self.file\u dict={parts[0]:parts[1]表示文件中的行\u obj表示文件中的部分[line.split(None,1)]}
,这样您只需进行一次拆分。是的,您仍然需要方括号。但就这一点而言,使用dict构造函数本身而不是扭曲成dict理解可能更为简洁:self.file\u dict=dict(line.split(None,1)表示文件中的行\u obj)
。同意,将dict构造函数与iterable一起使用将是最干净的。我会更新我的答案!但是我仍然不明白你关于\uuuuuuu setitem\uuuuuuu
的观点。键的长度是固定的吗?还是只是一些非空格字符序列?这是一个不可预测的非空格字符序列,因此你不得不使用split()要获取它们,至少要执行line.split(None,1)
,这样您只需完成获取前导键所需的最少工作。