web-dev-qa-db-ja.com

openpyxlの読み取り専用ワークブックの列を反復処理します

やや大きい.xlsxファイルがあります-19列、5185行。ファイルを開き、1つの列のすべての値を読み取り、それらの値に対していくつかの処理を行ってから、同じブックに新しい列を作成し、変更した値を書き出したいと思います。したがって、同じファイルで読み取りと書き込みの両方ができる必要があります。

私の元のコードはこれを行いました:

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)

ただし、pythonブックが大きすぎるため、行3853の後にメモリエラーがスローされました。openpyxlドキュメントは読み取り専用モードを使用すると述べています( https://openpyxl.readthedocs.io /en/latest/optimized.html )大きなワークブックを処理するために、これを使用しようとしていますが、read_only = True paramを追加すると、列を反復処理する方法がないようです。

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.

pythonはこのエラーをスローします:AttributeError: 'ReadOnlyWorksheet'オブジェクトには属性がありません 'iter_cols'

上記のスニペットの最後の行を次のように変更した場合:

for col in ws.columns:

pythonは同じエラーをスローします:AttributeError: 'ReadOnlyWorksheet'オブジェクトには属性がありません 'columns'

行を反復処理することは問題ありません(上記でリンクしたドキュメントに含まれています)。

for col in ws.rows:

(エラーなし)

この質問 AttritubeErrorについて質問しますが、解決策は読み取り専用モードを削除することです。これは、openpyxlが読み取り専用モードではないブック全体を読み取らないため、機能しません。

だから:大きなワークブックの列を反復処理するにはどうすればよいですか?

そして、私はまだこれに遭遇していませんが、列を繰り返すことができたら、同じワークブックを読み書きする方法を教えてください。ワークブックが大きい場合はどうすればよいですか?

ありがとう!

6
Jona

documentation によると、読み取り専用モードは行ベースの読み取りのみをサポートします(列の読み取りは実装されていません)。しかし、それを解決するのは難しいことではありません。

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

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()

同じワークブックに書き込むことはできませんが、上記のように、新しいワークブックを(書き込み専用モードで)開いて書き込み、OSコピーを使用して古いファイルを上書きすることができます。

1
cowbert

ワークシートに約100,000個のセルしかない場合は、メモリの問題は発生しないはずです。おそらくこれをさらに調査する必要があります。

iter_cols()は、基になるXMLファイルの継続的で非常に非効率的な再解析を必要とするため、読み取り専用モードでは使用できません。ただし、Zipを使用してiter_rows()から行を列に変換するのは比較的簡単です。

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)
2
Charlie Clark