如何从文本文件中获取一个二维数组的mmap?

问题描述 投票:0回答:1

我有一个非常大的文件,包含正整数的二维数组。

  • 每个文件包含一个矩阵

我想在不将文件读入内存的情况下处理它们。幸运的是,我只需要查看输入文件中从左到右的值。 我希望能够 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])

要说明的是,我不希望把所有的输入文件都放在内存中,因为它装不下。

python numpy mmap
1个回答
4
投票

更新答案

根据你的评论和澄清,看来你其实是有了一个 文字 文件,里面有一堆方括号,大约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

2
投票

最主要的问题是文件太大,而且似乎也没有分行。(参考你提供的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从逗号、括号等垃圾字符中找到数字,然后将数字插入到内存映射中。


2
投票

你描述的情况似乎更适合用一个生成器,从文件中获取下一个整数,或者下一行,然后让你进行处理。

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:
    ...

1
投票

这似乎正是 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()

1
投票

这取决于你想对你的输入矩阵进行的操作,如果是矩阵操作,那么你可以用一个 偏矩阵在大多数情况下,你能够部分处理小批量的输入文件,如 偏矩阵通过这种方式,你可以非常高效地处理文件,你只需要开发你的算法来读取和部分处理输入并缓存结果,对于一些操作,你可能只需要决定什么是你的输入矩阵的最佳表示方式(即 行大纵队).

使用部分矩阵方法的主要优点是,你可以利用应用并行处理技术的优势来处理。n 偏矩阵 在每一次迭代中使用 CUDA GPU 例如,如果你熟悉 CC++,然后用 Python C API 可能会大大改善部分矩阵运算的时间复杂度,但即使使用 蟒蛇 不会太差,因为你只需要处理你的。偏矩阵 使用 Numpy.

© www.soinside.com 2019 - 2024. All rights reserved.