优化数据转换程序以避免内存错误

时间:2017-02-23 03:08:11

标签: python list dictionary openpyxl

我是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)

2 个答案:

答案 0 :(得分:1)

主要优化是避免将整个主工作簿加载到内存中,以及避免在写入之前将整个结果存储在内存中。有write_onlyread_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函数,都会在内存中创建一个与您的范围一样大的列表,如果您有一个非常大的电子表格,则可以填满你的RAM。在您的情况下,您可以使用xrange动态生成每次迭代以节省一些内存。

答案 1 :(得分:1)

您可以在阅读源文件时使用read-only模式,并使用write-only模式来编写结果。这将最大限度地减少内存使用。