Nysol の Mコマンドで CSV ファイルをお手軽に解析

この記事は[SRA Advent Calendar 2019](https://qiita.com/advent-calendar/2019/sra)の25日目の記事です。

関西事業部の安井です。
今日はクリスマスですね！メリークリスマス！

今年は皆さんがあまり知らないのでは？と思うツールを紹介したいと思います。

現在のプロジェクトでは CSV ファイル(の中身)を処理することが多く、ちょっとした確認(行数だったり、ユニークな値の抽出など)がしたかったり、次の処理のためにデータを加工すること(ある条件に一致する行だけ抽出したり、他のファイルと結合させるなど)が多くあります。
確認する方法として、Excel でフィルターをかけて目視確認したり、Python の Pandas.DataFrame を用いて処理させたりすることもありますが、今回は Nysol の Mコマンドという CSV ファイルの処理に特化したツールを簡単に紹介します。

# Nysol とは
「大規模データの解析に関する様々な大学やプロジェクトでの研究成果を広く産業界に還元する目的で構築されたソフトウェアツールの総称およびそのプロジェクト活動のこと。」だそうです。日本発の OSS であり、UNIX 環境(Linux, Macなど)での動作を前提としています。今回は Mコマンドのみの紹介ですが、Nysol は大規模データの解析に役立つ複数のソフトウェア群からなっています。ちなみに"Nysol"とはアイヌ語で「雲」を意味するそうです。詳細は https://www.nysol.jp/ をご覧ください。

# Mコマンド(MCMD)とは
Mコマンド(MCMD とも表記する)とは、大規模表構造データ(CSV)を高速に処理する目的で開発されたコマンド群のことを指します。M は開発者(松田康之氏)のイニシャルからきているそうです。Mコマンドは、単一の機能(例えば、並べ替えや表の結合など)に特化したコマンドが80種類程存在しています。下記で説明しますが、使い方はとても簡単で Linux のコマンドのようにコンソール上で気軽に利用することができます。公式HPには「Mコマンドを使えば、標準的な PC であっても、数億件規模のデータ処理が可能」とあります。(標準的な PC がどの程度を指しているのかはわかりませんが。。。)

# 動作環境
Linux, MacOSX, Bash on Ubuntu on Windows などの代表的な OS での動作確認はされているようです。(私は MacOS/Ubuntu 上での動作を確認しました。)
また Docker イメージも公開されているので、Docker でも動かすことができます。(こちらも動作確認済みです。)
さらに Python や Ruby 上でも動かすことのできるライブラリが開発されているようです。(こちらは動作未確認です。)

# インストール方法
公式サイトの手順に従えば、特に問題なくインストールすることができます。
詳細はそちらを参考にしてください。

- OS へのインストール -> [こちら](https://github.com/nysol/mcmd/wiki#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)
- Docker イメージ -> [こちら](https://www.nysol.jp/software/nysol_docker)
- Python ライブラリ -> [こちら](https://github.com/nysol/nysol_python/wiki)
- Ruby ライブラリ -> [こちら](https://github.com/nysol/rubym/wiki)

# 使い方
インストールさえできてしまえば、使い方は簡単です。

## 基本書式
基本書式は下記の通りです。

```
[Mコマンド] i=<入力ファイルパス> o=<出力ファイルパス> <オプション>
```

- コンソール上で上記書式を入力するだけで実行できます。
- 後は、処理したい内容に対応したMコマンドを利用するだけです。
- oオプションは出力ファイルパスの指定となりますが、指定なしの場合は標準出力となります。
- 上記書式はあくまで基本であり、Mコマンドによっては必須の指定項目が存在します。
- 各コマンドの詳細は[こちら](https://www.nysol.jp/mcmd2/jp/index.html)から利用したいコマンドを参照してください。

今回Mコマンドを紹介するにあたって利用するデータは下記の記事で扱った「精神面での健康及び精神疾患の頻度に対する考え方を調査したアンケート結果」をデータセットを利用します。

- [精神病患者の判定しようとしたら前処理に時間がかかった話](https://qiita.com/totti0414/items/2d5fd4e55efd8dca78fc)
- [精神面での健康及び精神疾患の頻度に対する考え方を調査したアンケート結果](https://www.kaggle.com/osmi/mental-health-in-tech-survey)

ただし、今回は結果を見やすくするため、下記のような加工処理を事前に施したデータセットを利用することにします。

- "Timestamp, Age, Gender, Country, state"カラムのみを利用する
- "Gender"の値が"Male"と"Female"の値だけを用いる

では、Mコマンドの一部を紹介します。

## mcount

データの行数などを確認したいときに利用します。

#### 書式

```bash
mcount a=<パラメータ> k=<パラメータ> i=<入力パス> <オプション>
```

- a には出力時の追加カラム名を設定する
- この追加したカラムに対して結果(この場合だと行数)が出力される
- k にはカウントの単位となるカラム名のリストを指定する(複数のカラム指定時はカンマ区切り)
- k の指定が無い場合は、行数全体が出力される

#### ファイル全体の行数を知りたい
ファイル全体の行数を知りたいときは、下記のようにします。

```bash
# 実行コマンド
mcount a=count i=survey.csv
# 以下、標準出力内容
Timestamp,Age,Gender,Country,state,count
2016-02-01 23:04:31,25,Male,United States,IL,736
#END# kgcount a=count i=survey.csv; IN=736 OUT=1; 2019/12/23 20:14:26
```

結果として、countカラムが追加され、"736"と出力されていることがわかります。
このことからこのファイルにはデータ行が"736"行存在することがわかります。
※実はMコマンド実行結果には、IN=で入力データ数が表示されるので、ファイルの行数を知りたいだけであれば、他のコマンドの実行ついでに確認することが可能です。

#### ある特定のカラムの値毎にデータ数を知りたい
このような場合にもこのコマンドが利用できます。
例えば、Genderの値毎に結果を知りたいときは、下記のようにします。

```bash
# 実行コマンド
mcount k=Gender a=count i=survey.csv
# 以下、標準出力内容
Timestamp,Age,Gender%0,Country,state,count
2014-08-27 12:55:01,35,Female,United States,NY,121
2016-02-01 23:04:31,25,Male,United States,IL,615
#END# kgcount a=count i=survey.csv k=Gender; IN=736 OUT=2; 2019/12/23 20:15:08
```

Genderの値が"Female"のデータ数が121、"Male"のデータ数が615存在することがわかります。

## mcut

特定のカラムのみを抽出したいときにこのコマンドを利用します。

#### 書式

```bash
mcut f=<パラメータ> i=<入力パス> <オプション>
```

- fには抜き出したいカラム名のリストを指定する(複数のカラム指定時はカンマ区切り)

#### 特定のカラムのみを抽出したい

例えば、GenderとCountryのみを抽出したいときは、下記のようにします。

```bash
# 実行コマンド
mcut f=Gender,Country i=survey.csv o=out_mcut.csv
# 以下、標準出力内容
#END# kgcut f=Gender,Country i=survey.csv o=out_mcut.csv; IN=736 OUT=736; 2019/12/23 20:23:29
# head コマンドで中身確認
head -n 5 out_mcut.csv
# 以下、標準出力内容
Gender,Country
Female,United States
Male,Canada
Male,United Kingdom
Male,United States
```

GenderとCountryカラムだけが抽出されていることが分かります。

## muniq

値が重複した行を単一化したいときにこのコマンドを利用します。

#### 書式

```bash
muniq k=<パラメータ> i=<入力パス> <オプション>
```

- kには行を単一化する単位となるカラム名のリストを指定する(複数のカラム指定時はカンマ区切り)

#### 特定のカラムにどんな値が存在するのか確認したい

例えば、Countryカラムにどんな値が存在するのか確認したいときは、下記のようにします。

```bash
# 実行コマンド
muniq k=Country i=survey.csv
# 以下、標準出力内容
Timestamp,Age,Gender,Country%0,state
2014-08-27 23:30:52,27,Male,Australia,NA
2014-12-01 09:58:28,31,Female,Belgium,NA
2014-08-27 17:19:05,25,Male,Bosnia and Herzegovina,NA
2014-08-29 11:25:30,28,Male,Brazil,NA
2014-08-27 15:23:07,41,Male,Canada,NA
2014-09-08 16:46:53,26,Male,Colombia,NA
2014-08-27 12:36:18,38,Male,Costa Rica,NA
2014-08-28 10:25:39,43,Male,Croatia,NA
2015-02-21 08:54:40,33,Male,Czech Republic,NA
2014-08-28 14:51:46,35,Male,Denmark,NA
2014-08-27 15:31:38,34,Male,Finland,NA
2014-08-28 10:30:04,24,Male,France,NA
2014-09-14 09:04:08,20,Male,Georgia,NA
2014-08-27 13:08:17,30,Male,Germany,NA
2014-08-29 12:48:20,34,Male,Greece,NA
2014-08-28 08:23:11,22,Male,India,NA
2014-08-27 12:58:42,37,Male,Ireland,NA
2014-08-27 16:23:14,27,Male,Israel,NA
2014-08-28 16:56:26,37,Male,Italy,NA
2014-08-28 17:10:08,43,Male,Mexico,NA
2014-09-01 09:12:15,26,Male,Moldova,NA
2014-08-28 04:02:47,22,Female,Netherlands,NA
2014-08-27 17:33:52,51,Male,New Zealand,NA
2014-08-28 04:24:03,25,Male,Nigeria,NA
2015-02-21 09:19:12,31,Male,Philippines,NA
2014-08-27 11:49:51,26,Female,Poland,NA
2014-08-27 11:41:50,38,Male,Portugal,NA
2014-08-28 07:03:00,26,Male,Russia,NA
2014-08-27 21:45:31,27,Male,Singapore,NA
2014-08-27 12:53:15,38,Female,South Africa,NA
2014-08-27 14:52:53,25,Female,Sweden,NA
2014-08-27 14:13:24,30,Male,Switzerland,NA
2014-08-28 11:21:48,40,Male,Thailand,NA
2014-08-27 13:44:05,27,Male,United Kingdom,NA
2016-02-01 23:04:31,25,Male,United States,IL
#END# kguniq i=survey.csv k=Country; IN=736 OUT=35; 2019/12/23 21:24:52
```

見にくいですが、Countryカラムの値が重複する行は単一化されています。

## パイプによるMコマンドの連結

上記で Country カラムにどんな値が存在するのか確認したい、という目的で muniq コマンドを用いましたが、パイプで各コマンドの結果を繋いでデータを加工することでより見やすくなります。

#### 書式

```
[Mコマンド] i=<入力パス> <オプション> | [Mコマンド] <オプション> | [Mコマンド] <オプション> ...
```


#### 特定のカラムにどんな値が存在するのか確認したい(より見やすく)

先ほどの Country カラムに存在する値の確認を例にすると、Country カラムで行を単一化したのち、Country カラムのみを抽出してやれば、より見やすくなります。

```bash
# 実行コマンド
muniq k=Country i=survey.csv | mcut f=Country
# 以下、標準出力内容
#END# kguniq i=survey.csv k=Country; IN=736 OUT=35; 2019/12/23 21:34:54
Country%0
Australia
Belgium
Bosnia and Herzegovina
Brazil
Canada
Colombia
Costa Rica
Croatia
Czech Republic
Denmark
Finland
France
Georgia
Germany
Greece
India
Ireland
Israel
Italy
Mexico
Moldova
Netherlands
New Zealand
Nigeria
Philippines
Poland
Portugal
Russia
Singapore
South Africa
Sweden
Switzerland
Thailand
United Kingdom
United States
#END# kgcut f=Country; IN=35 OUT=35; 2019/12/23 21:34:54
```

このように複数のMコマンドをパイプで連結させることで、様々な加工が可能となります。


# その他

特筆すべき特徴として、ファイルサイズが大きくなればなるほど、Nysol は Python の Pandas.DataFrame と比較して非常に速いと言われており、私も業務上ファイルサイズが大きくなればなるほど速くなることを体感しています。速度比較に関しては、下記の記事が参考になります。

- [100MB超えたらnysolが良さそう](https://qiita.com/gg_hatano/items/7a11e3a203a7646f05bf)
- [データをなるはやで前処理したい＃１](https://qiita.com/adachi-h/items/d6bb968b4ac29a562aaa)


# まとめ

今回は CSV の処理ツールとして Nysol のMコマンドを簡単に紹介しました。今回は簡単なコマンドしか紹介しませんでしたが、Mコマンドにはたくさんの機能があるので、組み合わせれば複雑な処理も可能となります。ぜひ皆さんも一度利用されてみてはいかがでしょうか。

それでは良いお年をー！