Python
C#
Excel
pandas
ExcelDataReader

ExcelファイルをPython、C#で読み込むまとめ

Python、C#でExcelファイルを読み込むためのライブラリーを紹介したいと思います。自分の場合は、Webアプリ用のデータをExcelを使って作っているので、それらのライブラリーをよく使っています。

Excelファイルを読み込むための方法として、Microsoft.Office.Interop.Excelを使ったCOM参照による方法を紹介している記事も多いですが、それを使うのは処理が遅いし、問題も多いし、そもそも Excel が必要なので Linux サーバーだと動作しません。以下に紹介する OSS のライブラリーはかなり改善されていて、完璧ではないものの注意してプログラムすれば問題なくかつ高速に処理することができます。

以下では、各ライブラリーの特徴と Excel の xlsx ファイルを読み込んで、tsv 形式で書き出すコードを載せています。

Python

openpyxl

openpyxlは、Excel 2010 の xlsx/xlsm/xltx/xltmfor ファイルを読み書きできる Python のライブラリーです。PythonにはExcelファイルを操作するためのライブラリーがいくつかありますが、その中で最もよく使われるライブラリーの一つです。

example.py
from openpyxl import load_workbook
import csv

def iter_row(row):
    yield [cell.value for cell in row]

wb = load_workbook(in_path, read_only=True)
sheet = wb.worksheets[0]
with open(out_path, 'w') as f:
    writer = csv.writer(f, delimiter='\t', quoting=csv.QUOTE_MINIMAL)
    for row in sheet.rows:
        writer.writerows(iter_row(row))

Excelのセルには、上の場合であればsheet['A1']又はsheet.cell(row=1, column=1)のようにしてアクセスできます。

Pandas (xlrd)

xlrd は、単独で Excelファイルを読み込むことができますが、pandas.read_excel を使って Pandas のデータフレームに変換した方が処理が楽です。以下のように簡単な記述で tsv 形式に変換できます。pandas.read_excel を使用する場合 xlrd の import 文を書く必要はありませんが、ライブラリーをインストールしておく必要はあります。

example.py
import pandas as pd

df = pd.read_excel(in_path, header=None)
df.to_csv(out_path, header=False, sep='\t', index=False)

データフレームには多くのAPIがあり、csv形式以外に、json, HTMLテーブル,SQLデータベース等に簡単に出力できます。

データフレームは、df.iat[0, 0]のようにしてCellの番号でCellの値を取得したり設定したりすることができるので、VBAやC#でよくするようにfor文で処理をすることもできますが、行列での演算が整備されているので、そちらの方を使うことが多くなります。

上の式では、任意の表を扱えるように header=None にしていますが、データベース形式のようにヘッダーがきちんとある場合は、例えば3行目にある場合はheader=2と指定するとヘッダーの名前で列を指定できるので便利です。

また、上の式のようにシート名を指定していない場合は、一番最初のシートが読み込まれます。シートを指定したい場合は、sheet_name='シート名'又は2番目のシートであればsheet_name=1のように番号で指定することができます。詳しくは、pandas.read_excelのマニュアルをみてください。

C#

ExcelDataReader

読み込み専用のソフトで、高速に処理できるのが特徴で、海外では以前から人気のあるライブラリーです。かっては日本語の処理に問題があったのですが、現時点では解消されたので、日本語でも安心して使えます。Excel 2.0 から Excel 2007以降のOpen XML形式まで広く対応しています。

example.cs
using (var stream = File.Open(inFile, FileMode.Open, FileAccess.Read))          
using (var excelReader = ExcelReaderFactory.CreateReader(stream))
{
    var result = excelReader.AsDataSet();
    var sb = new StringBuilder();

    foreach (DataRow datarow in result.Tables[0].Rows)
    {
        sb.Append(datarow.ItemArray.Aggregate((s, x) => s + "\t" + x.ToString()));
        sb.Append("\n");
    }
    File.WriteAllText(outFile, sb.ToString());
}

EPPlus

Office Open XML 形式(xlsx)の読み込み及び書き込みに対応しています。チャートや VBA を操作できる API もあり、Excel に関して言えば NPOI よりも機能は豊富です。.NET Core で使う場合は、Ver4.5からの対応なので現在は RC のものを使う必要があります。また、.NETStandard 2.0 の対応なので、.NET Core では 2.0 からの対応になります。

EPPlusでは、Cellsの番号は1から始まります。

example.cs
FileInfo newFile = new FileInfo(infile);
ExcelPackage pck = new ExcelPackage(newFile);
//Ver4.5では、Worksheetsは 0 から始まるように変更
ExcelWorksheet sheet = pck.Workbook.Worksheets[1];

var sb = new StringBuilder();
int rows = sheet.Dimension.Rows;

for (int r = 1; r <= rows; r++)
{
    for (int c = 1; c <= sheet.Dimension.Columns; c++)
        sb.Append(sheet.Cells[r, c].Text + "\t");

    sb.Remove(sb.Length - 1, 1);
    sb.Append("\n");         
}
File.WriteAllText(outFile, sb.ToString());

NPOI

Java の Apacche POI を .NET に移植したものです。2007 の xlsx 及び 2003 の xls ファイルの双方に対応しており、読み込みも書き出しもできます。Excel だけでなく docx にも対応しています。Tony Qu's NPOIは.NET Core には対応していませんが、それから派生した DotNetCore.NPOIが、.NET Standard 2.0 に対応しているので、.NET Core 2.0 でも動作します。DotNetCore.NPOI は ZKWeb.System.Drawing を使っているので Linux で動作させる場合には、libgdiplus が必要になります。

NPOIは、Cellsの番号は0からになります。

example.cs
FileStream stream = File.OpenRead(inFile);
var book = new XSSFWorkbook(stream);
stream.Close();
ISheet sheet = book.GetSheetAt(0);
int lastRowNum = sheet.LastRowNum;

var sb = new StringBuilder();
for (int r = 0; r < sheet.LastRowNum; r++)
{
    var datarow = sheet.GetRow(r);
    {
        foreach (ICell cell in datarow.Cells)
        { 
            if(cell.CellType == CellType.Numeric)
                sb.Append(cell.NumericCellValue + "\t");
            else
                sb.Append(cell.StringCellValue + "\t");
        }       
        sb.Remove(sb.Length - 1, 1);
        sb.Append("\n");
    }
}
File.WriteAllText(outFile, sb.ToString());

ClosedXML

Microsoft 純正の Open XML SDK をそのままで使うのはあまりにも面倒なので、それを使いやすくしたライブラリーです。ベースとしてOpen XML SDK を使うので、対応する Excel のファイル形式は、Excel2007以降のOpen XML形式のものになります。Open XML SDK がベースなので機能は豊富ですが、処理は少し重いです。.NET Core 対応版は現在開発中のようです。

sheet.Cells()を使って処理をすると処理がものすごく遅くなるので、sheet.Rows()を使ってforeachで行ごとに処理をしています。

example.cs
var workbook = new ClosedXML.Excel.XLWorkbook(inFile);
ClosedXML.Excel.IXLWorksheet sheet = workbook.Worksheets.Worksheet(1);
var sb = new StringBuilder();
var rows = sheet.LastRowUsed().RangeAddress.FirstAddress.RowNumber;
var columns = sheet.LastColumnUsed().RangeAddress.FirstAddress.ColumnNumber;

foreach(var row in sheet.Rows())
{
    foreach (var cell in row.Cells())
        sb.Append(cell.GetString() + "\t");
    sb.Remove(sb.Length - 1, 1);
    sb.Append("\n");
}
File.WriteAllText(outFile, sb.ToString());

パフォーマンス比較

Excelのファイルを読み込みTSV形式で出力するコンソールアプリケーションを作成し、それの起動から終了までの時間を測定しました。Excelのファイルは、589行☓235列の表でxlsx形式で829KBのものを使いました。

テストに使ったソースコード及びデータは、GitHubの方に公開してあります。まだ、整理はできていませんが興味があればみてください。

テスト結果

Windows と Linux は、同一のPCでデュアルブートで立ち上げたものを使っています。処理時間の単位はミリ秒です。

Windows(Windows10)

ライブラリ バージョン xlsx 読込処理時間 xls 読込処理時間
openpyxl 2.5.0 1848 -
Pandas (xlrd) 0.22.0 (1.1.0) 1148 755
ExcelDataReader(.NET Framework) 3.4.0 641 392
ExcelDataReader(.NET Core) 3.4.0 665 422
EPPlus(.NET Framework) 4.1.1 1328 -
EPPlus(.NET Core) 4.5.0.2 RC 1339 -
NPOI(.NET Framework) 2.3.0 1004 394
DotNetCore.NPOI(.NET Core) 1.0.2 1080 421
ClosedXML(.NET Framework) 0.91.0 1542 -

Linux(Ubuntu 16.04)

ライブラリ バージョン xlsx 読込処理時間 xls 読込処理時間
openpyxl 2.5.0 1464 -
Pandas (xlrd) 0.22.0 (1.1.0) 951 578
ExcelDataReader(.NET Core) 3.4.0 765 541
EPPlus(.NET Core) 4.5.0.2 RC 2077 -
DotNetCore.NPOI(.NET Core) 1.0.2 1076 498

結論

テストに使った Excel ファイルは普通に使っているファイルとしてはかなり大きなサイズで、どのライブラリーも2秒以内で処理できているので、どれを使っても実用的には問題がないと思われます。

処理が一番早かったのは、予想どおりExcelDataReaderでした。比較的使いやすいライブラリーなので、データを読むだけならExcelDataReaderがベストだと思います。

Pandas (xlrd)は、予想していたよりも処理が速く、EPPlusを使うよりも処理時間は短くなっています。Python の Pandas を使うとプログラムを書くのが簡単で、データフレームに読み込んだ後は、Pandas, NumPy, Scipy, MatplotLib 等の Python の優秀なデータサイエンスライブラリーで処理ができるので、自分は今後 Pandas を主に使っていこうと思います。Pythonの計算処理は遅いですが、NumPy, Scipyをうまく使う事で C# を使うよりも速く処理させることができそうです。C# の方は、配列やリストの処理がそれほど速くなく、それに適当な数値処理ライブラリーがありません。今頃になって Span で配列の処理を速くする話が出てきていますが、利用環境が整備されるまでには時間がかかると思います。

また、.NET C# の大きな問題としては、サーバーで MatplotLib のようなグラフ描画できる適当なソフトがないことです。マイクロソフトとしては SQL Server の Reporting Services や Power BI を使えということのようですが、コストや処理速度を考えると使いたいとは思いません。サーバーでグラフ描画ができる適当なソフトがない原因としては、System.Drawing の問題があります。System.Drawing は、ASP.NET ではサポートされていません(参照「ASP.NET でお絵かき」)。また、.NET Core でも corefx ではサポートされず、やっと Windows Compatibility Pack for .NET Core の中で提供される予定です。サーバーで描画ができる適当なライブラリーがない状態では、MatplotLib のような優秀なグラフ描画ソフトが作られるわけがありません。

Windows環境とLinux環境を比較すると、Pythonの場合はLinuxの方が処理が早いですが、.NET Core は Linux の場合まだ最適化が十分できていないので Windows で動かすより2割ぐらい遅くなります。

次は、Excelファイルを作成する場合のまとめを書きたいと思っています。この場合は、Python であれば openpyxl、C# であれば EPPlus か ClosedXML を使うのがいいと思っています。