前編に続き,後編ではセル範囲の操作,そして.NETとの連携にも少しだけ触れます。
日付データの扱い
Excelの日付データは.NETのSystem.DateTime
構造体(オブジェクト)です。
一方,Pythonはdatetime
モジュールで日付を扱います。
IronPythonはどちらの機能も使うことができます。
それぞれの機能の説明は割愛して,相互の変換のみ確認します。
例として,セルA1に「4/1」と入力します。通常は「4月1日」として日付データになります。
以下,確認の様子です。
>>> from System import DateTime
>>> from datetime import *
>>> nd = ws.Range['A1']() # Excelの日付データは.NETのSystem.DateTime
>>> nd
<System.DateTime object at 0x000000000000007F [2021/04/01 0:00:00]>
>>> pd = datetime(nd) # Pythonのdatetimeオブジェクトに変換
>>> pd
datetime.datetime(2021, 4, 1, 0, 0)
>>> nd2 = DateTime.Parse(str(pd)) # Pythonから.NETへは文字列にしてから変換
>>> nd2
<System.DateTime object at 0x0000000000000080 [2021/04/01 0:00:00]>
>>> ws.Range['A2'] = nd2 # 変換後のデータをセルA2に貼り付けてみる
>>>
セル範囲の操作(1行または1列のデータ)
セル範囲に対するValue
プロパティで受け渡しされるデータは,.NETの2次元System.Array
クラス(以下,Array
)です。
Array
クラスのインスタンス上にデータを用意して一気にExcelに貼り付けた方が処理が速くなります。
具体例を示します。
(1) 1行のデータ
例1 リストを1行のセル範囲に書き込む
前編を参考に新規ワークブックを開きます。
前編同様,参照しているシートをws
とします。
リスト[0, 1, 2, 3, 4]をセルA1:E1の1行5列に書き込みます。
なお,セル範囲が1行の場合に限り,1次元のArray
でも書き込めます。
from System import Array
lst = range(5)
ws.Range['A1:E1'] = Array[object](lst)
もちろんws.Range['A1:E1'] = Array[object](range(5))
でも同じです。
Pythonのリストやタプルは簡単に1次元Array
に変換できるので,そのままセル範囲に書き込みます。
例2 1行のセル範囲をリストにする
ここでは手順を追って確認してみます。
>>> val_A1E1 = ws.Range['A1:E1']
>>> val_A1E1
<2 dimensional Array[object] at 0x000000000000006D>
セル範囲が1行でも,受け取る値は2次元Array
です。
>>> list(val_A1E1)
[0.0, 1.0, 2.0, 3.0, 4.0]
Array
はlist
関数でPythonの1次元リストに変換できます。(tuple
関数も同様)
(2) 1列のデータ
セル範囲が縦1列の場合,ワークシート関数Transpose
を活用します。
例3 1次元のリストを1列のセル範囲に書き込む
リスト[0, 1, 2, 3, 4]をセルA1:A5の5行1列に書き込みます。
tr = ex.WorksheetFunction.Transpose # 別名をつけて短縮すると使いやすい(任意)
ws.Range['A1:A5'] = tr(Array[object](lst)) # 1行のデータを1列に転置
例4 1列のセル範囲をリストにする
例2と同じです。list
関数は2次元Array
でも1次元のリストに平坦化します。
val_A1A5 = ws.Range['A1:A5']()
list(val_A1A5)
(3) Arrayクラスの扱い
Array
クラスのデータを直接利用する場合は,プロパティやメソッドを使います。
先に取得したval_A1A5
を使って内容を確認してみます。
Excelから取得した2次元Array
のベースは[1,1]です。(Cells.Item
プロパティと同じイメージ)
>>> val_A1A5.GetValue(4, 1)
3.0
IronPythonでは,Array
クラスのデータをPythonのシーケンス同様に”識別子[インデックス]”で扱うこともできます。ただし,ベースが[0,0]になることに注意します。
よって,val_A1A5.GetValue(4, 1)
はval_A1A5[3, 0]
と同じ要素を指します。
>>> val_A1A5[3, 0]
3.0
また,例2・例4でlist
関数に渡したように,Array
クラスのデータはIronPythonにおいて「反復可能なオブジェクト」として扱われるので,2次元Array
でもfor
文で直接回すことができます。
例示はしませんが,1次元Array
の場合はスライスも使えます。
詳細は公式ドキュメントをお読みください。
# 2次元Arrayを回す
for d in val_A1A5:
print d
セル範囲の操作(複数行×複数列のデータ)
最後に,複数行×複数列のセル範囲です。
とりあえずセルA1:E10の範囲にExcelの数式で適当にデータを入れます。
ws.Range['A1:E10'] = '=(COLUMN()-1)*10+ROW()'
値の取得
単独セル同様に2次元Array
で取得できます。
val_A1E10 = ws.Range['A1:E10']() # 2次元Array
ただし,後の処理を考えると,取得したデータは2次元リストかタプルの方がPythonの機能を効率よく利用できます。
Rows
やColumns
プロパティと内包表記を組み合わせればPythonの2次元リスト(リストのリスト)が簡単に得られます。
list2R_A1E10 = [list(r()) for r in ws.Range['A1:E10'].Rows] # 2次元リスト 行単位
list2C_A1E10 = [list(c()) for c in ws.Range['A1:E10'].Columns] # 2次元リスト 列単位
r
やc
はRange
オブジェクトなので,r()
,c()
だけでValue
が取得できるのは便利です。(前編参照)
確認します。
>>> list2R_A1E10
[[1.0, 11.0, 21.0, 31.0, 41.0], [2.0, 12.0, 22.0, 32.0, 42.0], [3.0, 13.0, 23.0, 33.0, 43.0], [4.0, 14.0, 24.0, 34.0, 44.0], [5.0, 15.0, 25.0, 35.0, 45.0], [6.0, 16.0, 26.0, 36.0, 46.0], [7.0, 17.0, 27.0, 37.0, 47.0], [8.0, 18.0, 28.0, 38.0, 48.0], [9.0, 19.0, 29.0, 39.0, 49.0], [10.0, 20.0, 30.0, 40.0, 50.0]]
>>> list2C_A1E10
[[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0], [11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0], [21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0], [31.0, 32.0, 33.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 40.0], [41.0, 42.0, 43.0, 44.0, 45.0, 46.0, 47.0, 48.0, 49.0, 50.0]]
>>>
値の設定
もし,PythonからExcelに貼り付けるデータがセル範囲をイメージした2次元リストであれば,2次元Array
に変換して一括でExcelに貼り付けた方が高速です。VBAでも配列変数を使って一括貼り付けする方法が数多く見られます。
残念ながらPythonの2次元リストを2次元Array
に直接変換する手段はありませんので,2次元リストの各要素を2次元Array
に複写します。
先ほど取得した2次元リストlist2R_A1E10
からすべてのデータを2倍にした2次元Array
(arr2D
)を作成して,セルH1:L10に書き込んでみます。
arr2D = Array.CreateInstance(object, 10, 5) # 10行5列の2次元Arrayを作成
for r in range(10):
for c in range(5):
arr2D[r, c] = list2R_A1E10[r][c] * 2
ws.Range['H1'].Resize[10, 5] = arr2D # 貼り付け(10行5列を意識してResizeを使用)
for文で回すことになりますが,Array
上なので高速です。
なお,多重ループの場合,Pythonではitertools.product
がよく使われます。
from itertools import product
for r, c in product(range(10), range(5)):
arr2D[r, c] = list2R_A1E10[r][c] * 2
ちなみに,2次元Array
の作成はArray.CreateInstance
が正攻法?ですが,私自身は,ExcelとIronPythonの画面を並べて,VBAの「イミディエイトウィンドウ」的に使うことが多いので,貼り付け先のセル範囲を使って
arr2D = ws.Range['H1:L10']()
とか
Excel上で貼り付け先のセル範囲を選択して
arr2D = ex.Selection()
で横着することもあります。
おわりに
本稿は「Excel作業中に,補助器具としてPythonの対話モードを使う」という前提でまとめました。
細かいことを書けばキリがないので,ここでは値の扱いだけに内容を絞っています。
手の込んだコレクション作業が多い場合はF#(fsi),ちょっとした単純作業はIronPythonみたいな感じで使い分けていますが,本当に重宝します。
なお,この原稿を書いている最中に,職場で簡単な実例ができたので, 後日投稿する予定です。
(投稿しました。Excelを扱う記事ではありませんが,実務ではExcelと連携させています。)