データ構造¶
このチュートリアルでは、値の読み込み・書き込み時における一般的な事例やxlwingsのデフォルトの動作を簡単に紹介します。options
メソッドの使い方についてのより詳しい説明は コンバーターおよびオプション をご覧ください。
これ以降のすべてのコード サンプルは次のインポートを前提としています:
>>> import xlwings as xw
単独セル¶
デフォルトでは単独セルは、セルの値が数字であれば float
、文字列であれば unicode
、空(empty)であれば None
、日付であれば datetime
として読み込まれます。
>>> import datetime as dt
>>> sheet = xw.Book().sheets[0]
>>> sheet['A1'].value = 1
>>> sheet['A1'].value
1.0
>>> sheet['A2'].value = 'Hello'
>>> sheet['A2'].value
'Hello'
>>> sheet['A3'].value is None
True
>>> sheet['A4'].value = dt.datetime(2000, 1, 1)
>>> sheet['A4'].value
datetime.datetime(2000, 1, 1, 0, 0)
リスト¶
1次元リスト: Excel上で行または列形式のRangeは、単純なリスト形式で読み込まれます。したがって、一度Pythonに読み込まれると元の形状(行であるか列であるか)がわからなくなります。このことが問題となるようなら、次の・で紹介するの方法をとりましょう:
>>> sheet = xw.Book().sheets[0] >>> sheet['A1'].value = [[1],[2],[3],[4],[5]] # Column orientation (nested list) >>> sheet['A1:A5'].value [1.0, 2.0, 3.0, 4.0, 5.0] >>> sheet['A1'].value = [1, 2, 3, 4, 5] >>> sheet['A1:E1'].value [1.0, 2.0, 3.0, 4.0, 5.0]
単独セルを強制的にリストとして読み込む方法は次のとおりです:
>>> sheet['A1'].options(ndim=1).value [1.0]
注釈
transpose
を使えば、リストをExcelの列方向に書き込めます:sheet.range('A1').options(transpose=True).value = [1,2,3,4]
2次元リスト: 行や列の方向を保持するには、Rangeのオプションで
ndim
を使います。こうすれば、Rangeは入れ子のリスト("2次元リスト")として読み込まれます:>>> sheet['A1:A5'].options(ndim=2).value [[1.0], [2.0], [3.0], [4.0], [5.0]] >>> sheet['A1:E1'].options(ndim=2).value [[1.0, 2.0, 3.0, 4.0, 5.0]]
2次元のRangeは自動的に入れ子のリストとして読み込まれます。逆に(入れ子の)リストをExcelのRangeに代入するには、左上のセルを指定します。次の例では、値をPythonに読み込む時のインデックスの使い方も示しています:
>>> sheet['A10'].value = [['Foo 1', 'Foo 2', 'Foo 3'], [10, 20, 30]] >>> sheet.range((10,1),(11,3)).value [['Foo 1', 'Foo 2', 'Foo 3'], [10.0, 20.0, 30.0]]
注釈
Excelとの相互作用は最低限に留めてください。 sheet.range('A1').value = [1, 2]
と sheet.range('A2').value = [3, 4]
を行うよりも、 sht.range('A1').value = [[1,2],[3,4]]
とする方が、常に効率的です。
Rangeの拡張¶
expand
メソッドか options
メソッドの expand
キーワードを使えば、ExcelのRangeをサイズに応じて動的に取得ができます。 expand
メソッドは拡張されたRangeオブジェクトを返すのに対し、 options
メソッドはRangeの値を取得する時のみ評価されます。次の例を見ればその違いがわかるでしょう:
>>> sheet = xw.Book().sheets[0]
>>> sheet['A1'].value = [[1,2], [3,4]]
>>> range1 = sheet['A1'].expand('table') # or just .expand()
>>> range2 = sheet['A1'].options(expand='table')
>>> range1.value
[[1.0, 2.0], [3.0, 4.0]]
>>> range2.value
[[1.0, 2.0], [3.0, 4.0]]
>>> sheet['A3'].value = [5, 6]
>>> range1.value
[[1.0, 2.0], [3.0, 4.0]]
>>> range2.value
[[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]
'table'
は行および列方向、'down'
は行方向、 'right'
は列方向に拡張します。
注釈
左上セルの代わりに名前付きRangeで expand()
メソッドを使用すれば、Excelでの柔軟なセットアップが可能になります。つまり、 sheet.range('NamedRange').expand().value
のような書き方をすることで、コードを変更することなく、Excel上でテーブルの位置やサイズの変更が可能になります。
NumPy arrays¶
Numpy arrayは、入れ子のリストと同様に機能します。ただし、空のセルは None
ではなく nan
となります。RangeをNumpy arrayとして読み込むには、options
メソッドで convert=np.array
を指定します:
>>> import numpy as np
>>> sheet = xw.Book().sheets[0]
>>> sheet['A1'].value = np.eye(3)
>>> sheet['A1'].options(np.array, expand='table').value
array([[ 1., 0., 0.],
[ 0., 1., 0.],
[ 0., 0., 1.]])
Pandas DataFrames¶
>>> sheet = xw.Book().sheets[0]
>>> df = pd.DataFrame([[1.1, 2.2], [3.3, None]], columns=['one', 'two'])
>>> df
one two
0 1.1 2.2
1 3.3 NaN
>>> sheet['A1'].value = df
>>> sheet['A1:C3'].options(pd.DataFrame).value
one two
0 1.1 2.2
1 3.3 NaN
# options: work for reading and writing
>>> sheet['A5'].options(index=False).value = df
>>> sheet['A9'].options(index=False, header=False).value = df
Pandas Series¶
>>> import pandas as pd
>>> import numpy as np
>>> sheet = xw.Book().sheets[0]
>>> s = pd.Series([1.1, 3.3, 5., np.nan, 6., 8.], name='myseries')
>>> s
0 1.1
1 3.3
2 5.0
3 NaN
4 6.0
5 8.0
Name: myseries, dtype: float64
>>> sheet['A1'].value = s
>>> sheet['A1:B7'].options(pd.Series).value
0 1.1
1 3.3
2 5.0
3 NaN
4 6.0
5 8.0
Name: myseries, dtype: float64
注釈
リストやNumPy arrayやPandas DataFrameをExcelに書き込むには、左上セルを指定するだけで済みます。例: sheet.range('A1').value = np.eye(10)
チャンク(Chunking): 巨大なDataFrame等の読み書き¶
大きなRangeを読み書きする場合、チャンクしないとタイムアウトやメモリー エラーになることがあります。理想的な chunksize
はシステムや配列のサイズに依存するため、いくつかのチャンクサイズを試して、うまく機能するものを見つける必要があります:
import pandas as pd
import numpy as np
sheet = xw.Book().sheets[0]
data = np.arange(75_000 * 20).reshape(75_000, 20)
df = pd.DataFrame(data=data)
sheet['A1'].options(chunksize=10_000).value = df
読み込みについても同様に:
# As DataFrame
df = sheet['A1'].expand().options(pd.DataFrame, chunksize=10_000).value
# As list of list
df = sheet['A1'].expand().options(chunksize=10_000).value