今回も引き続き、Pythonから「Excel」を操作する「openpyxl」ライブラリを紹介していきます。
前回の記事では、ライブラリの概要とワークブックを新規作成・保存するといった「基本操作に関すること」について。そして、openpyxlを使う上で必ず理解しておかなければならない「主要オブジェクトとそのレイヤー構造」について解説しました。
前回の記事はこちらになります。
重複する内容は割愛していますので、一読して頂くことをお勧めします。
また、本連載【Python×Excel】の全体コンテンツはこちらのようになっていますので、参考にして下さい。
さて、連載2回目となる今回の記事では、セル(Cellオブジェクト)の操作に関して、より深掘り解説をしていきます。
表計算ソフト「Excel」を使う目的は、「セルに入力されたデータ」に対して様々な処理を施こすことで、「分析・統計チャートにまとめる」ことだと思います。
つまり「Excel作業」=「セルに対する操作」となります。
セルの値をどのように「取得して」「処理」していくのか、具体例を示しながら解説します。
この記事を読むことで次のようなことが「できる・わかる」ようになりますので最後までお付き合いください。
本サイトでの紹介内容は一例です。また、関数などの省略可能なオプション引数などについては割愛していますので、詳細や不明点などは必要に応じて公式サイトなどを参照してください。
【公式ドキュメント】https://openpyxl.readthedocs.io/en/stable/
それでは、次節よりCellオブジェクトの操作方法について詳しく解説していきます
1. セル(Cellオブジェクト)を取得する
セルは、Excel作業の基本単位です。そのため、Cellオブジェクト には、セルの操作に関連するさまざまなオブジェクト・メソッド・プロパティ(属性)が用意されています。プログラマはそれらをよく理解し、使いこなす必要があります。
本節では、Cellオブジェクトに関する操作のうち「オブジェクトの追加と取得」「データを取得する」「数式を設定」「セル範囲の定義と管理」について、より踏み込んだ解説をしていきます。
1.1 Cellオブジェクトを追加(取得)する
前述のとおりExcelで行うデータ処理はCellオブジェクトに対する操作といえます。
新規作成したブックに関しては、まだCellオブジェクトがありませんので、「追加・取得」する必要があります。
また、何らかのコンテンツを含む、既存ブックを読込んで編集する場合には、既存のCellオブジェクトを「取得」できますし、さらに「追加・取得」もできます。
Cellオブジェクトの追加・取得には、さまざまな書式が用意されています。
前回記事と重複する部分がありますが、重要ですのでもう一度整理します。
1.1.1 単一セル(Cellオブジェクト)の追加
はじめに、単一のセル(Cellオブジェクト)を追加する方法についてです。
書式は以下のようにWorksheetオブジェクトに対して「セルアドレス(‘A1’など)」を指定する(➀)ものと cell()メソッド を使うもの(②)の2つがあります。
cell()メソッドは、引数:row/column には、追加セルの行・列番号を指定します。(A1セル基準「1」とします) また、引数:val に値を設定することもできます。
【SAMPLE (1)】
<List1>にプログラム例を示します。[A]は「セルアドレス指定」[B]は「Cell()メソッド」によりCellオブジェクトを追加しています。引数valueで値を設定できることを確認しています。
from openpyxl import Workbook
wb=Workbook()
ws=wb.active
# [A] ------------------------------------------------------------------
# Worksheetオブジェクトのインデックス指定による
# Cellオブジェクトの追加と値の設定
ws['B4']
print(ws['B4']) # >><Cell 'Sheet'.B4> Cellオブジェクトを追加
print(ws['B4'].value) # >>None デフォルトの値は空(None)
ws['B4']=4 # 'B4'セルに値を設定(valueプロパティ無し)
print(ws['B4'].value) # >>4 'B4'セルに値(4)を確認
ws['B5'].value=5 # 'B5'セルに値を設定(valueプロパティ有り)
print(ws['B5'].value) # >>5 'B5'セルに値(5)を確認
# [B] ------------------------------------------------------------------
# Worksheetオブジェクトのcell()メソッドによる
# Cellオブジェクトの取得と値の設定
ws.cell(row=4, column=2).value=10 # 名前付き引数指定・valueプロパティによる値の設定
print(ws.cell(row=4, column=2).value) # >>10 セルの値を確認
ws.cell(row=4, column=3, value=20) # 引数valueでセルの値も同時に設定できる
print(ws.cell(row=4, column=3).value) # >>20 セルの値を確認
ws.cell(4, 4, 30) # 順番指定引数でも当然問題ない
print(ws.cell(4, 4, 30).value) # >>30 セルの値を確認
1.1.2 複数セル(Cellオブジェクト)の追加
一度に複数のセルを追加することもできます。
セル範囲(行単位・列単位・もしくはその両方)を指定することで、複数個のCellオブジェクトを追加します。
書式は以下のようにWorksheetオブジェクトに対して「セルアドレス範囲(“A1:C3”, “A1”:”C3” など)を指定する(➀)ものと、append() iter_rows()/iter_cols() など専用のメソッドが用意されています。(②➂)それぞれ、書式は以下のとおりです。
append()メソッド は引数に1行分のセルの値を要素とするリストを設定することで、「A列」を起点にした最終行にデータが追記されていきます。
iter_rows()/cols()メソッド は、引数に行番号(開始、終了)、列番号(開始、終了)でセル範囲を指定してオブジェクトを追加します。戻り値は、iter_rows()メソッドであれば、Rowオブジェクトを要素とするタプル(ジェネレータ)が返ります。
両メソッドの使い分けの目安をまとめると
➀. 任意の位置から、セル範囲を指定してCellオブジェクトを追加する場合
→「セル範囲のアドレス指定」または「iter_rows() / iter_cols()メソッド」
➁. A1セルを起点に行単位でRow(Cell)オブジェクトを追記する場合
→「append()メソッド」
【SAMPLE (2)】
<List2>にプログラム例を示します。[A]はセルアドレス範囲による指定、[B]はappend()メソッド、[C][D]はiter_**()メソッドを使用しています。 [A]では、Cellオブジェクトを要素とする2重タプル型式で、[C][D]はRows/Columnオブジェクトを要素とするジェネレータが取得できます。
実行結果は、コード内のコメントを参照してください。
from openpyxl import Workbook
wb=Workbook()
ws=wb.active
# [A]------------------------------------------------------------------
# Worksheetオブジェクトのインデックス指定による
# Cellオブジェクトの追加と値の設定
print(ws["A1:C3"])
print(ws["A1":"C3"])
# Cellオブジェクトを追加と同時に
# Cellオブジェクトを要素としたタプルを追加(複数行ある場合は2次元タプルとなる)
# >>((<Cell 'Sheet'.A1>, <Cell 'Sheet'.B1>, … <Cell 'Sheet'.B3>, <Cell 'Sheet'.C3>))
# [B]------------------------------------------------------------------
# append()メソッドによるCellオブジェクトの追加
# 最終行に値を設定する(メソッドの戻り値はなし)
data = [1, 2, 3, 4]
ws.append(data)
# [C]------------------------------------------------------------------
# iter_rows()メソッドによるCellオブジェクトの追加
# Rowオブジェクトのジェネレータが生成される
print(ws.iter_rows(min_row=6, max_row=7, min_col=3, max_col=5))
# >><generator object Worksheet._cells_by_row at 0x0000025205EC58C8>
# [D]------------------------------------------------------------------
# iter_cols()メソッドによるCellオブジェクトの追加
# Columnオブジェクトのジェネレータが生成される
print(ws.iter_cols(min_row=6, max_row=7, min_col=7, max_col=9))
# >><generator object Worksheet._cells_by_col at 0x0000025205EC58C8>
wb.save('Multi_Cell_gen.xlsx')
1.1.3 Cellオブジェクトの有効範囲
Cellオブジェクトを追加する際に気を付けることがあります。
それは、基準となる「A1セル」と新規追加したCellオブジェクト間の空間には自動的にNull文字(空)ををもつCellオブジェクトが生成(追加)されるといったものです。
以下(図4)に具体例を示します。cell()メソッドで「C3」にCellオブジェクトを追加(値はセットしてもしなくてもどちらでも可)したとします。すると、A1セル~C3セルの間の空白セルも自動的にNull文字をもつCellオブジェクトが追加されます。あくまで自動的に追加される(されてしまう)点がポイントです。
これにより、ワークシート上にある全てのCellオブジェクトにアクセスできる、rows/columnsプロパティ(次項で解説)を実行した場合、空白セルも含めて取得します。
便利ですが、空白セルをデータ処理に使う場合は、予期せぬエラーが発生するケースが多々ありますので、セルの値の有無を確認する、例外処理を入れるなどの対策を考慮します。
1.1.4 Celllオブジェクトの取得
ここまでは、Cellオブジェクトを追加(取得)する方法について解説しました。メソッド・プロパティでCellオブジェクトを追加すると、同時に戻り値として取得ができます。
では、既存のCellオブジェクトを取得するだけの場合にはどのような方法があるのでしょうか。本項では、取得だけに特化した書式についてまとめています。
当然ですが、存在していない(追加していない)Cellオブジェクトの取得はできません。例えば、作成直後のWorkbookオブジェクトに対して、以下の書式を実行すると、「IndexError」が発生しますので注意してください。
Worksheetオブジェクトに [row_index][col_index]のようにインデックスを指定してCellオブジェクトを取得することができます(➀)。
行番号row_indexは“A1”セルを「1」を基底とし、列番号col_indexは“A1”セルを「0」を基底とします。
行列単位の(Row/Columnオブジェクト)も同様に[index]のようにインデックス指定できます。(②➂)こちらは、行列ともに、「0」基底のインデックスとなんります。
Row/Columnオブジェクトについては以下を関連記事を参照してください。
ワークシート上の全てのCellオブジェクトを一括取得するプロパティも提供されています。
行単位(Rowオブジェクト)で取得する rowsプロパティ と列単位(Columnオブジェクト)で取得する columnsプロパティ です。(➀➁)
いずれもジェネレータ形式で戻します。「A1セル」からの値(Noneを含めて)がセットされている最終行・最終列までの全ての範囲が対象となります。
その他、オブジェクトではなく直接「値」を一括取得できる valulesプロパティ もあります。(➂)
実行例は<前回の記事>を参照してください。
2. セル(Cellオブジェクト)を操作する
Cellオブジェクトの追加ができたので、次はセルを操作する方法を解説します。
本節では、「セルに値(数値、文字列、数式)を設定・取得する」「セルを結合する」「セル範囲に名前を付けて定義する」を取り上げます。
2.1 セルに値を設定する
Cellオブジェクトに値(数値や文字列)を設定するには valueプロパティ を使います。
その他、<1.1.1項>の cell()メソッド の 引数:val にてオブジェクト追加と同時に値を設定することもできます。また、<1.1.2項>の append()メソッド で引数に渡したリストをセルに追加することもできます
その一方で、複数セルをまとめたRow/Column/Range(セル範囲)オブジェクト各種には、valueプロパティ やそれに近い機能は提供されていません。ですので、複数セルに一度にまとめて値をセットすることはできずに、個々の構成Cellオブジェクトにまでアンパック(分割)した上で、valueプロパティを適用するようにします。(図5)
【SAMPLE (3)】
サンプルコードで具体的な使用例を確認しましょう。
[A]: セル範囲(“A1:C3”)を行単位(Rowオブジェクト)に展開します。さらに、For文をネストさせて、Rowオブジェクトをセル単位(Cellオブジェクト)に展開した後、valueプロパティ で値(1)を設定しています。
[B]:「valueプロパティ」を使わずに、append()メソッド でセルへ追記します。
[C]: iter_rows()メソッド で行単位のジェネレータを生成後は[A]と同様です。
[D]: iter_cols()メソッド で列単位のジェネレータを生成後は[A]と同様です。
from openpyxl import Workbook
wb=Workbook()
ws=wb.active
# 【A】------------------------------------------------------------------
# Worksheetオブジェクトのインデックス指定による
# Cellオブジェクトの追加と値の設定
# 個々のセルに分解してセルの値を設定する
for row in ws["A1:C3"]:
for cell in row:
cell.value = 1
# 【B】------------------------------------------------------------------
# append()メソッドによるCellオブジェクトの追加
# 最終行に値を設定する(メソッドの戻り値はなし)
data = [1, 2, 3, 4]
ws.append(data)
# 【C】------------------------------------------------------------------
# iter_rows()メソッドによるCellオブジェクトの追加
for row in ws.iter_rows(min_row=6, max_row=7, min_col=3, max_col=5):
print(row)
# >>(<Cell 'Sheet'.C6>, <Cell 'Sheet'.D6>, <Cell 'Sheet'.E6>)
# >>(<Cell 'Sheet'.C7>, <Cell 'Sheet'.D7>, <Cell 'Sheet'.E7>)
# 個々のセルに分解してセルの値を設定する
for cell in row:
cell.value = 'iter_rows'
# 【D】------------------------------------------------------------------
# iter_cols()メソッドによるCellオブジェクトの追加
for col in ws.iter_cols(min_row=6, max_row=7, min_col=7, max_col=9):
print(col)
# >>(<Cell 'Sheet'.G6>, <Cell 'Sheet'.G7>)
# >>(<Cell 'Sheet'.H6>, <Cell 'Sheet'.H7>)
# >>(<Cell 'Sheet'.I6>, <Cell 'Sheet'.I7>)
# 個々のセルに分解してセルの値を設定する
for cell in col:
cell.value = 'iter_cols'
<List3>の実行結果は以下のようになりました。
図6([A]:緑枠, [B]:赤枠, [C,D]:青枠)
2.2 セルに数式を設定する
CellオブジェクトにExcel関数を適用することもできます。
通常のExcel関数と同様に「=(イコール記号)」に続けて計算式(Operator)を記述します。
そして「=」を含めた全体式を “(ダブルクオーテーション) もしくは‘(シングルクオーテーション) で囲みます。(式中に”(ダブル)が含まれている場合は全体を’(シングル)で囲みます。)
最後にCellオブジェクトに設定しますが、この際 valueプロパティ を経由することなく直接設定します。(valueで設定すると文字列として認識されます)
【Sample (4)】
具体例をサンプルコード<List4>で確認してみましょう。
セルの参照は、絶対参照($)にも対応しています。
from openpyxl import Workbook
wb=Workbook()
ws=wb.active
# Excel関数をセルに適用する
# 計算式を”(ダブルクオーテーション)で囲む
ws["A2"] = "=AVERAGE(C1:C5)"
# 計算式を’(シングルオーテーション)で囲む
ws["A3"] = '=AVERAGE(C1:C5)'
# 参照先は絶対参照でも問題なし
ws["A4"] = "=AVERAGE($C$1:$C$5)"
# 計算式中に文字列などで"を使っている場合は計算式
# を'(シングルクオーテーション)で囲む
ws["A1"] = '=IF(B1>=50,"50以上","50未満")'
wb.save('formula.xlsx')
2.3 複数のセル結合する
複数のセルを結合して1つのセルにマージすることも簡単にできます。結合はWorksheetオブジェクトの merge_cells()メソッド を、逆に結合を解除するときは unmerge_cells()メソッド を使います。
引数には結合範囲を’B2:F4’のようにA1アドレス範囲で指定、もしくは行列の番号で指定します。
結合したセルに値や書式を適用する際は、結合領域の「最左上セル」に対して設定を行います。それ以外のセルにアクセスを試みるとエラーが発生します。
さらに、結合領域の周囲に罫線(Borderオブジェクト※)を適用する場合は、外周セル一つ一つ個別に設定する必要がありますので注意してください。(※罫線の適用方法は<こちら>の記事で解説しています)
【SAMPLE (5)】
それでは、実際のコードで確認してみましょう。<List5>
[A]では、merge_cels()メソッドに、セルアドレス範囲(‘B2:F4’)を指定して結合させます。“B2“セルのCellオブジェクトを取得し、属性で値と配置を設定します。このとき、“C2”や“D3”など、“B2”セル以外にアクセスするとエラーとなります。
[B]では、結合範囲の行・列番号を始点(引数:start_row, start_column)と終点(引数:end_row, end_column) で指定しています。
from openpyxl import Workbook
from openpyxl.styles import Alignment
wb = Workbook()
ws = wb.active
#【A】-----------------------------------------------------------
# merge_cells()メソッドによる複数セルの結合(A1アドレス指定)
ws.merge_cells('B2:F4')
# 結合セル領域の左上のCellオブジェクト(B2)の取得
top_left_cell = ws['B2']
# 結合セル全体の装飾
# 左上のCellオブジェクト(B2)にスタイル書式を設定することで結合セル全体に装飾される
# B3, C2など左上以外のセルを指定するとエラーになるので注意
# また、枠線などは一度に設定できずに個々のセル毎に設定していく
top_left_cell.value = "Merged Cell!"
top_left_cell.alignment = Alignment(horizontal="center", vertical="center")
#【B】-----------------------------------------------------------
# merge_cells()メソッドによる複数セルの結合(行列数指定)
ws.merge_cells(start_row= 7, end_row=9, start_column=2, end_column=3)
wb.save('sample_Merged-Cell.xlsx')
<List5>の実行結果は以下のようになりました。
2.4 セルの範囲に名前を付ける
特定のセル範囲に名前を付けて、ブックやシート内で管理することもできます。
定義されたセル範囲は、次の書式で取得できる DefinedNameオブジェクト で管理されます。
DefinedName()クラス には、数多くのオプショナルな引数を持ちますが、少なくとも 引数:name (セル範囲に割り当てる名前(定義名))と 引数:attr_text (定義するセル範囲アドレス)の設定が必要です。 なお、引数attr_textは名前付き引数で指定してください。
定義されたセル範囲は、ブック内に複数保持できます。DefinedNameListオブジェクト(リスト)にまとめられています。リストに追加する場合は、append()メソッド を、リストを取得する場合は defineNameプロパティ を使います。(図8)
【SAMPLE (6)】
それでは、実際のコード<List6>で確認してみましょう。
コードの概要は、ブックを読込んで、定義済みのセル範囲の属性確認と新規定義を追加するものです。
このプログラムで読み込んだファイル(.xlsx)は以下からダウンロードすることができます。
from openpyxl import workbook
from openpyxl import load_workbook
wb=load_workbook('sample_Defined_Names.xlsx')
#(A)------------------------------------------------------------------------------
# オブジェクトの種類を確認
print(type(wb.defined_names)) # >><class 'openpyxl.workbook.defined_name.DefinedNameList'>
print(type(wb.defined_names.definedName)) # >><class 'list'>
print(type(wb.defined_names.definedName[0])) # >><class 'openpyxl.workbook.defined_name.DefinedName'>
#(B)------------------------------------------------------------------------------
# 定義したセル範囲名と領域のアドレスを取得
for defined in wb.defined_names.definedName:
print(defined.name) # >> my_range1
print(defined.attr_text) # >> Sheet1!$C$6:$E$8
# その他、DefinedNameオブジェクトには数多くの属性が用意されている
# Comment, description, hidden … いずれの属性も初期値は'None'
#(C)------------------------------------------------------------------------------
# 新規のセル範囲名の定義を追加
# DefinedNameオブジェクトの生成
new_range=workbook.defined_name.DefinedName(name='new_range', attr_text='Sheet1!$E$12:$G$14')
print(type(new_range)) # >><class 'openpyxl.workbook.defined_name.DefinedName'>
# DefinedNameListオブジェクトのappend()メソッドで新規定義した
# セル範囲(DefinedNameオブジェクト)を追加
wb.defined_names.append(new_range)
#(D)------------------------------------------------------------------------------
# 定義したセル範囲名と領域のアドレスを取得
for defined in wb.defined_names.definedName:
print(defined.name) # >> my_range1, new_range
print(defined.attr_text) # >> Sheet1!$C$6:$E$8 Sheet1!$E$12:$G$14
# 新たに追加したセル範囲名(new_range)が出力される
wb.save('sample_Defined_Names.xlsx')
それでは、ポイントを解説します。
<List6>の実行結果は以下のようになり、セル範囲(new_range)が新たに追加されました。
このコードの実行結果は、以下からダウンロード頂けます。
3. まとめ
いかがでしたでしょうか?
今回は、Excelを操作する「openpyxl」ライブラリを取り上げ、セル(Cellオブジェクト)の扱い方を中心に解説してきました。
Excelを使った作業は、セルを経由して行われることが基本となりますから、本稿の解説内容をぜひ活用していただきたいと思います。
最後に、今回の記事内容をまとめておきます。
・ セルに対して何らかの操作を施すための前提条件は、対象セルのCellオブジェクトが取得できていることである。新規ブックなどでCellオブジェクトがない場合は、cell()メソッドなどで追加(取得する)必要がある。
・ Cellオブジェクトの追加・取得は、「単一セルごと」または「複数セルをまとめて」行うことができるが、セルにデータを設定する場合や、書式を設定する場合には「単一セルごと」に対応する必要がある。
・ セルに「数式(Excel関数)を設定する」「結合する」「セル範囲に名前を定義する」こともできる。
さて次回は、「セルの書式設定」について取り上げます。セルに書式を施すことで、フォントや塗り潰し・装飾など見栄えを整えることができるようになります。
ぜひ、次の記事も参考にして頂けたら幸いです。リンク先はこちらになります。↓
最後までお読みいただきありがとうございました。