У меня есть большой сжатый файл (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