概要

こないだは xls をディスってみたけれど、今回はセル結合をディスってみようぜ。

Excel のセル結合は全人類に憎まれているわけであるが、いまだに世界に残存している。今回は openpyxl でセル結合をなんとかするぞ。具体的には、この左みたいなの↓を、右みたいに↓する。

 

openpyxl

とりあえずコイツをゲットしようぜ。ドキュメントはここ (https://openpyxl.readthedocs.io/en/stable/) だ。

pip install openpyxl

 

セル結合を滅ぼす

じゃあ Python ちゃんに任せていこう。まず、結合された哀れなセルは、一体どういうオブジェクトになっているのか確認しよう。

import os
import openpyxl
from openpyxl.cell.cell import MergedCell

# target xlsx
xlsx = os.path.join("excel", "merged_cell.xlsx")

# xlsx file -> Workbook object
workbook = openpyxl.load_workbook(filename=xlsx, data_only=True)

# get Worksheet
print(workbook.sheetnames)  # ['Sheet1']
worksheet = workbook["Sheet1"]

for row in worksheet.iter_rows():
    row_for_print = [
        (type(cell), cell.value)
        for cell in row
    ]
    print(row_for_print)
    # [(<class 'Cell'>, 'フィールド'), (<class 'Cell'>, 'ティルコネイル')]
    # [(<class 'MergedCell'>, None), (<class 'Cell'>, 'ダンバートン')]
    # [(<class 'MergedCell'>, None), (<class 'Cell'>, 'カブ港')]
    # [(<class 'MergedCell'>, None), (<class 'Cell'>, 'バンホール')]
    # [(<class 'MergedCell'>, None), (<class 'Cell'>, 'イメンマハ')]
    # [(<class 'MergedCell'>, None), (<class 'Cell'>, 'タルティーン')]
    # [(<class 'MergedCell'>, None), (<class 'Cell'>, 'タラ')]
    # [(<class 'Cell'>, 'ダンジョン'), (<class 'Cell'>, 'アルビ')]
    # [(<class 'MergedCell'>, None), (<class 'Cell'>, 'キア')]
    # [(<class 'MergedCell'>, None), (<class 'Cell'>, 'ラビ')]
    # [(<class 'MergedCell'>, None), (<class 'Cell'>, 'マス')]
    # [(<class 'MergedCell'>, None), (<class 'Cell'>, 'フィアード')]
    # [(<class 'MergedCell'>, None), (<class 'Cell'>, 'バリ')]
    # [(<class 'MergedCell'>, None), (<class 'Cell'>, 'コイル')]
    # [(<class 'MergedCell'>, None), (<class 'Cell'>, 'ルンダ')]
    # [(<class 'MergedCell'>, None), (<class 'Cell'>, 'ペッカ')]
    # [(<class 'MergedCell'>, None), (<class 'Cell'>, 'アルベイ')]

ふつーのセルは openpyxl.cell.cell.Cell で、哀れなセルは openpyxl.cell.cell.MergedCell になっているらしいぜ。これは知らんかったな。

それなら、 MergedCell の部分には “ひとつ上のセルの値” を入れてやれば、欲しかった表が作れそうだぜ。今回は、既存の xlsx を編集するんじゃなくて、新しい xlsx を作っていくよ。

new_rows = []
for row in worksheet.iter_rows():
    new_row = []
    for i, cell in enumerate(row):
        if isinstance(cell, MergedCell):
            new_row.append(new_rows[-1][i])
            continue
        new_row.append(cell.value)
    new_rows.append(new_row)
    print(new_row)
    # ['フィールド', 'ティルコネイル']
    # ['フィールド', 'ダンバートン']
    # ['フィールド', 'カブ港']
    # ['フィールド', 'バンホール']
    # ['フィールド', 'イメンマハ']
    # ['フィールド', 'タルティーン']
    # ['フィールド', 'タラ']
    # ['ダンジョン', 'アルビ']
    # ['ダンジョン', 'キア']
    # ['ダンジョン', 'ラビ']
    # ['ダンジョン', 'マス']
    # ['ダンジョン', 'フィアード']
    # ['ダンジョン', 'バリ']
    # ['ダンジョン', 'コイル']
    # ['ダンジョン', 'ルンダ']
    # ['ダンジョン', 'ペッカ']
    # ['ダンジョン', 'アルベイ']

よしよし、そんでこれを新しい xlsx へ書き込めば……。

# save edited worksheet
new_workbook = openpyxl.Workbook()
worksheet = new_workbook.active
worksheet.title = "Sheet1"
for new_row in new_rows:
    worksheet.append(new_row)
new_workbook.save(os.path.join("excel", "new_workbook.xlsx"))

セル結合を滅ぼしてやったぜ。冒頭の Workbook とは、フォントファミリーとフォントサイズが違うな。 Python ちゃんにゼロから作ってもらうと、こんな設定になるみたいね。

 

所感

  • セル結合は、 “ふつーのセル” と “そのセルに結合された空セル” で構成されている、
  • じゃあ “空セルにふつーのセルをコピーすりゃ” いい感じになるじゃん、

というのがキレーに出来たのが楽しかったので記事にしたぜ。

最後に、公式ドキュメントの各ページへたどり着くのがけっこう難しかったのでリンクをしっかり乗っけておこう。一度たどり着けてしまえば、わかりやすいんだけどねー。