我有一个非常大的文件,包含正整数的二维数组。
我想在不将文件读入内存的情况下处理它们。幸运的是,我只需要查看输入文件中从左到右的值。 我希望能够 mmap
每个文件,这样我就可以像处理内存一样处理它们,但不需要实际将文件读入内存。
小版本的例子。
[[2, 2, 6, 10, 2, 6, 7, 15, 14, 10, 17, 14, 7, 14, 15, 7, 17],
[3, 3, 7, 11, 3, 7, 0, 11, 7, 16, 0, 17, 17, 7, 16, 0, 0],
[4, 4, 8, 7, 4, 13, 0, 0, 15, 7, 8, 7, 0, 7, 0, 15, 13],
[5, 5, 9, 12, 5, 14, 7, 13, 9, 14, 16, 12, 13, 14, 7, 16, 7]]
是否可以 mmap
这样的文件,这样我就可以处理 np.int64
值与
for i in range(rownumber):
for j in range(rowlength):
process(M[i, j])
要说明的是,我不希望把所有的输入文件都放在内存中,因为它装不下。
更新答案
根据你的评论和澄清,看来你其实是有了一个 文字 文件,里面有一堆方括号,大约4行,每行有1,000,000,000个ASCII整数,用逗号隔开。这不是一个非常有效的格式! 我建议你简单地对文件进行预处理,去除所有的方括号、换行和空格,并将逗号转换为换行符,这样你就可以轻松地处理每行一个值。
使用 tr
命令来翻译,那就是这个。
# Delete all square brackets, newlines and spaces, change commas into newlines
tr -d '[] \n' < YourFile.txt | tr , '\n' > preprocessed.txt
然后你的文件就会变成这个样子,你可以很容易地在Python中一次处理一个值。
2
2
6
10
2
6
...
...
如果您是在 Windows 上,那么 tr
工具可用于Windows的 GNUWin32
和在Windows子系统的Linux的东西(git bash?
你可以更进一步,做一个文件,你可以 memmap()
就像我答案的第二部分一样,你可以随机找到文件中的任何一个字节。所以,把 preprocessed.txt
上面创建的,你可以做一个这样的二进制版本。
import struct
# Make binary memmapable version
with open('preprocessed.txt', 'r') as ifile, open('preprocessed.bin', 'wb') as ofile:
for line in ifile:
ofile.write(struct.pack('q',int(line)))
原始答案
你可以这样做。第一部分只是设置。
#!/usr/bin/env python3
import numpy as np
# Create 2,4 Numpy array of int64
a = np.arange(8, dtype=np.int64).reshape(2,4)
# Write to file as binary
a.tofile('a.dat')
现在在shell中用十六进制转储的方式检查文件
xxd a.dat
00000000: 0000 0000 0000 0000 0100 0000 0000 0000 ................
00000010: 0200 0000 0000 0000 0300 0000 0000 0000 ................
00000020: 0400 0000 0000 0000 0500 0000 0000 0000 ................
00000030: 0600 0000 0000 0000 0700 0000 0000 0000 ................
现在,我们都准备好了,让我们... memmap()
的文件。
# Memmap file and access values via 'mm'
mm = np.memmap('a.dat', dtype=np.int64, mode='r', shape=(2,4))
print(mm[1,2]) # prints 6
最主要的问题是文件太大,而且似乎也没有分行。(参考你提供的array.txt的例子,arr_map.dat是个空文件)
import re
import numpy as np
N = [str(i) for i in range(10)]
arrayfile = 'array.txt'
mmapfile = 'arr_map.dat'
R = 4
C = 17
CHUNK = 20
def read_by_chunk(file, chunk_size=CHUNK):
return file.read(chunk_size)
fp = np.memmap(mmapfile, dtype=np.uint8, mode='w+', shape=(R,C))
with open(arrayfile,'r') as f:
curr_row = curr_col = 0
while True:
data = read_by_chunk(f)
if not data:
break
# Make sure that chunk reading does not break a number
while data[-1] in N:
data += read_by_chunk(f,1)
# Convert chunk into numpy array
nums = np.array(re.findall(r'[0-9]+', data)).astype(np.uint8)
num_len = len(nums)
if num_len == 0:
break
# CASE 1: Number chunk can fit into current row
if curr_col + num_len <= C:
fp[curr_row, curr_col : curr_col + num_len] = nums
curr_col = curr_col + num_len
# CASE 2: Number chunk has to be split into current and next row
else:
col_remaining = C-curr_col
fp[curr_row, curr_col : C] = nums[:col_remaining] # Fill in row i
curr_row, curr_col = curr_row+1, 0 # Move to row i+1 and fill the rest
fp[curr_row, :num_len-col_remaining] = nums[col_remaining:]
curr_col = num_len-col_remaining
if curr_col>=C:
curr_col = curr_col%C
curr_row += 1
#print('\n--debug--\n',fp,'\n--debug--\n')
基本上,每次读取数组文件的一小部分(确保不要破坏数字),用regex从逗号、括号等垃圾字符中找到数字,然后将数字插入到内存映射中。
你描述的情况似乎更适合用一个生成器,从文件中获取下一个整数,或者下一行,然后让你进行处理。
def sanify(s):
while s.startswith('['):
s = s[1:]
while s.endswith(']'):
s = s[:-1]
return int(s)
def get_numbers(file_obj):
file_obj.seek(0)
i = j = 0
for line in file_obj:
for item in line.split(', '):
if item and not item.isspace():
yield sanify(item), i, j
j += 1
i += 1
j = 0
这样可以保证每次只有一行在内存中驻留。
这可以像这样使用。
import io
s = '''[[2, 2, 6, 10, 2, 6, 7, 15, 14, 10, 17, 14, 7, 14, 15, 7, 17],
[3, 3, 7, 11, 3, 7, 0, 11, 7, 16, 0, 17, 17, 7, 16, 0, 0],
[4, 4, 8, 7, 4, 13, 0, 0, 15, 7, 8, 7, 0, 7, 0, 15, 13],
[5, 5, 9, 12, 5, 14, 7, 13, 9, 14, 16, 12, 13, 14, 7, 16, 7]]'''
items = get_numbers(io.StringIO(s))
for item, i, j in items:
print(item, i, j)
如果你真的想访问矩阵中的任意元素 你可以把上面的逻辑改编成一个类来实现。__getitem__
而你只需要跟踪每行开头的位置。在代码中,这看起来像。
class MatrixData(object):
def __init__(self, file_obj):
self._file_obj = file_obj
self._line_offsets = list(self._get_line_offsets(file_obj))[:-1]
file_obj.seek(0)
row = list(self._read_row(file_obj.readline()))
self.shape = len(self._line_offsets), len(row)
self.length = self.shape[0] * self.shape[1]
def __len__(self):
return self.length
def __iter__(self):
self._file_obj.seek(0)
i = j = 0
for line in self._file_obj:
for item in _read_row(line):
yield item, i, j
j += 1
i += 1
j = 0
def __getitem__(self, indices):
i, j = indices
self._file_obj.seek(self._line_offsets[i])
line = self._file_obj.readline()
row = self._read_row(line)
return row[j]
@staticmethod
def _get_line_offsets(file_obj):
file_obj.seek(0)
yield file_obj.tell()
for line in file_obj:
yield file_obj.tell()
@staticmethod
def _read_row(line):
for item in line.split(', '):
if item and not item.isspace():
yield MatrixData._sanify(item)
@staticmethod
def _sanify(item, dtype=int):
while item.startswith('['):
item = item[1:]
while item.endswith(']'):
item = item[:-1]
return dtype(item)
class MatrixData(object):
def __init__(self, file_obj):
self._file_obj = file_obj
self._line_offsets = list(self._get_line_offsets(file_obj))[:-1]
file_obj.seek(0)
row = list(self._read_row(file_obj.readline()))
self.shape = len(self._line_offsets), len(row)
self.length = self.shape[0] * self.shape[1]
def __len__(self):
return self.length
def __iter__(self):
self._file_obj.seek(0)
i = j = 0
for line in self._file_obj:
for item in self._read_row(line):
yield item, i, j
j += 1
i += 1
j = 0
def __getitem__(self, indices):
i, j = indices
self._file_obj.seek(self._line_offsets[i])
line = self._file_obj.readline()
row = list(self._read_row(line))
return row[j]
@staticmethod
def _get_line_offsets(file_obj):
file_obj.seek(0)
yield file_obj.tell()
for line in file_obj:
yield file_obj.tell()
@staticmethod
def _read_row(line):
for item in line.split(', '):
if item and not item.isspace():
yield MatrixData._sanify(item)
@staticmethod
def _sanify(item, dtype=int):
while item.startswith('['):
item = item[1:]
while item.endswith(']'):
item = item[:-1]
return dtype(item)
用作:
m = MatrixData(io.StringIO(s))
# get total number of elements
len(m)
# get number of row and col
m.shape
# access a specific element
m[3, 12]
# iterate through
for x, i, j in m:
...
这似乎正是 mmap
模块在python中的作用。参见 https:/docs.python.org3librarymmap.html)。
文件中的例子
import mmap
# write a simple example file
with open("hello.txt", "wb") as f:
f.write(b"Hello Python!\n")
with open("hello.txt", "r+b") as f:
# memory-map the file, size 0 means whole file
mm = mmap.mmap(f.fileno(), 0)
# read content via standard file methods
print(mm.readline()) # prints b"Hello Python!\n"
# read content via slice notation
print(mm[:5]) # prints b"Hello"
# update content using slice notation;
# note that new content must have same size
mm[6:] = b" world!\n"
# ... and read again using standard file methods
mm.seek(0)
print(mm.readline()) # prints b"Hello world!\n"
# close the map
mm.close()
这取决于你想对你的输入矩阵进行的操作,如果是矩阵操作,那么你可以用一个 偏矩阵在大多数情况下,你能够部分处理小批量的输入文件,如 偏矩阵通过这种方式,你可以非常高效地处理文件,你只需要开发你的算法来读取和部分处理输入并缓存结果,对于一些操作,你可能只需要决定什么是你的输入矩阵的最佳表示方式(即 行大 或 纵队).
使用部分矩阵方法的主要优点是,你可以利用应用并行处理技术的优势来处理。n 偏矩阵 在每一次迭代中使用 CUDA GPU 例如,如果你熟悉 C 或 C++,然后用 Python C API 可能会大大改善部分矩阵运算的时间复杂度,但即使使用 蟒蛇 不会太差,因为你只需要处理你的。偏矩阵 使用 Numpy.