Чтение текстовых файлов в список, а затем хранение в словаре заполняет системную память? (А что я делаю не так?) Python - PullRequest
1 голос
/ 21 февраля 2010

У меня есть 43 текстовых файла, которые занимают «232,2 МБ на диске (232 129 355 байт) для 43 элементов». что читать их в памяти (см. код ниже). Проблема, с которой я сталкиваюсь, заключается в том, что каждый файл размером около 5,3 МБ на диске заставляет Python использовать дополнительные 100 МБ системной памяти. Если проверить размер dict () getsizeof () (см. Пример выходных данных). Когда python занимает до 3 ГБ системной памяти, getizeof (dict ()) использует только 6424 байта памяти. Я не понимаю, что использует память.

Что занимает всю память?

Связанная ссылка отличается тем, что сообщаемое использование памяти python было "правильным" связанный вопрос Меня не очень интересуют другие решения БД .... Меня больше интересует понимание происходящего, поэтому я знаю, как этого избежать в будущем. Тем не менее, использование другого встроенного в Python массива ins, а не списков - отличное предложение, если это поможет. Я слышал предложения об использовании гуппи, чтобы найти то, что использует память.

пример вывода:

Loading into memory: ME49_800.txt
ME49_800.txt has 228484 rows of data
ME49_800.txt has 0 rows of masked data
ME49_800.txt has 198 rows of outliers
ME49_800.txt has 0 modified rows of data
280bytes of memory used for ME49_800.txt
43 files of 43 using 12568 bytes of memory
120

Пример данных:

CellHeader=X    Y   MEAN    STDV    NPIXELS
  0   0 120.0   28.3     25
  1   0 6924.0  1061.7   25
  2   0 105.0   17.4     25

Код:

import csv, os, glob
import sys


def read_data_file(filename):
    reader = csv.reader(open(filename, "U"),delimiter='\t')
    fname = os.path.split(filename)[1]
    data = []
    mask = []
    outliers = []
    modified = []

    maskcount = 0
    outliercount = 0
    modifiedcount = 0

    for row in reader:
        if '[MASKS]' in row:
            maskcount = 1
        if '[OUTLIERS]' in row:
            outliercount = 1
        if '[MODIFIED]' in row:
            modifiedcount = 1
        if row:
            if not any((maskcount, outliercount, modifiedcount)):
                data.append(row)
            elif not any((not maskcount, outliercount, modifiedcount)):
                mask.append(row) 
            elif not any((not maskcount, not outliercount, modifiedcount)):
                outliers.append(row)  
            elif not any((not maskcount, not outliercount, not modifiedcount)):
                modified.append(row)
            else: print '***something went wrong***'

    data = data[1:]
    mask = mask[3:]
    outliers = outliers[3:]
    modified = modified[3:]
    filedata = dict(zip((fname + '_data', fname + '_mask', fname + '_outliers', fname+'_modified'), (data, mask, outliers, modified)))
    return filedata


def ImportDataFrom(folder):

    alldata = dict{}
    infolder = glob.glob( os.path.join(folder, '*.txt') )
    numfiles = len(infolder)
    print 'Importing files from: ', folder
    print 'Importing ' + str(numfiles) + ' files from: ', folder

    for infile in infolder:
        fname = os.path.split(infile)[1]
        print "Loading into memory: " + fname

        filedata = read_data_file(infile)
        alldata.update(filedata)

        print fname + ' has ' + str(len(filedata[fname + '_data'])) + ' rows of data'
        print fname + ' has ' + str(len(filedata[fname + '_mask'])) + ' rows of masked data'
        print fname + ' has ' + str(len(filedata[fname + '_outliers'])) + ' rows of outliers'
        print fname + ' has ' + str(len(filedata[fname +'_modified'])) + ' modified rows of data'
        print str(sys.getsizeof(filedata)) +'bytes'' of memory used for '+ fname
        print str(len(alldata)/4) + ' files of ' + str(numfiles) + ' using ' + str(sys.getsizeof(alldata)) + ' bytes of memory'
        #print alldata.keys()
        print str(sys.getsizeof(ImportDataFrom))
        print ' ' 

    return alldata


ImportDataFrom("/Users/vmd/Dropbox/dna/data/rawdata")

Ответы [ 2 ]

3 голосов
/ 21 февраля 2010

Сам словарь очень мал - большая часть данных представляет собой все содержимое файлов, хранящихся в списках, содержащих по одному кортежу на строку. Увеличение в 20 раз больше, чем я ожидал, но кажется реальным. Разделение 27-байтовой строки из вашего входного примера в кортеж дает мне 309 байт (рекурсивный подсчет на 64-битной машине). Добавьте к этому некоторые неизвестные накладные расходы на выделение памяти, и 20x не является невозможным.

Альтернативы: для более компактного представления вы хотите преобразовать строки в целые числа / числа с плавающей запятой и плотно их упаковать (без всех этих указателей и отдельных объектов). Я говорю не только об одной строке (хотя это и начало), а о целом списке строк вместе - поэтому каждый файл будет представлен всего четырьмя двумерными массивами чисел. Модуль array - это начало, но на самом деле вам нужны numpy массивы:

# Using explicit field types for compactness and access by name
# (e.g. data[i]['mean'] == data[i][2]).
fields = [('x', int), ('y', int), ('mean', float), 
          ('stdv', float), ('npixels', int)]
# The simplest way is to build lists as you do now, and convert them
# to numpy array when done.
data = numpy.array(data, dtype=fields)
mask = numpy.array(mask, dtype=fields)
...

Это дает мне 40 байтов, потраченных на строку (измерено для атрибута .data; sys.getsizeof сообщает, что массив имеет постоянную служебную информацию в 80 байтов, но не видит фактические используемые данные). Это все еще на ~ 1,5 больше, чем исходные файлы, но должно легко помещаться в ОЗУ.

Я вижу, что два ваших поля помечены "x" и "y" - если ваши данные плотные, вы можете упорядочить их по ним - data [x, y] == ... - вместо простого хранения (x , у, ...) записи. Помимо того, что он немного более компактен, он был бы наиболее разумной структурой, позволяющей упростить обработку.

Если вам нужно обрабатывать даже больше данных, чем умещается в ОЗУ, pytables - хорошая библиотека для эффективного доступа к компактным (даже сжатым) табличным данным в файлах. (Это намного лучше, чем обычные базы данных SQL.)

2 голосов
/ 21 февраля 2010

Эта строка определенно получает размер объекта функции:

print str(sys.getsizeof(ImportDataFrom))

вряд ли это то, что вас интересует.

Размер контейнера не включает размер данных, которые он содержит. Рассмотрим, например:

>>> import sys
>>> d={}
>>> sys.getsizeof(d)
140
>>> d['foo'] = 'x'*99
>>> sys.getsizeof(d)
140
>>> d['foo'] = 'x'*9999
>>> sys.getsizeof(d)
140

Если вы хотите размер контейнера плюс размер всех содержащихся вещей, вы должны написать свою собственную (предположительно рекурсивную) функцию, которая достигает контейнеров и копает каждый байт. Или вы можете использовать сторонние библиотеки, такие как Pympler или guppy .

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