2017-02-23 89 views
-1

Я очень новичок в Python и имею идею, как оптимизировать эту программу, чтобы избежать ошибки памяти.Оптимизация программы преобразования данных во избежание ошибки памяти

Я пытаюсь прочитать данные из двух книг: raw_data и mapping. Я хочу преобразовать raw_data в новую электронную таблицу, используя документ сопоставления для преобразования данных. Поэтому я загружаю книги, создаю словари данных из данных сопоставления и начинаю конвертировать. Однако я столкнулся с ошибкой памяти.

Есть ли способ оптимизации кода ниже, чтобы избежать этой ошибки?

import openpyxl 
from openpyxl.utils import get_column_letter 

mapping = openpyxl.load_workbook(r'C:...\mapping.xlsx') #load mapping doc 
wb = openpyxl.load_workbook(r'C:...\raw_data.xlsx') #load raw data 


sheet = wb.active #look at the active sheet in the raw data file 
user_map_raw = mapping.get_sheet_by_name('User ID Mapping') #for user ids 
item_map_raw = mapping.get_sheet_by_name('Item ID Mapping') #for item ids 
...other mappings here 

def load(sheet): 

    user_dict = {} 
    print "creating user dictionary..." 
    for row in range(1, user_map_raw.max_row+1): 
     old_name = user_map_raw['A' + str(row)].value #old user name 
     new_name = user_map_raw['B' + str(row)].value #new user name 
     user_dict[old_name] = new_name #old name is key for the new name 

    item_dict = {} 
    print "creating item id dictionary..." 
    for row in range(1, item_map_raw.max_row+1): 
     old_item = item_map_raw['A' + str(row)].value #old item id 
     new_item = item_map_raw['B' + str(row)].value #new item id 
     item_dict[old_item] = new_item #old item id is key for new item id 

    raw = [] #empty list to store data before writing to new file 
    for row in range(2, sheet.max_row+1): #loop thru raw data and map 
     print "loading row %s" % row 
     user_ID = user_dict[sheet['A' + str(row)].value] 
     item_type = sheet['B' + str(row)].value 
     item_ID = item_dict[sheet['C' + str(row)].value] 
     ...other transformations here 
     add = [user_ID, item_type, item_ID, ...] 
     raw.append(add) #add transformed data to list 

    new = openpyxl.Workbook() #create new workbook 
    output = new.active #select the active sheet 
    for i in range(len(raw)): #loop through transformed data list 
     "print writing row %s" %i 
     for j in range(len(raw[i])): #write to new sheet 
      output[get_column_letter(j+1) + str(i+1)] = raw[i][j] 
    new.save('new_doc.xlsx') 

load(sheet) 

ответ

1

Первичная оптимизация было бы избежать загрузки всей первичной рабочей книги в память, а также избежать хранения всего результата в памяти перед записью. Существуют write_only и read_only режимы for openpyxl книги, которые могут сэкономить много памяти во время выполнения, реализовав оптимизированные представления с уменьшенными функциями и поддержкой итераторов. Поскольку вы пишете новый файл вместо редактирования на месте, эти режимы могут иметь большое значение.

wb = openpyxl.load_workbook(r'C:...\raw_data.xlsx', read_only=True) 
sheet = wb.active 

# mapping related code... 

from openpyxl.writer.write_only import WriteOnlyCell 
wb = openpyxl.Workbook(write_only=True) #create new workbook 
ws = new.create_sheet() 

for row in sheet.iter_rows(row_offset=1): 
    for i, cell in enumerate(row): 
     if i = 0: #A 
      user_ID = WriteOnlyCell(ws, user_dict[cell.value]) 
     elif i = 1: #B 
      item_type = WriteOnlyCell(ws, cell.value) 
     elif i = 2: #C 
      item_ID = WriteOnlyCell(ws, item_dict[cell.value]) 
     else: 
      break 
    ws.append([user_ID, item_type, item_ID]) 

wb.save('new_doc.xlsx') 

Имейте итерацию по ячейкам, так как это генератор, поэтому нельзя использовать индекс. Кажется неуклюжим, но я устал.

Для небольшой экономии, если вы используете Python 2.x, всякий раз, когда вы используете функцию range, список создается в памяти с таким же большим, как ваш диапазон, который, если у вас очень большая электронная таблица, может заполнить ваш ОЗУ. В вашем случае вы, вероятно, можете использовать xrange, который динамически генерирует каждую итерацию для сохранения некоторой памяти.

+0

Привет, спасибо - это работает. Мой единственный вопрос: почему вы использовали row_offset = 2? – kbball

+0

В примере вопроса вы просматриваете основной лист, пропуская пару строк и начиная со строки 2: 'range (2, sheet.max_row + 1):'. Надеюсь, что 'row_offset' делает то же самое. Трудно сказать из документов openpyxl. Вам может потребоваться проверить, что вы получаете все нужные строки. – systemjack

+0

Правильно, я думаю, это должно быть row_offset = 1. Но в любом случае теперь работаем. Спасибо за все ваши усилия. Key takeaways: прочитайте файл как доступный только для чтения и напишите его как только для записи. Используйте iter_rows для извлечения значений ячейки – kbball

0

Вы можете использовать режим read-only при чтении исходных файлов и write-only режим для записи результатов. Это минимизирует использование памяти.