遍历openpyxl中只读工作簿中的列

Jon*_*ona 5 python excel openpyxl

我有一个较大的.xlsx文件-19列5185行。我想打开文件,读取一列中的所有值,对这些值做一些处理,然后在同一工作簿中创建一个新列,并写出修改后的值。因此,我需要能够在同一文件中进行读写。

我的原始代码这样做:

def readExcel(doc):
    wb = load_workbook(generalpath + exppath + doc)
    ws = wb["Sheet1"]

    # iterate through the columns to find the correct one
    for col in ws.iter_cols(min_row=1, max_row=1):
        for mycell in col:
            if mycell.value == "PerceivedSound.RESP":
                origCol = mycell.column

    # get the column letter for the first empty column to output the new values
    newCol = utils.get_column_letter(ws.max_column+1)

    # iterate through the rows to get the value from the original column,
    # do something to that value, and output it in the new column
    for myrow in range(2, ws.max_row+1):
        myrow = str(myrow)
        # do some stuff to make the new value
        cleanedResp = doStuff(ws[origCol + myrow].value)
        ws[newCol + myrow] = cleanedResp

    wb.save(doc)
Run Code Online (Sandbox Code Playgroud)

但是,由于工作簿太大,python在第3853行之后引发了内存错误。据说openpyxl文档使用只读模式(https://openpyxl.readthedocs.io/en/latest/optimized.html)处理大型工作簿。我现在正在尝试使用它;但是,当我添加read_only = True参数时,似乎没有办法遍历各列:

def readExcel(doc):
    wb = load_workbook(generalpath + exppath + doc, read_only=True)
    ws = wb["Sheet1"]

    for col in ws.iter_cols(min_row=1, max_row=1):
        #etc.
Run Code Online (Sandbox Code Playgroud)

python抛出此错误: AttributeError:'ReadOnlyWorksheet'对象没有属性'iter_cols'

如果我将以上代码段的最后一行更改为:

for col in ws.columns:
Run Code Online (Sandbox Code Playgroud)

python抛出相同的错误: AttributeError:'ReadOnlyWorksheet'对象没有属性'columns'

遍历行很好(并且包含在我上面链接的文档中):

for col in ws.rows:
Run Code Online (Sandbox Code Playgroud)

(没有错误)

这个问题询问有关AttritubeError的问题,但是解决方案是删除只读模式,这对我不起作用,因为openpyxl不会在非只读模式下读取我的整个工作簿。

那么:如何遍历大型工作簿中的列?

而且我还没有遇到过,但是我将遍历各列:如果说工作簿很大,我该如何读写同一份工作簿?

谢谢!

Cha*_*ark 8

如果工作表只有大约 100,000 个单元格,那么您应该不会有任何内存问题。您可能应该进一步调查这一点。

iter_cols()在只读模式下不可用,因为它需要不断且非常低效地重新解析底层 XML 文件。iter_rows()然而,通过使用将行转换为列相对容易zip

def _iter_cols(self, min_col=None, max_col=None, min_row=None,
               max_row=None, values_only=False):
    yield from zip(*self.iter_rows(
        min_row=min_row, max_row=max_row,
        min_col=min_col, max_col=max_col, values_only=values_only))

import types
for sheet in workbook:
    sheet.iter_cols = types.MethodType(_iter_cols, sheet)
Run Code Online (Sandbox Code Playgroud)


cow*_*ert 3

根据文档,ReadOnly模式仅支持基于行的读取(未实现列读取)。但这并不难解决:

wb2 = Workbook(write_only=True)
ws2 = wb2.create_sheet()

# find what column I need
colcounter = 0
for row in ws.rows:
    for cell in row:
        if cell.value == "PerceivedSound.RESP":
            break
        colcounter += 1
    
    # cells are apparently linked to the parent workbook meta
    # this will retain only values; you'll need custom
    # row constructor if you want to retain more

    row2 = [cell.value for cell in row]
    ws2.append(row2) # preserve the first row in the new file
    break # stop after first row

for row in ws.rows:
    row2 = [cell.value for cell in row]
    row2.append(doStuff(row2[colcounter]))
    ws2.append(row2) # write a new row to the new wb
    
wb2.save('newfile.xlsx')
wb.close()
wb2.close()

# copy `newfile.xlsx` to `generalpath + exppath + doc`
# Either using os.system,subprocess.popen, or shutil.copy2()
Run Code Online (Sandbox Code Playgroud)

您将无法写入同一工作簿,但如上所示,您可以打开一个新工作簿(以只写模式),对其进行写入,然后使用操作系统复制覆盖旧文件。