Транспонировать большой массив без загрузки в памятьPython

Программы на Python
Ответить
Anonymous
 Транспонировать большой массив без загрузки в память

Сообщение Anonymous »

У меня есть большой файл в формате gzip (5000 столбцов × 1 млн строк), состоящий из 0 и 1:
0 1 1 0 0 0 1 1 1....(×5000)
0 0 0 1 0 1 1 0 0
....(×1M)

Я хочу ее транспонировать, но использование numpy или других методов просто загружает всю таблицу в ОЗУ, а в моем распоряжении всего 6 ГБ.
По этой причине я хотел использовать метод, который записывает каждую транспонированную строку в открытый файл, а не сохраняет ее в ОЗУ. Я придумал следующий код:
import gzip

with open("output.txt", "w") as out:

with gzip.open("file.txt", "rt") as file:

number_of_columns = len(file.readline().split())

# iterate over number of columns (~5000)
for column in range(number_of_columns):

# in each iteration, go to the top line to start again
file.seek(0)

# initiate list storing the ith column's elements that will form the transposed column
transposed_column = []

# iterate over lines (~1M), storing the ith element in the list
for line in file:
transposed_column.append(line.split()[column])

# write the transposed column as a line to an existing file and back again
out.write(" ".join(transposed_column) + "\n")

Однако это очень медленно. Может ли кто-нибудь предложить мне другое решение? Есть ли способ добавить список в виде столбца (а не строки) в существующий открытый файл? (псевдокод):
with open("output.txt", w) as out:
with gzip.open("file.txt", rt) as file:
for line in file:
transposed_line = line.transpose()
out.write(transposed_line, as.column)


ОБНОВЛЕНИЕ
Ответ пользователя 7813790 привел меня к этому коду:
import numpy as np
import random

# create example array and write to file

with open("array.txt", "w") as out:

num_columns = 8
num_lines = 24

for i in range(num_lines):
line = []
for column in range(num_columns):
line.append(str(random.choice([0,1])))
out.write(" ".join(line) + "\n")

# iterate over chunks of dimensions num_columns×num_columns, transpose them, and append to file

with open("array.txt", "r") as array:

with open("transposed_array.txt", "w") as out:

for chunk_start in range(0, num_lines, num_columns):

# get chunk and transpose
chunk = np.genfromtxt(array, max_rows=num_columns, dtype=int).T
# write out chunk
out.seek(chunk_start+num_columns, 0)
np.savetxt(out, chunk, fmt="%s", delimiter=' ', newline='\n')

Это принимает матрицу типа:
0 0 0 1 1 0 0 0
0 1 1 0 1 1 0 1
0 1 1 0 1 1 0 0
1 0 0 0 0 1 0 1
1 1 0 0 0 1 0 1
0 0 1 1 0 0 1 0
0 0 1 1 1 1 1 0
1 1 1 1 1 0 1 1
0 1 1 0 1 1 1 0
1 1 0 1 1 0 0 0
1 1 0 1 1 0 1 1
1 0 0 1 1 0 1 0
0 1 0 1 0 1 0 0
0 0 1 0 0 1 0 0
1 1 1 0 0 1 1 1
1 0 0 0 0 0 0 0
0 1 1 1 1 1 1 1
1 1 1 1 0 1 0 1
1 0 1 1 1 0 0 0
0 1 0 1 1 1 1 1
1 1 1 1 1 1 0 1
0 0 1 1 0 1 1 1
0 1 1 0 1 1 0 1
0 0 1 0 1 1 0 1

и перебирает 2D-фрагменты, оба размера которых равны количеству столбцов (в данном случае 8), транспонируя их и добавляя в выходной файл.
1-й фрагмент транспонируется:
[[0 0 0 1 1 0 0 1]
[0 1 1 0 1 0 0 1]
[0 1 1 0 0 1 1 1]
[1 0 0 0 0 1 1 1]
[1 1 1 0 0 0 1 1]
[0 1 1 1 1 0 1 0]
[0 0 0 0 0 1 1 1]
[0 1 0 1 1 0 0 1]]

2-й фрагмент транспонирован:
[[0 1 1 1 0 0 1 1]
[1 1 1 0 1 0 1 0]
[1 0 0 0 0 1 1 0]
[0 1 1 1 1 0 0 0]
[1 1 1 1 0 0 0 0]
[1 0 0 0 1 1 1 0]
[1 0 1 1 0 0 1 0]
[0 0 1 0 0 0 1 0]]

и т. д.
Я пытаюсь добавить каждый новый фрагмент в выходной файл в виде столбцов, используя out.seek(). Насколько я понимаю, seek() принимает в качестве первого аргумента смещение от начала файла (т.е. столбца), а 0 в качестве второго аргумента означает, что нужно снова начать с первой строки. Итак, я бы предположил, что следующая строка поможет:
out.seek(chunk_start+num_columns, 0)

Но вместо этого он не продолжается с этим смещением в следующих строках. Кроме того, он добавляет n = num_columns пробелов в начале первой строки. Вывод:
0 0 0 1 0 1 1 1 0 1 1 0 1 0 0 0
1 1 0 1 1 0 1 0
1 1 1 0 1 1 1 1
1 1 1 1 1 1 0 0
1 0 1 1 1 0 1 1
1 1 0 1 1 1 1 1
1 0 0 1 0 1 0 0
1 1 0 1 1 1 1 1

Есть какие-нибудь идеи о том, как правильно использовать Seek() для этой задачи? то есть сгенерировать это:
0 0 0 1 1 0 0 1 0 1 1 1 0 0 1 1 0 1 1 0 1 0 0 0
0 1 1 0 1 0 0 1 1 1 1 0 1 0 1 0 1 1 0 1 1 0 1 0
0 1 1 0 0 1 1 1 1 0 0 0 0 1 1 0 1 1 1 0 1 1 1 1
1 0 0 0 0 1 1 1 0 1 1 1 1 0 0 0 1 1 1 1 1 1 0 0
1 1 1 0 0 0 1 1 1 1 1 1 0 0 0 0 1 0 1 1 1 0 1 1
0 1 1 1 1 0 1 0 1 0 0 0 1 1 1 0 1 1 0 1 1 1 1 1
0 0 0 0 0 1 1 1 1 0 1 1 0 0 1 0 1 0 0 1 0 1 0 0
0 1 0 1 1 0 0 1 0 0 1 0 0 0 1 0 1 1 0 1 1 1 1 1

Обратите внимание, что это всего лишь фиктивная тестовая матрица, фактическая матрица составляет 5008 столбцов × >1M строк.

ОБНОВЛЕНИЕ 2
Я понял, как заставить эту работу работать, она также может использовать фрагменты любых размеров.
import numpy as np
import random

# create example array and write to file

num_columns = 4
num_lines = 8

with open("array.txt", "w") as out:
for i in range(num_lines):
line = []
for column in range(num_columns):
line.append(str(random.choice([0,1])))
out.write(" ".join(line) + "\n")

# iterate over chunks of dimensions num_columns×chunk_length, transpose them, and append to file

chunk_length = 7

with open("array.txt", "r") as array:

with open("transposed_array.txt", "w") as out:

for chunk_start in range(0, num_lines, chunk_length):

# get chunk and transpose
chunk = np.genfromtxt(array, max_rows=chunk_length, dtype=str).T

# write out chunk
empty_line = 2 * (num_lines - (chunk_length + chunk_start))

for i, line in enumerate(chunk):
new_pos = 2 * num_lines * i + 2 * chunk_start
out.seek(new_pos)
out.write(f"{' '.join(line)}{' ' * (empty_line)}"'\n')

В данном случае он принимает такой массив:
1 1 0 1
0 0 1 0
0 1 1 0
1 1 1 0
0 0 0 1
1 1 0 0
0 1 1 0
0 1 1 1

и транспонирует его, используя фрагменты по 4 столбца × 7 строк, поэтому первый фрагмент будет
1 0 0 1 0 1 0
1 0 1 1 0 1 1
0 1 1 1 0 0 1
1 0 0 0 1 0 0

он ​​записывается в файл, удаляется из памяти, а затем создается второй фрагмент.
0
1
1
1

и снова добавляется в файл, поэтому окончательный результат:
1 0 0 1 0 1 0 0
1 0 1 1 0 1 1 1
0 1 1 1 0 0 1 1
1 0 0 0 1 0 0 1


Подробнее здесь: https://stackoverflow.com/questions/567 ... nto-memory
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»