Skip to content

python-calamine(と calamine)からの移行

calamine は定評ある Rust 製 XLSX/XLS リーダー、python-calamine はその Python バインディングです。どちらもスプレッドシートのみに特化しています。

Office Oxide は XLSX で python-calamine より 2.8 倍高速(1,802 ファイルで平均 5.0 ms 対 13.9 ms)、最高のパス率(97.8% 対 96.6%)です。さらに完全な DOCX、PPTX、レガシー DOC/PPT サポートも加わります — calamine がまったく読めないフォーマットです。

いつ移行するか

以下のいずれかが当てはまるなら切り替えを:

  • .docx / .pptx / .doc / .ppt も必要(calamine は XLSX/XLS のみ)
  • より広い機能セットが欲しい: Markdown / HTML 出力、構造化された IR、EditableDocument によるテンプレート化
  • 一部シナリオでの calamine のわずかな性能優位よりパス率が重要
  • Python バインディング上にいて、クロス FFI 変換を減らしたい

calamine に留まる方がよい場合:

  • .xlsx.xls しか読まない
  • calamine 固有の API(Reader::with_header_rowworksheet_range_at など)に依存
  • 数式表現が必要(calamine は公開する; Office Oxide の IR はしない)

インストール(Python)

pip uninstall python-calamine
pip install office-oxide

インストール(Rust)

# Cargo.toml
[dependencies]
# 置き換え:
#   calamine = "0.30"
office_oxide = "0.1.0"

並べて比較するチートシート — Python

ワークブックを開く

python-calamine

from python_calamine import CalamineWorkbook

wb = CalamineWorkbook.from_path("budget.xlsx")

office_oxide

from office_oxide import Document

with Document.open("budget.xlsx") as doc:
    ...

シートをイテレーション

python-calamine

for name in wb.sheet_names:
    sheet = wb.get_sheet_by_name(name)
    for row in sheet.to_python():
        print(row)

office_oxide

with Document.open("budget.xlsx") as doc:
    ir = doc.to_ir()

for section in ir["sections"]:
    print(f"# {section.get('title')}")
    for el in section["elements"]:
        if el["kind"] == "Table":
            for row in el["rows"]:
                print(row)

1 枚のシートを行として読む

python-calamine

sheet = wb.get_sheet_by_name("Q4")
rows = sheet.to_python()

office_oxide

with Document.open("budget.xlsx") as doc:
    table = next(
        el for section in doc.to_ir()["sections"]
        if section.get("title") == "Q4"
        for el in section["elements"] if el["kind"] == "Table"
    )
    rows = table["rows"]

より直接的なパスなら:

with Document.open("budget.xlsx") as doc:
    sheet = doc.as_xlsx().sheet("Q4")
    rows = sheet.rows()    # list[list[str]]

シート名

python-calamine

print(wb.sheet_names)

office_oxide

with Document.open("budget.xlsx") as doc:
    print([s.name() for s in doc.as_xlsx().sheets()])

並べて比較するチートシート — Rust

開いてイテレーション

calamine

use calamine::{open_workbook, Xlsx, Reader};

let mut wb: Xlsx<_> = open_workbook("budget.xlsx")?;
for sheet_name in wb.sheet_names() {
    if let Ok(range) = wb.worksheet_range(&sheet_name) {
        for row in range.rows() {
            println!("{row:?}");
        }
    }
}

office_oxide

use office_oxide::Document;

let doc = Document::open("budget.xlsx")?;
if let Some(xlsx) = doc.as_xlsx() {
    for sheet in xlsx.sheets() {
        for cell in sheet.cells() {
            println!("{}: {:?}", cell.address(), cell.value());
        }
    }
}

フォーマット非依存の IR(calamine に同等物なし)

let doc = Document::open("budget.xlsx")?;
let ir = doc.to_ir();
serde_json::to_writer(std::io::stdout(), &ir)?;

これは .docx.pptx から得られるものと同じ形です — 下流コンシューマがソースフォーマットを気にしなくてよいときに便利です。

XLSX への書き込み

calamine は 読み取り専用 です。Office Oxide は EditableDocument 経由で XLSX セルを書けます:

from office_oxide import EditableDocument

with EditableDocument.open("budget.xlsx") as ed:
    ed.set_cell(0, "B5", 42_000)
    ed.save("budget.xlsx")

完全な XLSX 構築には xlsx::create::XlsxBuilder に降りるか、umya-spreadsheet / rust_xlsxwriter を使ってください。

パフォーマンス

ライブラリ XLSX 平均 p99 通過率
office_oxide 5.0 ms 40 ms 97.8%
python-calamine 13.9 ms 183 ms 96.6%
openpyxl 94.5 ms 698 ms 96.2%
ライブラリ XLS 平均 p99 通過率
office_oxide 2.8 ms 75 ms 99.2%
python-calamine 9.0 ms 96 ms 90.7%

異なる点

calamine はセルごとに型付き Data enum(IntFloatStringBoolDateTimeEmptyError)を返します。Office Oxide の IR は文字列に畳み込みます; 型付きセルアクセスにはフォーマット固有のアクセサを使います:

with Document.open("budget.xlsx") as doc:
    for sheet in doc.as_xlsx().sheets():
        for cell in sheet.cells():
            print(cell.value(), cell.value_type())   # value_type: "string" | "number" | "boolean" | "empty"

関連項目