新人エンジニアのつぶやき

日々の学びをアウトプットするためのブログです

【npm パッケージ】 処理速度を指標とした CSV を解析するライブラリの選定

この記事について

いいコードはいい心から

フロントエンドの CSV ファイル解析パッケージについて、2つの npm パッケージを比較しました。 今回比較したのはpapaparsecsv-parseです。比較の観点としては、3つです。高速に処理できること登録されている場合でも正常に文字列型に変換できることQiita の記事などから広く使われており、知見が多くあることです。比較した結果、条件を満たす papaparse を使用することになりました。

背景

そもそもなぜ CSV ファイルを解析するツールの選定を行うことになったか、それは私が現在携わっている農業分野で特有の「合筆」が関係しています。合筆とは複数の土地を法的に 1 つの土地に合体することです。例えば、東京都千代田区丸の内 1-2 と東京都千代田区丸の内 1-3 の2つの土地がある場合、登記簿には2つをまとめて東京都千代田区丸の内 1-2,1-3 として登録するのです。既存の実装ではこの合筆を含む CSV ファイルは文字列型に変換できず、処理が止まってしまうという問題がありました。これを解決するために、オープンソースのパッケージの中で合筆を含む CSV ファイルを解析できるものを探しました。

選定方法

npm のパッケージから CSV ファイルを解析するためのパッケージを選定しました。条件としては以下の通りです。

  • データの中身としては地番(住所のようなもの)、タイプ、管理者名、作物名、土地の面積が格納されている(例:'東京都千代田区丸の内 1-2, 1-3', '1', '丸の内太郎', 'とまと', '10')。地番は合筆で登録されている場合でも正常に文字列型に変換できること
  • 1回の処理で最低 1000 件のデータを含む CSV ファイルを処理する必要がある。その場合でも高速に処理できること。
  • Qiita の記事などから広く使われており、知見が多くあること。

3 つの条件を満たせそうなパッケージの中でpapaparsecsv-parseを選択しました。

データの確認と処理時間の計測のためのサンプルコード

以下のようなサンプルコードを作成し、データの確認と処理時間の計測を実施しました。 * papaparseの場合

import fs from "fs";
import Papa from 'papaparse';
import { performance } from "perf_hooks";

const file = fs.readFileSync("./csv/sample.csv", "utf8");

const startTime = performance.now(); // 開始時間

const result = papa.parse(file); // papaparseを用いた処理

const endTime = performance.now(); // 終了時間

console.log("計測時間[ms]:", endTime - startTime); // 何ミリ秒かかったかを表示する

console.log(result);
  • csv-parseの場合
import fs from "fs";
import { parse } from "csv-parse/sync";
import { performance } from "perf_hooks";

const file = fs.readFileSync("./csv/sample.csv", "utf8");

const startTime = performance.now(); // 開始時間

const result = parse(file, { skip_empty_lines: true }); // csv-parseを用いた処理

const endTime = performance.now(); // 終了時間

console.log("計測時間[ms]:", endTime - startTime); // 何ミリ秒かかったかを表示する

console.log(result);

csv-parse ではskip_empty_lines: trueを指定することで空行のレコードをスキップしてくれます。

計測結果

  • csv-parse の場合
計測時間[ms]:6.990803003311157
// resultの内容
  data: [
    [ '地番', 'タイプ', '管理者名', '作物', '面積(㎡)' ],
    [ '東京都千代田区丸の内1-2, 1-3', '1', '不明', '不明', '1' ],
    [ '東京都千代田区丸の内1-4, 1-5', '3', '不明', '不明', '1' ],
    [ '東京都千代田区丸の内2-1', '3', '不明', '不明', '1' ],
    [ '東京都千代田区丸の内2-2', '3', '不明', '不明', '1' ],
    ... 995 more items
  ],
  • papaparse の場合
計測時間[ms]:34.444244146347046
// resultの内容
  data: [
    [ '地番', 'タイプ', '管理者名', '作物', '面積(㎡)' ],
    [ '東京都千代田区丸の内1-2, 1-3', '1', '不明', '不明', '1' ],
    [ '東京都千代田区丸の内1-4, 1-5', '3', '不明', '不明', '1' ],
    [ '東京都千代田区丸の内2-1', '3', '不明', '不明', '1' ],
    [ '東京都千代田区丸の内2-2', '3', '不明', '不明', '1' ],
    ... 995 more items
  ],

2つとも合筆を含む CSV ファイルを解析することができた。

また計測時間の平均値を下記に示す。(n = 3) | csv-parse を用いた場合の計測時間の平均値[ms] | papaparse を用いた場合の計測時間の平均値[ms] | | -------------------------------------------- | -------------------------------------------- | | 2.9 | 37.93 |

以上の結果より、Papa Parse を使用し、実装を進めることにしました。

最後に

今回は合筆した地番が存在する CSV ファイルを解析するパッケージについての比較、処理時間の計測を実施しました。npm のパッケージでCSV Parseと調べると、267 パッケージも出てきます。react-parse-csv や vue-papa-parse などフレームワークに特化したようなものもあるようです。パッケージの選定はより難しくなっていきそうなので、個人やチームの中で基準を持っておくことが大切なのかなと感じました。

おまけ

  • データの件数ごとの計測時間の比較結果を以下に示します。データを増やすほど、papaparseの恩恵を受けやすいようです。
データの件数 papaparseを用いた場合の計測時間[ms] csv-parseを用いた場合の計測時間[ms]
10 2.356204032897949 2.863926887512207
100 2.318110942840576 7.995607137680054
1000 3.1772091388702393 30.95689105987549
3000 4.6432719230651855 49.76654601097107
5000 8.524864912033081 57.25490593910217
10000 18.785115003585815 65.55030989646912