株式会社クリアコード > ククログ

ククログ


Apache Arrowの最新情報(2018年9月版)

Apache ArrowPMC(Project Management Commitee、プロジェクト管理チームみたいな感じ)のメンバーの須藤です。

みなさんはApache Arrowを知っていますか?聞いたことがないとか名前は聞いたことがあるとかコンセプトは知っているあたりがほとんどで、触っている人はあまりいないのではないでしょうか。Apache Arrowは数年後にはデータ処理界隈で重要なコンポーネントになっているだろうプロジェクトです。データ処理界隈に興味がある人は知っておくと役に立つはずなので2018年9月現在の最新情報を紹介します。

私は、PMCの中では唯一の日本人で、コミット数は3番目に多いので、日本ではApache Arrowのことをだいぶ知っている方なはずです。日本語でのApache Arrowの情報があまりないので日本語で紹介します。ちなみに、英語ではいろいろ情報があります。有用な情報源はApache Arrowの公式ブログ公式メーリングリストやそれぞれの開発者のブログ・発表などです。この記事ではそれらの情報へのリンクも示しながら最新情報を紹介するので、ぜひ英語の情報も活用してください。

Apache Arrowが実現すること

Apache Arrowが目指していることはメモリー上でのデータ処理を効率化することです。

「効率化」には2つの観点があります。1つは「速度」です。速いほど効率的ということです。もう1つは「実装コスト」です。実装コストが低いほど効率的ということです。

速度

まず「速度」の方から説明します。

Apache Arrowは大量のデータを処理するケースを想定しています。そのため、速度のことを考えるときも大量のデータを処理する前提で考えます。

大量のデータを処理するときにネックになる部分を解消し、高速化できる部分を最適化していくことで速く処理できるようになります。

Apache Arrowが注目したネックになる部分はデータ交換の部分です。

Apache Arrowが注目した高速化できる部分は並列処理の部分です。

順番に説明します。

データ交換

ここではネックになる部分の解消について説明します。対象となる処理はデータ交換処理です。

大量のデータを扱う時は複数のシステムが連携してデータを処理します。たとえば、データを集めるシステム・データを永続化するシステム・データを前処理するシステム・データを集計するシステムなどです。それぞれのシステム間でデータを交換する必要がありますが、そのときのデータのシリアライズ・デシリアライズ処理にかかるコストが結構かかるのです。たとえば、JSONでデータ交換するとした場合、データをJSONにフォーマットするコストとJSONをパースしてプログラムで扱える形式にするコストが結構かかるということです。

そこで、Apache Arrowは各種システムで共通で使えるシリアライズ・デシリアライズコストがすごく低いデータフォーマットの仕様(Apache Arrowフォーマット)を作っています。基本的なアイディアは「パースしない」です。プログラムから直接扱えるフォーマットでデータを配置することでパースせずにそのままデータを扱えます。これはデータをシリアライズするときも効率的です。プログラムで扱っているデータをそのまま書き出せばよいのでシリアライズコストがほとんどありません。なお、パースしないデータフォーマットの実現にはFlatBuffersを使っています。

Apache Arrowフォーマットで一番うまみがでるのは、各システムすべてがApache Arrowフォーマットに対応しているときです。Apache Arrowフォーマットに対応していないシステムがあるとそのシステムとのデータ交換時にApache Arrowフォーマットを使えないからです。つまり、そのシステムとのデータ交換コストが高くなります。

データ交換コストを下げることによる速度向上の実現にはApache Arrowフォーマットを広く使える状況になっていなければいけません。そのため、より多くの言語でApache Arrowフォーマットを扱えるように開発を進めています。現在Apache Arrowフォーマットを読み書きできる言語は次の通り(アルファベット順)です。

  • C
  • C++
  • Go
  • Java
  • JavaScript
  • Lua
  • MATLAB
  • Python
  • R
  • Ruby
  • Rust

完成度が高いのはC++とJavaです。C++のバインディングとして実装されているC、Lua、Python、Rubyも完成度が高いです。MATLABとRもC++のバインディングですがまだ開発が始まったばかりで完成度はそれほどではありません。JavaScriptとGoとRustはバインディングではなくその言語で1から実装しています。これらの言語の実装は、主な型(たとえば数値型)はサポートしているがまだサポートしていない型もある、という状況です。Juliaの実装も進んでいます。各種言語での実装状況の詳細は後述します。

並列処理

ここでは高速化できる部分の最適化について説明します。対象となる処理は並列処理です。

大量のデータを高速に処理するには次の両方またはどちらかを実現します。

  • 1つのデータをより速く処理する
  • 同時に複数のデータを処理する(並列処理)

Apache Arrowのデータフォーマットはできるだけデータを局所化する配置になっています。局所化するとCPUのキャッシュメモリーを活用しやすくなり「1つのデータをより速く処理する」ことにつながります。局所化した上でデータの境界を64bitに揃えるとSIMDを有効活用しやすくなります。SIMDを活用できると1回の命令で複数のデータを処理できるため「同時に複数のデータを処理する」ことにつながります。

Apache ArrowのデータはOLTP(OnLine Transaction Processing)(ECサイトで購買するような処理)よりもOLAP(OnLine Analytical Processing)(データから探索的に知見を探し出すような処理)で性能がでるように設計されています。OLTPとOLAPでは処理の内容が違うので、データの配置の仕方で実現しやすさが変わってきます。RDBMSを知っている人向けな説明をすると、OLTPでは行単位の処理(行の追加・削除など)が多くて、OLAPでは列単位の処理(COUNTGROUP BYなど)が多いです。OLTPでは行単位でデータが固まっていた方が性能がでやすく、OLAPでは列単位でデータが固まっていた方が性能がでやすいです。Apache ArrowはOLAP向けの設計なので列単位でデータをまとめています。このようなデータの持ち方を「カラムナー」と言います。

「速度」のまとめ

Apache Arrowのデータフォーマットは速く大量のデータを処理するために次のことをうまくできるような仕様になっています。

  • データ交換
  • 並列処理

データ交換のためにシリアライズ・デシリアライズコストが小さくなるように設計されています。また、より多くの言語で読み書きできるようにライブラリーの整備を進めています。

並列処理のためにデータの配置方法を工夫しています。最近のCPU・GPUで高速に処理できる配置になっています。

実装コスト

Apache Arrowはメモリー上でのデータ処理を効率化することを目指しています。「効率化」のために「速度」と「実装コスト」に着目しています。ここまでで「速度」について説明したので次は「実装コスト」について説明します。

メモリー上でのデータ処理するソフトウェアはたくさんあります。速度が重要なソフトウェアも多いです。速度が必要なソフトウェアでは速くするためにいろいろな工夫を実装します。これにはそれなりの時間がかかります。

Apache Arrowは高速に大量のデータをメモリー上で処理するときに必要な機能をライブラリーとして提供しようとしています。Apache Arrowをインフラとして使うことで同じような工夫をしなくて済むようにしたい、多くのソフトウェアで重要な部分は協力してみんなでよいものを開発して共有したい、ということです。

最近では次のような処理をApache Arrowに取り込む動きがあります。

なんでもかんでも取り込むとメンテナンスしにくくなって開発が停滞する危険性もあるのですが、そのあたりはバランスを取りながら判断していくはずです。

Apache Arrowが実現することのまとめ

Apache Arrowは効率的に大量のデータをメモリー上で処理することを目指しています。そのためにしていることは次の通りです。

  1. データ交換・高速処理しやすいApache Arrowフォーマットの仕様を定義
  2. 各種言語用のApache Arrowフォーマットを読み書きするライブラリーを開発
  3. 大量のメモリー上のデータを高速処理するためライブラリーを開発

Apache Arrowの位置付けの説明はこのくらいにして、どのような用途に向いているか、現状はどうなっているか、今後どうなっていくかについて説明します。

Apache Arrowが向いている用途

Apache Arrowはどんな用途にでも向いているわけではありません。得意なこと不得意なことがあります。用途にあわせて使い分けましょう。

Apache Arrowは複数のシステムが協調して大量のデータを処理するユースケース向けに設計されています。そのため、次の用途に向いています。

  • 大量データの交換
  • メモリー上での大量データの分析処理

シリアライズ・デシリアライズコストの低いデータフォーマットな点が「大量データの交換」に向いています。

高速処理に向いたデータ配置・高速な処理をライブラリーで提供している点が「メモリー上での大量データの分析処理」に向いています。

もし、ファイルサイズが気にならないなら「処理結果の一時的なキャッシュ」にも有用です。ファイルサイズが気になるならApache Parquetフォーマットの方が向いています。

一方、永続化には向いていません。たとえば、ログをApache Arrowフォーマットで保存するというのは向いていませんし、データベースのデータをApache Arrowフォーマットで保存するというのにも向いていません。

永続化用のデータフォーマットではサイズが要重なポイントになるケースが多いです。サイズが小さい方がたくさんのデータを保存できますし、データを読み書きするときのI/Oが減るからです。Apache Arrowはシリアライズ・デシリアライズコストを低くすることを重視しているので、サイズを小さくすることに関してはそれほどがんばれません。たとえば、Zstandardなどで圧縮するとApache Arrowのメリットの一部が薄れてしまいます。そのメリットとは「ゼロコピー」です。

ゼロコピーはデータをコピーしないことです。Apache Arrowフォーマットではデータをコピーせずにそのまま扱えるように設計されています。具体的には次のように設計されています。

  • データをプログラムから直接効率よく扱える配置にしている
  • データは基本的にリードオンリーで扱う

プログラムから直接効率良く扱える配置にしてあるとそのままデータを扱えます。システムが提供するメモリーマップ機能を使えばゼロコピーでデータを扱えます。しかし、直接扱えるデータでも圧縮すると伸張しなければ使えません。つまり、「直接効率よく扱える配置」ではなくなってしまいます。そのため、新しい領域を用意して伸張したデータを格納する必要があります。コピーのような挙動です。大量データを処理する想定なのでこのような伸張するコストが無視できないことも多いです。ただ、ネットワーク越しにデータ交換する時のようにネットワーク上のデータ送受信コストの方が圧縮・伸張コストよりも高いことがあります。このような場合は圧縮した方が割に合うので、用途によって使い分けます。(まだ圧縮機能はサポートされていませんが、ZstandardやLZ4を扱うための部分は実装済みです。ARROW-300 [Format] Add buffer compression option to IPC file format参照。)

リードオンリーとゼロコピーについても説明します。データが変更される可能性があると、安全にデータを処理するためにはデータをコピーするか排他制御をして扱わないといけません。大量データを処理する想定なのでコピーするコストは無視できません。排他制御をすると並列度が下がりがちで速度が犠牲になります。そのため、Apache Arrowはリードオンリーでデータを扱う設計になっています。

このようにApache Arrowはゼロコピーを大事にした実装になっています。Apache Arrowではゼロコピーはよくでてくるキーワードなので覚えておいてください。

参考:

Apache Arrowが向いている用途は次の用途だということを説明しました。

  • 大量データの交換
  • メモリー上での大量データの分析処理

つづいて、現状はどうなっているか、今後どうなっていくかについて説明します。

Apache Arrowの現状

2018年9月現在、Apache Arrowのデータフォーマットの仕様と実装がどのようになっているかを説明します。

データフォーマットの仕様は今年中には固める方向で進んでいます。仕様が固まったらApache Arrow 1.0がリリースされる予定です。現時点での最新バージョンは0.10.0です。9月中に0.11.0がリリースされる予定です。

実装は、データ交換のための実装(Apache Arrowフォーマットを扱うための実装)がだいたい完成してきていて、Apache Arrowフォーマットのデータを高速に処理するための実装に比重が移ってきています。

それでは詳細を説明していきます。

扱えるデータ

まずはApache Arrowが扱えるデータを説明します。

現在Apache Arrowが扱えるデータは次の通りです。

  • データフレーム
  • 密な多次元配列

それぞれ説明します。

データフレーム

Apache Arrowはもともと「データフレーム」なデータを扱うために開発が始まりました。「データフレーム」は表形式のデータです。RDBMSのテーブルのようなものです。1つのテーブルには1つ以上のカラムがあり、それぞれのカラムは違う型にできます。

現在、Apache Arrowがサポートしている型は次の通りです。これらの型のデータを扱えるということです。

  • 真偽値(1bit)
  • 整数
    • 8bit非負整数(リトルエンディアン)
    • 8bit整数(リトルエンディアン)
    • 16bit非負整数(リトルエンディアン)
    • 16bit整数(リトルエンディアン)
    • 32bit非負整数(リトルエンディアン)
    • 32bit整数(リトルエンディアン)
    • 64bit非負整数(リトルエンディアン)
    • 64bit整数(リトルエンディアン)
  • 浮動小数点数
    • 16bit浮動小数点数
    • 32bit浮動小数点数
    • 64bit浮動小数点数
  • 小数(精度・スケールを指定)
  • 可変長UTF-8文字列
  • バイナリーデータ
    • 可変長バイナリーデータ
    • 固定長バイナリーデータ
  • 日付
    • UNIXエポックからの経過日数(32bit)
    • UNIXエポックからの経過ミリ秒数(64bit)
  • タイムスタンプ(64bit整数)
    • UNIXエポックからの経過秒数
    • UNIXエポックからの経過ミリ秒数
    • UNIXエポックからの経過マイクロ秒数
    • UNIXエポックからの経過ナノ秒数
  • 時刻
    • 深夜0時からの経過秒数(32bit整数)
    • 深夜0時からの経過ミリ秒数(32bit整数)
    • 深夜0時からの経過マイクロ秒数(64bit整数)
    • 深夜0時からの経過ナノ秒数(64bit整数)
  • リスト(0個以上の同じ型の値を持つ型)
  • 構造体(1個以上のフィールドを持つ型で、各フィールドは別の型にできる)
  • 共用体(1個以上のフィールドを持つ型で、各フィールドは別の型にでき、どれか1つのフィールドの値のみが設定されている)
  • 辞書
    • 統計っぽい説明:名義尺度なカテゴリーデータ
    • 実装よりの説明:各値に整数でIDを割り当て、数値で値を表現する型
    • scikit-learnを知っている人向けの説明:sklearn.preprocessing.LabelEncodertransform結果を値として使う型

なお、どの型でもnullを設定できます。

多次元配列

Apache Arrowはデータフレームを想定して開発が始まりましたが、多次元配列も扱えるとよさそうというフィードバックがありApache Arrow 0.3.0から多次元配列をサポートしました。

参考:ARROW-550 [Format] Add a TensorMessage type

データフレームと多次元配列の違いは次の通りです。

  • 次元数:
    • データフレームは2次元
    • 多次元配列はn次元
  • 型:
    • データフレームはカラム毎に違う型を使える
    • 多次元配列の要素の値はすべて同じ型

Pythonでいうと、pandasが実現しているのがデータフレームで、NumPyが実現しているのが多次元配列です。

多次元配列には密(dense)な多次元配列と疎(sparse)な多次元配列があります。密な多次元配列は要素の値をすべて持っている多次元配列で、疎な多次元配列は0以外の要素の値だけを持っている多次元配列です。

たとえば、[0, 0, 0, 1, 0]という多次元配列(1次元配列)があった場合、5要素全部の値を保持しているのが密な多次元配列で、「4番目の要素の値が1でそれ以外は0」という情報だけ保持しているのが疎な多次元配列です。

Apache Arrowは現在は密な多次元配列のみサポートしています。疎な多次元配列もサポートする予定があります。現在は妥当なデータフォーマットの仕様を検討する人を募集している状態です。疎な多次元配列の知見がある人はぜひ取り組んでみてください。

参考:ARROW-854 [C++] Support sparse tensor

データ処理部分の実装

Apache Arrowは各種データ処理ツールが共通で使える高速なデータ処理機能の開発も重視しています。これまでデータフォーマットの方に注力していたためまだあまり進んでいませんが、現時点できることを紹介します。

データ処理部分の実装に着手しているのはC++、JavaScript、Goでの実装だけなので、それぞれの言語での実装ごとに紹介します。

なお、データ処理部分が実装されていない言語あるいは実装が始まっている言語でも未実装の処理はできないかというとそうでもありません。読み込んだApache Arrowフォーマットのデータを他の既存のライブラリーで扱えるように変換することで処理できます。コピーやデータの変換が必要になるので多少非効率ですが、Apache Arrowフォーマットによりデータ交換部分のコストがすごく効率化されるので割に合うことが多いです。

たとえば、Apache Sparkではデータ交換部分でApache Arrowを使うことにより大きく速度向上しています。Apache Sparkには処理の一部をPythonで実装できるPySparkという機能があります。Apache Spark本体はScalaで実装されているので別プロセスで動いているPythonにデータを渡す必要があります。このときにソケット経由でApache Arrowデータを渡しています。従来はPythonに標準で含まれているpickleを使ってデータを転送していました。Apache Arrowフォーマットで受け取ったデータはpandasのオブジェクトに変換し、データ処理はpandasの機能を使います。ちなみに、Apache Arrowのデータからpandasのオブジェクトへの変換は、型によってはゼロコピーで実現できます。この場合はさらに効率的にデータ交換できます。

参考:Speeding up PySpark with Apache Arrow

それでは現時点で実装されているデータ処理機能を各言語毎に紹介します。

C++

C++で実装されている処理は次の通りです。

  • キャスト(型の変換)
    • 例:16bit整数を32bit整数へ変換
    • 辞書型への変換も実装済み
  • 要素ごとの論理否定
  • 要素ごとの論理積
  • 要素ごとの論理和
  • 要素ごとの排他的論理和

実装が予定されている処理のリストはApache Arrowのプロジェクト管理ツールであるJIRAを「kernel」で検索してみてください。実装してみたい処理があったらぜひ取り組んでみてください。

JavaScript

JavaScriptで実装されている処理は次の通りです。

  • 要素ごとの論理否定
  • 要素ごとの論理積
  • 要素ごとの論理和
  • 要素ごとの排他的論理和
  • 要素ごとの==比較
  • 要素ごとの<比較
  • 要素ごとの<=比較
  • 要素ごとの>比較
  • 要素ごとの>=比較
  • 値ごとの出現数のカウント
    • 辞書型のみサポート
Go

Goで実装されている処理は次の通りです。

  • 全要素の合計値の計算

まだ1つしか実装されていないんですが、SIMDを活用するために面白い実装になっているので少し説明します。

SIMDを使うためにはCPUがSIMDをサポートしていないと使えません。最近のC/C++コンパイラーはCPUがSIMDをサポートしているかどうかで自動的にSIMDを使ったコードを生成できるくらい賢くなっています。しかし、Goのコンパイラーはまだその最適化はできません。そこで、事前にClangを使ってSIMD対応のアセンブリを出力してそれをGoに組み込んでいます。ただ、この方法だとCPUが対応していないSIMDもGoに組み込まれています。そこで、使えるSIMDを実行時に検出して適切なSIMD実装の処理を呼び出すようにしています。詳細は以下の参考URLを参照してください。Go実装を書いたInfluxDataの人が書いたブログです。

参考:InfluxData Working on Go Implementation of Apache Arrow | InfluxData

ちなみに、InfluxDataは時系列データベースInfluxDBを開発している会社です。InfluxDBはGoで実装されているのでInfluxDBで活用するためにApache Arrowの開発に参加しています。

データ処理部分の実装のまとめ

データ処理部分はあまり実装されていなくてがっかりしたかもしれません。少し将来の話もしておきます。

Apache Arrowでは各要素の処理だけでなく、複雑な条件を高速に処理する実行エンジンの実装も視野に入れています。その実装として使えそうなモジュールが近いうちにApache Arrowに入る予定です。

このモジュールにはGandivaという名前がついています。Gandivaはどのように処理を実行すればよいかを実行時に計算して、LLVMを使って実行時にコンパイルしてから実行します。コンパイルしているので事前にコンパイルしたときと同じくらい高速になりますし、LLVMはSIMD用の最適化機能もあるので、SIMD対応の処理にもなります。詳細はGandivaの開発者が書いた以下のブログを参考にしてください。

参考:Introducing the Gandiva Initiative for Apache Arrow - Dremio

「近いうちにApache Arrowに入る予定」で今はどんなステータスなのかについても触れておきます。

外部のモジュールがApache Arrowに取り込まれるには次のステップを踏む必要があります。

  1. PMC(プロジェクト管理チームみたいなやつ)が承認する
  2. IPクリアランス(知的財産侵害を心配しなくても使えることを確認する)
  3. 取り込み

Gandivaは今は「PMCが承認する」を完了したステータスです。

参考:[RESULT] [VOTE] Accept donation of Gandiva to Apache Arrow

次はIPクリアランスなのですが、そのためにはApache Arrowのリポジトリーにpull requestを作る必要があるのですが、まだそれはできていません。

0.11.0のリリースが今月中の予定なので、それには間に合わなそうですが、数ヶ月以内には入るんじゃないかと私は思っています。

Plasma

Apache Arrowには同一マシン内でオブジェクトを共有する機能もあります。それがPlasmaという名前のモジュールです。Apache Arrowにはデータフレームなデータを低いシリアライズ・デシリアライズコストで交換するための仕組みがあります。それを活用して実装しています。Plasmaはデータフレームなデータを交換するための機能ではなく、メモリー上の生データを交換するためのもっと低レベルな機能であることに注意してください。

PlasmaはもともとRayの一部として開発していた機能ですが、広く有用そうだということでApache Arrowに移動しました。Rayは大規模な機械学習・強化学習用の高速な分散処理フレームワークです。カリフォルニア大学バークレイ校のRISELabが開発しています。

Plasmaを使うとCPU上のデータもGPU上のデータもゼロコピーで共有できます。Plasmaでデータを共有することで複数プロセスで分散処理できます。

Plasmaを使うためにはサーバープロセスを起動する必要があります。各プロセスがサーバープロセスに接続して、共有したいデータを置いたり、共有されているデータを参照したりして処理を進めます。

参考:Plasma In-Memory Object Store

各言語での実装の完成度

次は各言語での実装の完成度を説明します。

Apache Arrowフォーマットはより多くの環境で使えるほどメリットが大きくなります。そのため、Apache Arrowフォーマットを使える言語を増やすべく開発しています。現在は次の言語で使えます(再掲)が、その完成度は様々です。

  • C
  • C++
  • Go
  • Java
  • JavaScript
  • Lua
  • MATLAB
  • Python
  • R
  • Ruby
  • Rust

特に完成度が高いのがJava、C++、Python、C、Ruby実装です。

最初にJava、C++実装から開発が始まっているのでこれらの言語の実装は完成度が高いのです。Python、C、Ruby実装も完成度が高いのはこれらはC++実装のバインディング(C++実装の機能を他の言語でも使えるようにしている)からです。Lua実装も(間接的に)C++実装のバインディングなのですが、APIの使い勝手という面で少し完成度が落ちます。

MATLAB、R実装もC++実装のバインディングですが、まだ開発が始まったばかりで完成度はそれほどではありません。

Go、JavaScript、Rust実装は1から実装しているのでまだJava、C++実装ほどの完成度にはなっていません。ただ、対応しているデータフレームの型に関してはJavaScript実装の完成度は高いです。

まだApache Arrowのリポジトリーには入っていませんが、Juliaの実装も進んでいます。Julia実装は1から実装しています。

それではアルファベット順に各言語の実装の概要と完成度を紹介します。

C

C実装はC++実装のバインディングです。これは私が作りはじめたモジュールです。今は主に私と@shiro615が開発しています。

GLibというC言語用の便利ライブラリーを使っているのが特徴です。GLibを使った理由は「各種言語のバインディングを自動生成できる」からです。念のため補足しますが、「C++実装のバインディングを自動生成できる」のではなく、「C++実装のバインディングであるC実装のバインディングを自動生成できる」です。

この機能を使ってRubyとLuaのバインディングが動いています。GLibにはC言語でオブジェクト指向プログラミングをするためのライブラリーGObjectも含まれています。GObjectを使ってオブジェクト指向なAPIを実現しています。そのため、自動生成されたRuby・Lua用のバインディングもオブジェクト指向なAPIになっていて使いやすいです。通常、Cのライブラリーのバインディングを自動生成するアプローチは生のCの関数をそのまま使えるようなAPIになります。そうすると使いやすくするために手動で使いやすいAPIをラップするレイヤーを実装する必要があるのですが、C実装で使っている自動生成機能ではその手間は必要ありません。

なお、C実装で使っている自動生成する仕組みはGObject Introspectionです。

C実装ではPlasma以外のすべてのC++実装の機能を使えます。近いうちにPlasmaも使えるようになる予定です。

C++

C++実装は1から開発しています。主に@wesm@xhochy@pitrou@pcmoritz@cpcloudが開発しています。

C++実装はC++11を使っています。そのため、CentOS 6に標準で入っている古いg++ではビルドできません。CentOS 6でビルドしたい時はdevtoolset-6あるいはdevtoolset-7パッケージをインストールして新しいg++を用意します。

C++実装はデータフレームのすべての型も多次元配列もサポートしています。

PlasmaのサーバープロセスはC++で実装されています。もちろん、Plasmaクライアント機能も実装されています。

C++実装には他のフォーマットとの相互変換機能がいくつか含まれています。現時点で実装されているフォーマットは次の通りです。

  • Feather
    • PythonとR間でデータ交換するためのフォーマット。
    • Apache Arrowで置き換えるため現在は非推奨。
  • Apache ORC
    • Hadoop用のカラムナーなフォーマット。
    • 現時点では読み込みのみ。簡単な書き込み機能はサポート予定。
  • Apache Parquet
    • Hadoop用のカラムナーなフォーマット。
    • Apache ArrowのC++実装ではなく、Apache ParquetのC++実装の方に実装されている。

メモリー上ではなくGPU上にデータを置く機能もあります。まだあまり活用されていない機能ですが、今後より活用されていくはずです。というのは、Apache ArrowフォーマットのデータをGPU上で処理するライブラリーが開発されているからです。

たとえばlibgdfというライブラリーです。libgdfはC言語用のデータフレームライブラリーです。CUDAを使って実装しています。値の比較処理だけでなくにグループ化機能やジョイン機能といった高度な機能も実装されています。

libgdfはGoAi (GPU Open Analytics Initiative)という団体が開発しています。この団体のミッションは「GPUを活用してデータ分析できるプラットフォームを構築する」です。

他にもMapDというGPU上で動くデータベースがApache Arrowフォーマットに対応しています。MapDではSQLを使ってデータを処理できます。MapDを開発している会社もGoAiに参加しています。

Python連携用のモジュールもC++実装に含まれています。Python連携用のモジュールではC++のApache ArrowオブジェクトとPythonのpandas・NumPyオブジェクトを相互変換する機能を提供しています。Pythonバインディングの方に入れずにC++実装の方に入れているのはいろいろなライブラリーでこの機能を共有したいからです。当初の想定の1つのpandas 2.0ではまだ使っていませんがすでに活用している例があります。それはRed Arrow PyCallという同一プロセス内でRubyとPythonを両方動かしてRubyとPython間でApache Arrowデータを共有するためのライブラリーです。Apache ArrowデータとPythonオブジェクトを変換するためにPython連携用のモジュールを使っています。

参考:ARROW-341 [Python] Making libpyarrow available to third parties

2018年9月にApache ParquetのC++実装がApache Arrowのリポジトリーに移動しました。理由は現時点では両者がすごく密に連携をして開発を進めているので同じリポジトリーにあった方が開発を進めやすいからです。将来的に、APIが安定したらまた分離するかもしれません。

参考:

Go

Go実装は1から開発しています。InfluxDataの人たちが開発しました。Apache Arrowのリポジトリーに入ってからは@sbinet(InfluxDataの人ではない)が開発しています。

対応しているデータフレームの型は次の通りです。

  • 真偽値(1bit)
  • 整数
    • 8bit非負整数(リトルエンディアン)
    • 8bit整数(リトルエンディアン)
    • 16bit非負整数(リトルエンディアン)
    • 16bit整数(リトルエンディアン)
    • 32bit非負整数(リトルエンディアン)
    • 32bit整数(リトルエンディアン)
    • 64bit非負整数(リトルエンディアン)
    • 64bit整数(リトルエンディアン)
  • 浮動小数点数
    • 32bit浮動小数点数
    • 64bit浮動小数点数
  • バイナリーデータ
    • 可変長バイナリーデータ
  • タイムスタンプ(64bit整数)
    • UNIXエポックからの経過秒数
    • UNIXエポックからの経過ミリ秒数
    • UNIXエポックからの経過マイクロ秒数
    • UNIXエポックからの経過ナノ秒数
  • リスト(0個以上の同じ型の値を持つ型)
  • 構造体(1個以上のフィールドを持つ型で、各フィールドは別の型にできる)

多次元配列はまだサポートしていません。

Plasmaクライアント機能もまだサポートしていません。

他のフォーマットとの相互変換機能もまだサポートしていません。

Java

Java実装は1から開発しています。主に@julienledem@BryanCutler@StevenMPhillips@siddharthteotia@elahrvivaz@icexellossが開発しています。

Java実装はデータフレームの型をほとんどサポートしています。サポートしていないのは次の型だけです。

  • 浮動小数点数
    • 16bit浮動小数点数

多次元配列はまだサポートしていません。

Plasmaクライアント機能をサポートしています。

JDBCで取得したデータをApache Arrowオブジェクトで返す機能が実装されているので、簡単にRDBMSのデータをApache Arrowオブジェクトとして扱えます。

JavaScript

JavaScript実装は1から開発しています。主に@trxcllnt@TheNeuralBitが開発しています。

TypeScriptで開発しています。Webブラウザー上でもNode.js上でも動きます。

MapDで処理した結果をWebブラウザー上でビジュアライズするために活用できます。MapDはApache Arrowに対応しているのでWebブラウザー上のJavaScriptと低いコストでデータ交換できるのです。

参考:Supercharging Visualization with Apache Arrow

JavaScript実装はデータフレームのすべての型に対応しています。

多次元配列はまだサポートしていません。

Plasmaクライアント機能もまだサポートしていません。node-plasmaというNode.js用モジュールを作ってサポートしたいという構想があります。

参考:Connecting JS to modern GPU and ML frameworks: Update from Nvidia GTC 2018

Julia

公式のJulia実装はまだありませんが、@ExpandingManArrow.jlの実装を進めています。公式実装にするための議論も進んでいます。

参考:collaboration with Apache Arrow org · Issue #28 · ExpandingMan/Arrow.jl

Julia実装が対応しているデータフレームの型は次の通りです。

  • 真偽値(1bit)
  • 整数
    • 8bit非負整数(リトルエンディアン)
    • 8bit整数(リトルエンディアン)
    • 16bit非負整数(リトルエンディアン)
    • 16bit整数(リトルエンディアン)
    • 32bit非負整数(リトルエンディアン)
    • 32bit整数(リトルエンディアン)
    • 64bit非負整数(リトルエンディアン)
    • 64bit整数(リトルエンディアン)
  • 浮動小数点数
    • 32bit浮動小数点数
    • 64bit浮動小数点数
  • 可変長UTF-8文字列
  • 日付
    • UNIXエポックからの経過日数(32bit)
  • タイムスタンプ(64bit整数)
    • UNIXエポックからの経過秒数
    • UNIXエポックからの経過ミリ秒数
    • UNIXエポックからの経過マイクロ秒数
    • UNIXエポックからの経過ナノ秒数
  • 時刻
    • 深夜0時からの経過秒数(32bit整数)
    • 深夜0時からの経過ミリ秒数(32bit整数)
    • 深夜0時からの経過マイクロ秒数(64bit整数)
    • 深夜0時からの経過ナノ秒数(64bit整数)
  • リスト(0個以上の同じ型の値を持つ型)
  • 辞書
    • 統計っぽい説明:名義尺度なカテゴリーデータ
    • 実装よりの説明:各値に整数でIDを割り当て、数値で値を表現する型
    • scikit-learnを知っている人向けの説明:sklearn.preprocessing.LabelEncodertransform結果を値として使う型

多次元配列はまだサポートしていません。

Plasmaクライアント機能もまだサポートしていません。

Lua

Lua実装はC実装とlgiを使って自動生成しています。特にパッケージにはしていなくて、以下のようにすれば使えるという状態です。

local lgi = require 'lgi'
local Arrow = lgi.Arrow

参考:arrow/c_glib/example/lua at master · apache/arrow

C実装で使える機能はすべて使えます。

MATLAB

MATLAB実装はC++実装のバインディングです。2018年9月10日現在ではまだ1コミットだけですが、主に開発をしている人は@kevingurneyです。MATLABを開発しているMathWorksの人です。

現在はFeatherフォーマットのデータを読み込む機能だけがあります。Apache Arrow形式のデータはまだ読めません。

Python

Python実装はC++実装のバインディングです。主に開発をしている人たちはC++実装を開発している人たちとだいたい同じです。

C++実装で使える機能はすべて使えます。単に使えるだけではなく、Pythonからより便利に使えるようにAPIが整備されています。

Cythonを使って実装されています。

pandas・NumPyのオブジェクトに簡単に相互変換できる機能も実装されていて、既存のライブラリーとシームレスに使えるようになっています。

R

R実装はC++実装のバインディングです。2018年9月10日現在ではまだ1コミットだけですが、主に開発をしている人は@romainfrancoisです。↓の最初のpull requestにR関係の人が何人かコメントしたりJIRAに新しくissueを作っている人もいるので、今後開発に参加する人は増えていきそうな気配がします。

参考:ARROW-1325: [R] Initial R package that builds against the arrow C++ library by romainfrancois · Pull Request #2489 · apache/arrow

現在はC++実装の簡単に機能を使えるだけでApache Arrowフォーマットのデータを読めたりはしません。ただ、開発環境の整備は進んでいます。すでにTravis CIでテストできるようになっています。

R実装の進め方はGoogle Docsで議論しています。ある程度まとまったらJIRAのチケットにブレイクダウンされる予定です。

参考:Apache Arrow for R - Initial Roadmap - Google Docs

Ruby

Ruby実装はC実装とgobject-introspection gemを使って自動生成しています。主に開発している人たちはC実装を開発している人たちです。

C実装で使える機能はすべて使えます。単にバインディングを自動生成しているだけではなく、Python実装のように、より便利に使うためのAPIも整備してあります。

既存のライブラリーとシームレスに使えるようにするための機能もあります。依存関係を増やしたくないのでRuby実装自体には入っていないのですが、関連ライブラリーとして次のライブラリーがあります。

また、Apache Parquetフォーマットのデータを読み込むためのRed Parquetもあります。

Rust

Rust実装は1から開発しています。主に@andygroveが開発しています。

Rust実装が対応しているデータフレームの型は次の通りです。

  • 真偽値(1bit)
  • 整数
    • 8bit非負整数(リトルエンディアン)
    • 8bit整数(リトルエンディアン)
    • 16bit非負整数(リトルエンディアン)
    • 16bit整数(リトルエンディアン)
    • 32bit非負整数(リトルエンディアン)
    • 32bit整数(リトルエンディアン)
    • 64bit非負整数(リトルエンディアン)
    • 64bit整数(リトルエンディアン)
  • 浮動小数点数
    • 16bit浮動小数点数
    • 32bit浮動小数点数
    • 64bit浮動小数点数
  • 可変長UTF-8文字列
  • リスト(0個以上の同じ型の値を持つ型)
  • 構造体(1個以上のフィールドを持つ型で、各フィールドは別の型にできる)

多次元配列はまだサポートしていません。

Plasmaクライアント機能もまだサポートしていません。

各言語での実装の完成度のまとめ

Apache Arrowの各言語での実装をそれぞれ説明しました。

サポートしている型という観点では、数値型と真偽値型はどの言語の実装でもカバーしています。時間関連の型はGo、Rust実装以外はカバーしています。複合型は共用体以外ならどの言語の実装でもカバーしています。C++、Java、JavaScript実装およびC++実装のバインディングはすべての型に対応しています。

他のフォーマットとの相互変換はC++実装ファミリーが進んでいます。Apache ParquetやApache ORC、Featherフォーマットと相互変換できます。

GPUサポートもC++実装ファミリーが進んでいます。

Plasmaを使いたい場合はC++、Java、Python実装を使うことになります。近いうちにC実装ファミリーでも使えるようになる予定です。

Apache Arrowデータを高速に処理するという観点ではどの実装もまだカバー範囲は狭いです。

Apache Arrowの今後

ここまででApache Arrowの概要、現状を説明しました。最後に今後のことを説明します。

実行エンジンGandivaの取り込み

現状ではApache Arrowデータを高速に処理するところがまだ弱いです。そのあたりをカバーするモジュールがGandivaです。このGandivaが近いうちに取り込まれ、Apache Arrowデータを高速処理できる基盤となるでしょう。

GandivaはC++で実装されているので、C++実装ではすぐにGandivaを使えるようになります。

Gandivaを開発しているDremioは自社で開発しているサービスでApache Arrowを利用しています。このサービスはJavaで実装しているので、C++で実装したGandivaをJavaから使うためのバインディングも開発しています。そのため、Java実装でもすぐにGandivaを使えるようになります。

C++実装ベースのPython実装、C実装ファミリーもすぐに使えるようになるでしょう。

CSVパーサーの追加

世の中では今でもCSVがよく使われています。そのため、CSVをパースしてApache Arrowオブジェクトを作成するパーサーの開発を進めています。

参考:Building a fast Arrow-native delimited file reader (e.g. for CSVs) - Apache Mail Archives

これはC++実装での話です。実装されたらC++実装ファミリーでもすぐに使えるようになるでしょう。

Apache Arrow対応クライアントの追加

Java実装にはJDBCで取得したデータをApache Arrowオブジェクトで返す機能があります。それと同様な機能がC++実装にも入る予定です。

まずは、Apache Hive・Apache Impalaからデータを取得してApache Arrowオブジェクトで返す機能が入りました。

他にもPostgreSQLからデータを取得してApache Arrow形式で返す機能の実装も計画しています。

参考:Developing native Arrow interfaces to database protocols - Apache Mail Archives

同様のことをしているPythonライブラリーがあります。それがturbodbcです。これはODBCで取得したデータをApache Arrowオブジェクトで返すことができます。もしかしたら、今後、この機能がApache Arrowの方に移動して、turobdbcはそれを使うという関係になるかもしれません。

まとめ

Apache Arrowの概要、2018年9月時点での現状、今後のことについて説明しました。Apache Arrowは数年後にはデータ処理界隈で重要なコンポーネントになっているだろうプロジェクトです。日本でもApache Arrowのことを知っている人が増えるといいと思うので日本語でまとめました。Apache Arrowを使う人が増えるといいなぁと思います。さらに言えば開発に参加する人も増えるといいなぁと思います。

この説明では省略しましたが、Apache Arrowの生い立ちについて面白い読み物があります。PMC(プロジェクト管理チームみたいな感じ)のチェアー(1番偉い人)が書いたものです。興味がある人はこちらも読んでみてください。

私が知っていることはまとめたつもりですが、もしかしたらカバーできていない話があるかもしれません。もし、「○○についても知りたい!」という方がいたらRed Data Toolsのチャットで声をかけてください。この記事に追加します。

Apache Arrowについて講演して欲しいという方はお問い合わせフォームからご連絡ください。

私はデータ処理ツールの開発という仕事をしたいと思っています。その中にはもちろんApache Arrowの開発も含まれています。一緒に仕事をしたい!(自社サービスをApache Arrow対応したいとか)という方はお問い合わせフォームからご連絡ください。

2018-09-05

fcitx-mozcの入力モードを外部プロセスから制御する

はじめに

何年か前に、とある案件で「fcitx-mozcの入力モードを自分のアプリケーションから制御したい」というご要望をお客様から承りました。そのときに、fcitx-dbus-statusというFcitx用アドオンを実装して、このご要望にお応えしました。

今回はこのアドオンについて紹介します。

fcitx-dbus-statusとは

fcitx-dbus-statusはFcitxで動作している入力メソッドのステータスをD-Bus経由で取得したり、変更したりできるようにするためのFcitxアドオンです。dbus-sendコマンドやdbus-monitorコマンドを使えば、シェルスクリプトで入力メソッドのステータスを制御したり監視したりすることもできます。

主にMozcを想定して開発しましたが、他の入力メソッドでも使用することができます。

インストール方法

おそらくどのディストリビューションでもfcitx-dbus-statusのパッケージは用意されていないでしょうから、自分でビルドしてインストールする必要があります。

例えばUbuntu 18.04では以下のような手順でインストールすることができます。

$ sudo apt install g++ cmake fcitx-libs-dev libdbus-1-dev fcitx-module-dbus
$ git clone git@github.com:clear-code/fcitx-dbus-status.git
$ cd fcitx-dbus-status
$ mkdir build
$ cd build
$ cmake ..
$ make
$ sudo make install

インストールが完了したら、アドオンが確実に読み込まれるようにシステムを再起動した方が良いでしょう。

使用方法

scriptsディレクトリ以下にMozc用のサンプルスクリプトがあります。

たとえばMozcの入力モードを全角カタカナに変更したい場合には以下のコマンドを実行します。

$ ./scripts/set-mozc-composition-mode.sh katakana

現在のモードを取得したい場合は以下のコマンドを実行します。

$ ./scripts/get-mozc-composition-mode.sh

モードの変更をリアルタイムで検知したい場合は以下のコマンドを実行します。

$ ./scripts/monitor-status.sh

まとめ

fcitx-dbus-statusについて紹介しました。なお、ステータス変更のリアルタイム検知を実現するにはFcitx側の変更も必要だったので、事前にFcitxのメーリングリストで実装方針を作者の方に相談した上で、パッチを提案しています。この修正は既に取り込まれており、最近のLinuxディストリビューションではこの変更が含まれたバージョンを利用できるようです。

2018-09-06

Markdownで書いたテキストをPDFに変換してドキュメントを作成する方法(テンプレート活用編)

はじめに

以前Markdownで書いたテキストをPDFに変換して納品用ドキュメントを作成する方法としてテキストから納品用のPDFを作成する方法を紹介しました。 今回は、前回のやりかたとはちょっと趣向を変えて、既存のテンプレートを活用してドキュメントを作成する方法を紹介します。

体裁を整える

体裁を整えるときの条件を以下のとおりと想定します。

  • 章や節を採番する(-Nを指定する)
  • 注釈を使う(footnotesを指定する)
  • 定義リストを使う(definition_listsを指定する)
  • フォントにはIPAexGothicを指定する(-V CJKmainfont=IPAexGothic
  • 表紙を作成する(-V titlepage=trueを指定する)
  • クロスリファレンスを使う(pandoc-crossrefが必要)
  • 目次を作成する(--table-of-contentsを指定する)
  • 目次のレベルを3にする(--toc-depth=3を指定する)
  • 既存のテンプレートを使う(--template eisvogelを指定する)
  • コードのシンタックスハイライトはtangoを使う(--highlight-style tangoを指定する)

pandocpandoc-crossrefはすでに導入済みであるものとします。

既存のテンプレートを指定するので、以前指定していた自前のクラスファイルmyltjsarticleははずします。 eisvogelというテンプレートは https://github.com/Wandmalfarbe/pandoc-latex-template から入手できます。

上記をまとめると、次のようなコマンドでPDFを作成できます。

pandoc -o sample.pdf \
	       -N \
	       -f markdown+ignore_line_breaks+footnotes+definition_lists \
	       -V CJKmainfont=IPAexGothic \
	       -V titlepage=true \
	       -V toc-own-page=true \
	       -F pandoc-crossref \
	       --table-of-contents \
	       --toc-depth=3 \
	       --pdf-engine=lualatex \
	       --template eisvogel \
	       --highlight-style tango \
	       sample.md

メタ情報を記述するには

ドキュメントにはタイトルやキーワードといったメタ情報を含めることができます。

---
title: ○○仕様書
subject: ○○に関する仕様書
date: 2018/09/12
author: 株式会社××
keywords: [△△, □□]
---

文書の冒頭に上記のようなメタ情報を記述しておくと、eisvogelテンプレートを使ったPDFの表紙として使われます。 表紙に含まれるのはtitleauthordateです。

メタ情報の詳細については、Usageを参照するとよいでしょう。

生成されたサンプルのPDF

実際にサンプルからPDFを生成してみましょう。以下のようなMarkdownのテキストをPDFに変換してみます。

---
title: ○○仕様書
subject: ○○に関する仕様書
date: 2018/09/12
author: 株式会社××
keywords: [△△, □□]
---

# 大項目1タイトル

サンプルのテキスト

## 中項目1タイトル

サンプルのテキスト

### 小項目1タイトル

サンプルのテキスト

![サンプルの図](sample.png){#fig:sample-image}

[@fig:sample-image] はサンプルの図です。

サンプルのテキスト [^footnote]

```ruby
puts "Hello, Ruby"
```

定義リスト

: 定義の説明

[^footnote]: サンプルのテキストの注釈

\appendix
\clearpage

# 付録Aタイトル

サンプルのテキスト

PDFに変換した結果は次のようになります。

生成したPDFの複数ページプレビュー画像

サンプルはシンプル過ぎるように感じるかもしれませんが、テンプレート変数を利用していろいろカスタマイズができます。 詳細については、次のページを参照してください。

pdffonts を使うと、実際に使われているフォントを確認できます。

% pdffonts sample.pdf
name                                 type              encoding         emb sub uni object ID
------------------------------------ ----------------- ---------------- --- --- --- ---------
SXTRUT+IPAexGothic                   CID TrueType      Identity-H       yes yes yes     24  0
OAJEHK+IPAexGothic                   CID TrueType      Identity-H       yes yes yes     25  0
MKLQPT+SourceSansPro-Regular         CID Type 0C       Identity-H       yes yes yes     26  0
CRVRHO+SourceSansPro-Bold            CID Type 0C       Identity-H       yes yes yes     37  0
JSIIWV+SourceCodePro-Regular         CID Type 0C       Identity-H       yes yes yes     51  0

指定したとおりにIPAexGothicが使われていることがわかります。

pdfinfoを使うとPDFファイルの情報を確認できます。

% pdfinfo sample.pdf
Title:          ○○仕様書
Subject:        ○○に関する仕様書
Keywords:       △△, □□
Author:         株式会社××
Creator:        LaTeX with hyperref package
Producer:       LuaTeX-1.7.0
CreationDate:   Wed Sep 12 18:26:42 2018 JST
ModDate:        Wed Sep 12 18:26:42 2018 JST
Tagged:         no
UserProperties: no
Suspects:       no
Form:           none
JavaScript:     no
Pages:          4
Encrypted:      no
Page size:      595.276 x 841.89 pts (A4)
Page rot:       0
File size:      236561 bytes
Optimized:      no
PDF version:    1.5

メタ情報として記述していたキーワードなども反映されていることがわかります。

まとめ

今回は、Pandocを使ってMarkdownで書いたテキストをPDFに変換してドキュメントを生成する方法(既存のテンプレートを使う)を紹介しました。 PDFの生成には少し時間がかかりますが、綺麗に出力できるので特にレイアウトに制限のないようなドキュメントをテキストエディタで書きたい人は検討してみてはいかがでしょうか。

2018-09-13

db tech showcase Tokyo 2018 - MySQL・PostgreSQLだけで作る高速あいまい全文検索システム #dbts2018

db tech showcase Toyo 2018で話すことを事前に宣伝をしておきたかったけど間に合わなかった須藤です。

関連リンク:

内容

去年は「MySQL・PostgreSQLだけで作る高速でリッチな全文検索システム」というタイトルで話しました。去年はMySQL(Mroonga)・PostgreSQL(PGroonga)で次のことを実現するための具体的なSQLを紹介しました。

  • 全文検索
  • キーワードハイライト
  • 周辺テキスト表示
  • 入力補完
  • 同義語展開
  • 関連文書の表示
  • 構造化データ(オフィス文書・HTML・PDFなど)対応

今年は「MySQL・PostgreSQLだけで作る高速あいまい全文検索システム」というタイトルで話しました。今年も話の流れは同じにしました。あることを実現する具体的なSQLを紹介するというスタイルです。今年はMySQL(Mroonga)・PostgreSQL(PGroonga)で次のことを実現するための具体的なSQLを紹介しました。

  • ヨミガナ検索
  • 同義語展開
  • 電話番号検索
  • ワイン名検索
  • fuzzy検索

今年は「あいまい検索」の実現方法にフォーカスした機能を選びました。同義語展開は去年も紹介したのですが、「あいまい検索」というテーマでは入っていたほうがよさそうだと思ったので入れました。「近傍検索」と「quorumマッチ」は入れられませんでした。

「あいまい検索」というテーマにしたのは今年はこのあたりの機能を強化したからです。「ヨミガナ検索」は今回の発表のために間に合わせました。

まとめ

去年の内容と組み合わせると全文検索まわりのかなりのことをMySQL・PostgreSQLで実現できます。ぜひ、Mroonga・PGroongaを使ってMySQL・PostgreSQLをさらに活用してください。

Mroonga・PGroongaのサポートが必要な方は問い合わせフォームからご相談ください。

タグ: Groonga
2018-09-20

«前月 最新記事 翌月»
タグ:
年・日ごとに見る
2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|