LoginSignup
1
2

More than 1 year has passed since last update.

IronPythonを使ったExcel操作の覚え書き(後編)

Last updated at Posted at 2021-03-30

前編に続き,後編ではセル範囲の操作,そして.NETとの連携にも少しだけ触れます。

日付データの扱い

Excelの日付データは.NETのSystem.DateTime構造体(オブジェクト)です。
一方,Pythonはdatetimeモジュールで日付を扱います。
IronPythonはどちらの機能も使うことができます。
それぞれの機能の説明は割愛して,相互の変換のみ確認します。
例として,セルA1に「4/1」と入力します。通常は「4月1日」として日付データになります。
以下,確認の様子です。

IronPython 対話モード画面
>>> 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でも書き込めます。

IronPython
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行のセル範囲をリストにする

ここでは手順を追って確認してみます。

IronPython 対話モード画面
>>> val_A1E1 = ws.Range['A1:E1']
>>> val_A1E1
<2 dimensional Array[object] at 0x000000000000006D>

セル範囲が1行でも,受け取る値は2次元Arrayです。

IronPython 対話モード画面
>>> list(val_A1E1)
[0.0, 1.0, 2.0, 3.0, 4.0]

Arraylist関数でPythonの1次元リストに変換できます。(tuple関数も同様)

(2) 1列のデータ

セル範囲が縦1列の場合,ワークシート関数Transposeを活用します。

例3 1次元のリストを1列のセル範囲に書き込む

リスト[0, 1, 2, 3, 4]をセルA1:A5の5行1列に書き込みます。

IronPython
tr = ex.WorksheetFunction.Transpose    # 別名をつけて短縮すると使いやすい(任意)
ws.Range['A1:A5'] = tr(Array[object](lst))    # 1行のデータを1列に転置

例4 1列のセル範囲をリストにする

例2と同じです。list関数は2次元Arrayでも1次元のリストに平坦化します。

IronPython
val_A1A5 = ws.Range['A1:A5']()
list(val_A1A5)

(3) Arrayクラスの扱い

Arrayクラスのデータを直接利用する場合は,プロパティやメソッドを使います。
先に取得したval_A1A5を使って内容を確認してみます。
Excelから取得した2次元Arrayベースは[1,1]です。(Cells.Itemプロパティと同じイメージ)

IronPython 対話モード画面
>>> val_A1A5.GetValue(4, 1)
3.0

IronPythonでは,ArrayクラスのデータをPythonのシーケンス同様に”識別子[インデックス]”で扱うこともできます。ただし,ベースが[0,0]になることに注意します。
よって,val_A1A5.GetValue(4, 1)val_A1A5[3, 0]と同じ要素を指します。

IronPython 対話モード画面
>>> val_A1A5[3, 0]
3.0

また,例2・例4でlist関数に渡したように,ArrayクラスのデータはIronPythonにおいて「反復可能なオブジェクト」として扱われるので,2次元Arrayでもfor文で直接回すことができます。
例示はしませんが,1次元Arrayの場合はスライスも使えます。
詳細は公式ドキュメントをお読みください。

IronPython
# 2次元Arrayを回す
for d in val_A1A5:
    print d

セル範囲の操作(複数行×複数列のデータ)

最後に,複数行×複数列のセル範囲です。
とりあえずセルA1:E10の範囲にExcelの数式で適当にデータを入れます。

IronPython
ws.Range['A1:E10'] = '=(COLUMN()-1)*10+ROW()'

値の取得

単独セル同様に2次元Arrayで取得できます。

IronPython
val_A1E10 = ws.Range['A1:E10']()    # 2次元Array

ただし,後の処理を考えると,取得したデータは2次元リストかタプルの方がPythonの機能を効率よく利用できます。
RowsColumnsプロパティと内包表記を組み合わせればPythonの2次元リスト(リストのリスト)が簡単に得られます。

IronPython
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次元リスト 列単位

rcRangeオブジェクトなので,r()c()だけでValueが取得できるのは便利です。(前編参照

確認します。

IronPython 対話モード画面
>>> 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次元Arrayarr2D)を作成して,セルH1:L10に書き込んでみます。

IronPython
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がよく使われます。

IronPython
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と連携させています。)

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2