Ошибка ОЗУ большого массива - PullRequest
2 голосов
/ 12 марта 2010

Мне нужно получить номера одной строки случайным образом и поместить каждую строку в другой массив, а затем получить числа в одну строку.

У меня большой файл, больше 400M. В этом файле 13496 * 13496, что означает 13496 строк и 13496 столбцов. Я хочу прочитать их в массив. Это мой код:

_L1 = [[0 for col in range(13496)] for row in range(13496)]
_L1file = open('distanceCMD.function.txt')
while (i<13496):
    print "i="+str(i)
    _strlf = _L1file.readline()
    _strlf = _strlf.split('\t')
    _strlf = _strlf[:-1]
    _L1[i] = _strlf
    i += 1
_L1file.close()

А это мое сообщение об ошибке:

MemoryError:
File "D:\research\space-function\ART3.py", line 30, in <module>
  _strlf = _strlf.split('\t')

Ответы [ 5 ]

7 голосов
/ 12 марта 2010

вы можете подойти к вашей проблеме по-другому. Обрабатывать файл построчно. Я не вижу необходимости хранить весь большой файл в массиве. В противном случае вы можете сообщить нам, что вы на самом деле пытаетесь сделать.

for line in open("400MB_file"):
     # do something with line.

или

f=open("file")
for linenum,line in enumerate(f):
    if linenum+1 in [2,3,10]:
         print "there are ", len(line.split())," columns" #assuming you want to split on spaces
         print "100th column value is: ", line.split()[99]
    if linenum+1>10:
         break # break if you want to stop after the 10th line
f.close()
3 голосов
/ 12 марта 2010

Краткий ответ: накладные расходы объекта Python убивают вас. В Python 2.x на 64-битной машине список строк потребляет 48 байтов на элемент списка даже до учета содержимого строк. Это превышает 8,7 Гб на размер массива, который вы описываете. На 32-битной машине это будет немного лучше: только 28 байтов на запись списка.

Более длинное объяснение: вы должны знать, что сами объекты Python могут быть довольно большими: даже простые объекты, такие как int, float и string. В вашем коде вы получаете список списков строк. На моей (64-битной) машине даже пустой строковый объект занимает 40 байтов, и к этому вам нужно добавить 8 байтов для указателя списка, который указывает на этот строковый объект в памяти. Так что это уже 48 байтов на запись, или около 8,7 Гб. Учитывая, что Python выделяет память в количестве, кратном 8 байтам за раз, и что ваши строки почти наверняка непусты, вы фактически просматриваете 56 или 64 байта (я не знаю, какова длина ваших строк) для каждой записи.

Возможные решения:

(1) Вы могли бы (немного) сделать лучше, преобразовав свои записи из строк в целые или плавающие в зависимости от ситуации.

(2) Вы бы сделали намного лучше, используя тип Python array (не совпадающий со списком!) Или numpy : затем Ваши целые числа или числа с плавающей запятой будут занимать всего 4 или 8 байт каждый.

Начиная с Python 2.6, вы можете получить основную информацию о размерах объектов с помощью функции sys.getsizeof. Обратите внимание, что если вы примените его к списку (или другому контейнеру), тогда возвращаемый размер не будет включать размер содержащихся объектов списка; только структура, используемая для хранения этих объектов. Вот некоторые значения на моей машине.

>>> import sys
>>> sys.getsizeof("")
40
>>> sys.getsizeof(5.0)
24
>>> sys.getsizeof(5)
24
>>> sys.getsizeof([])
72
>>> sys.getsizeof(range(10))  # 72 + 8 bytes for each pointer
152
3 голосов
/ 12 марта 2010

Это простой случай, когда ваша программа требует больше памяти, чем доступно компьютеру. Массив из 13496x13496 элементов требует 182,142,016 «ячеек», где ячейка - это минимум один байт (если хранятся символы) и потенциально несколько байтов (например, если хранятся числа с плавающей запятой). Я даже не принимаю во внимание метаданные массива вашей конкретной среды выполнения, хотя это обычно будет крошечные издержки для простого массива.

Предполагая, что каждый элемент массива представляет собой всего один байт, вашему компьютеру требуется около 180 МБ ОЗУ, чтобы полностью сохранить его в памяти. Попытка обработать это может быть нецелесообразно.

Вы должны думать о проблеме по-другому; как уже упоминалось, лучше подходить построчно. Или, возможно, обработка сетки в меньших единицах, возможно, 10x10 или 100x100, и агрегирование результатов. Или, может быть, сама проблема может быть выражена в другой форме, что избавляет от необходимости обрабатывать весь набор данных в целом ...?

Если вы дадите нам немного больше информации о характере данных и цели, возможно, у кого-то возникнет идея сделать задачу более управляемой.

0 голосов
/ 12 марта 2010

Заменить это:

_strlf = _strlf[:-1]

с этим:

_strlf = [float(val) for val in _strlf[:-1]]

Вы создаете большой массив строк. Я могу гарантировать, что строка "123.00123214213" занимает намного меньше памяти при преобразовании ее в число с плавающей запятой.

Возможно, вы захотите включить некоторую обработку для нулевых значений.

Вы также можете перейти к типу массива numpy, но ваша проблема может быть слишком маленькой, чтобы беспокоить.

0 голосов
/ 12 марта 2010

MemoryError исключение:

Поднимается , когда операция заканчивается память но ситуация все еще может быть спас (удалив некоторые объекты). Связанное значение является строкой с указанием, какой (внутренний) операция исчерпала память. Обратите внимание, что из-за основной памяти архитектура управления (C malloc () функция), переводчик не может всегда быть в состоянии полностью восстановиться из этой ситуации; это тем не менее вызывает исключение, чтобы стек traceback может быть распечатан, если причиной была побег-программа.

Кажется, что, по крайней мере, в вашем случае, чтение всего файла в память не выполнимо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...