データ構造

このチュートリアルでは、値の読み込み・書き込み時における一般的な事例や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