4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

最強の解析ソフトを作っていきたい

Posted at

はじめに

どんな解析もできる、そして将来どんな新しい解析技術が登場しても導入できる、そんな「最強な解析ソフトが欲しいと思い、頑張って作っています。

ドキュメントはこちら
インストールは pip install himena[recommended] でできます。

himena という名前は、なんとなくテトラヒメナって姿も名前も神秘的・近未来的でいいよねっていう浅い理由から来ています1昔好きだった子の名前とかではない

ここに至った経緯や動機、現存するソフトウェアの問題点、そしてどのような設計で「万能」を実現していけそうかを書いていこうと思います。

§1. 良い感じのデータ解析ソフトがないんですよね

プログラミング言語というのはチューリング完全なので、いわば「万能」なわけです。しかも拡張可能性が非常に高くデザインされているおかげで、新しい手法・技術を取り入れたり、既存のものと組み合わせたりが簡単にできます。

にもかかわらず、そのプログラミング言語で書かれたデータ解析ソフトはどうかというと、「簡単に拡張できますよ」と言っていて、確かにプラグインのインストールなどで簡単に拡張できるのですが、結局いまだに「このソフトすごい!いろいろできて便利!」というものが見つからないのが現状です。たとえば、僕は生物物理の研究をしているので、解析で扱うのは画像や時系列データ、遺伝子配列などになりますが、これを皆がどうやるかというと

やりたいこと 使用ソフト
画像処理・画像解析 ImageJ, napari
時系列データの可視化 Excel
遺伝子配列の設計・解析 ApE
統計解析 Prism
タンパク質構造関係 ChimeraX, Pymol

というふうにソフトウェアを使い分けて、組み合わせて解析をする際には中間の結果をコピーしたりファイルに一度吐き出してほかのソフトウェアで読み込むといった煩雑な手順を踏んでいます2

ソフトウェアごとにショートカットが違かったり、古いソフトウェアはUIが悪かったりと、かなりフラストレーションがたまります。

§2. 何故これまでのソフトウェアでは「万能」が実現できなかったのか

これまでソフトウェアの拡張がどのようになされ、何が不完全だったかを考えてみます。

§2.1. プラグインという発想

1つのソフトウェアで標準であらゆるものをカバーというのは不可能です。そのため、多くのソフトでは「プラグイン」というシステムを採用することで、必要に応じてサードパーティが機能を追加できる仕組みにしています。上で登場した中ではImageJやnapari、またプログラミング用のものではVSCodeやJupyter Notebookでプラグインは活発に開発されており、日々更新されています。

実装としては、

  • ソフトウェアの状態を取得または更新するインターフェイスを用意し、外部プログラムからそれを利用できる。
  • 外部プログラムをソフトウェアから呼び出せる。

の2点に過ぎません。シンプルですが、このおかげで標準実装ではできないことが数多くできるようになります。

例えば、現在開いている画像を取得する関数get_image、その画像を更新する関数set_image、そして関数をメニューに追加するような関数register_functionが用意されていれば、以下のように様々な画像解析プラグインが作れます。

ファイルの読み書きするプラグイン

標準でサポートしていない画像ファイルの拡張子がある場合、GUIと無関係なパッケージmy_packageがあれば、以下のプラグイン関数を介してファイルを読み込めるようになります。

from my_package import read_image, write_image  # 画像を読み書きする外部関数

def my_reader_plugin(path):
    img = read_image(path)
    set_image(img)

def my_writer_plugin(path):
    img = get_image()
    write_image(img, path)

# 登録
register_function(my_reader_plugin)
register_function(my_writer_plugin)

画像処理するプラグイン

以下の関数を定義し、これをソフトウェアから呼び出せば、どんな画像処理もできるようになります。

def my_plugin():
    img = get_image()
    img_output = nanika_sugoi_keisan(img)
    set_image(img_output)

# 登録
register_function(my_plugin)

ウィジェットプラグイン

ウィジェットを追加するインターフェイスadd_widgetが用意されていれば、GUIそのものを拡張することもできます。

def my_widget_plugin():
    my_widget = VeryGoodWidget()  # ウィジェット作成
    add_widget(my_widget)  # メインウィンドウに追加

# 登録
register_function(my_widget_plugin)

当然、VeryGoodWidgetにボタンを追加し、その中でget_image / set_imageを呼び出せば、解析のツールボックスのようなものも作れます。

§2.2. プラグインの限界

以上挙げたように、プラグインを使えばかなり自由に拡張できるので、十分なのではないか?と思うかもしれませんが、一つ重大な問題があります。それは、プラグインではプラグインの拡張ができないという点です。

napariのプラグインの一つであるnapari-skimage-regionpropsを例にとりましょう。このプラグインは、画像の領域ごとの特徴量 (面積、平均強度、長短径など) を計算してテーブルとして表示することができ、使いやすい優れたプラグインです。こんな感じ ↓ で左の画像で手で塗った領域の特徴量が計測され、右のテーブルに出ています。

image.png

テーブルがあるので、ここからプロットしたいですよね。例えば、明るさの分布や、その時間依存的な変化などがよく行われる解析です。ところが、それをやるにはCSV出力してほかで読み出すしかないです。何故ならこのプラグインにプラグインシステムがないからです。このテーブルの機能を拡張するには、プラグイン開発者に連絡するしかありません。
また逆に、右上のボタンにあるように、せっかく実装した「Copy to clipboard」と「Save as csv...」も、ほかのプラグインからは使えません。とてももったいないですよね。

§3. himenaのソフトウェアデザイン

himenaはこの欠点の克服を第一に考え、開発者がお互いのプラグインをより良いものにして行ける設計を心がけています。どのように従来のプラグインシステムの欠点を克服しているのかを述べていきます。

§3.1. 強力なアプリケーションフレームワークの提供

himena何か特定の解析に特化しません。データ解析ソフト一般を考えたときに必要な、またあると便利な機能を備えているだけで、それ以外の具体的なウィジェットや関数はすべてプラグインに委ねています。言い換えると、ソフトウェアの実装よりも一歩抽象化した階層にあるフレームワークを実装しています。

見た目は以下のような感じです。

window.png

ここに、

  • メニューバーとツールバーでコマンドを整理
  • コマンドパレットで素早くコマンドを検索・実行
  • コピー&ペースト、ドラッグ&ドロップによるウィジェット間のやり取り
  • セッションの保存

といった、実装されたウィジェットをサポートする機能を多く備えています。

§3.2. WidgetDataModelによるスタンダード化

ではhimenaではどのように機能を拡張するのかという点です。GUI操作で解析が行われる以上、ウィジェットの実装が不可欠です。

himenaでは、ウィジェットからのデータの入出力は必ずWidgetDataModelを介して行われます。なお、WidgetDataModelは以下の通り、1つのPythonオブジェクトにGUI固有の追加情報を付加したようなものです。

WidgetDataModel(
    value: Any = ...  # 何かしらのPythonオブジェクト
    type: str = ...  # "text", "table"のような、「ウィジェット型」のようなもの
    title : str = ...  # サブウィンドウのタイトル
    metadata: Any = ...  # 各ウィジェット特有の、データには関係ない情報(テーブルの選択範囲など)
    ...
)

これにより、まず、プラグインの実装は非常にスッキリします3

ファイルの読み書き

ファイルを読むプラグインは、ファイルを開いてWidgetDataModelを返すだけの関数です。返ってくるWidgetDataModelを表示するウィジェットが実装されている必要はありません

同様に、ファイルに書き込むプラグインは、WidgetDataModelのデータを指定されたパスに書き込むだけの関数です。ウィジェットからどのようにWidgetDataModelを読み出すかは次のウィジェットプラグインに委ねているので、ここで考える必要はありません。

ウィジェットプラグイン

ウィジェットは、与えられたWidgetDataModelから状態を更新するupdate_model()と、今の状態から適切なWidgetDataModelを返すto_model()が実装されているのが基本です。あとはregister_widget_classで、そのウィジェットがどのようなデータの表示を想定しているかを記述すれば、対応するWidgetDataModelが出てきたときにこのウィジェットを選んでくれます。

from himena.plugins import register_widget_class

# "text"型が来たらMyWidget使ってねと教える
register_widget_class("text", MyWidget)

これだけあればほかのプラグインとの連携は問題なくできます(他にもいくつか使い勝手を良くするプロトコルが用意されていますが割愛します)。

データ処理プラグイン

データ処理はWidgetDataModelから新しいWidgetDataModelを計算するだけの関数です。メニューのどこに配置するか、いつアクティブになるかといった情報をregister_functionで記述することで使えるようになります。

from himena import WidgetDataModel
from himena.plugins import register_function

@register_function(
    menus="tools/my-plugins",  # ここに配置してねと教える
    type="table",  # どのデータ型に使えるか
    title="すごい計算する",  # メニューでの表示名
)
def my_function(model: WidgetDataModel) -> WidgetDataModel:
    model_out = ...  # 実装部分
    return model_out

入力元、出力先のウィジェットがどうかは関係ありません。すなわち、解析手法の開発者はGUIを意識する必要はありません。

以上をまとめた図が次のようになります。

02_chart_io.png

  • ユーザーはメモリの層は見えないので、今まで通りファイルを読み込み (1, 2)、テキストからテーブルに変換し (3, 4, 5)、CSVに書き出します (6, 7)。
  • 開発者はファイルの読み込み (1)、ウィジェットの実装 (2, 3 および 5, 6)、処理関数の実装 (4)、ファイルへの書き出し (7)を分業します。この設計により、各ステップはプラグイン間で互換性があるので、あらゆるプラグインはほかのプラグインを拡張することになります。

ユーザーと開発者でやっていることが半拍ずつずれているのが何だか面白いですね。

§3.3. ワークフローの記録とセッションの保存

全てのコマンドはregister_functionによって登録されます。このおかげで、実行コマンドやパラメータなど、どのようなワークフローでそのウィジェットがが作られたかがほぼ4記録され、見返して参考にしたり、保存したり、さらには再度実行したりできます。以下のGIFは例として、「CSVの読み込み → "dataframe"型への変換 → プロット」という作業を記録したワークフローを右のウィンドウで表示5しており、ウィンドウをすべて消してもワークフローを実行するコマンドで結果を再取得できていることを実演しています。また、ドラッグ&ドロップで途中までのワークフローを取り出しています。

workflow.gif

このワークフローはJSON出力が可能なため、データの再現に非常に便利です。また、himenaには全ウィンドウをまとめてセッションとして保存する機能6があり、その際にもワークフローは便利です。たとえば、ファイルへの書き出しが困難だったり、ファイルが重くなるようなデータは、ワークフローだけ記録しておけばセッションで復元できます。

§3.4. データ型によるフィルタリング

機能の多いソフトウェアは、仕方ないことですが、その機能の多さゆえに重大な欠点があります。

  • コマンドが覚えきれない
  • メニューが多いし、ネストが深くて探すのに時間がかかる
  • ショートカットが足りない

Officeの各製品やAffinity Designerなどが最たる例ですね(悪く言っているわけではないぞ)。himenaはコマンドパレットで全コマンドを管理できるので、この問題は部分的には解決していますが、もう一つ工夫をしています。

00_model_menu.png

サブウィンドウの左上にあるこちらのボタンには、そのサブウィンドウに対応するコマンドのみが格納されます。例えば、標準の状態でテーブルデータでは「Plot」メニューが出てきますが、テキストデータでは出てきません。
himenaの各ウィジェットは1つのデータを有しており、WidgetDataModelで簡潔に記述されるということを見てきました。WidgetDataModelにはデータ型の情報が入っているので、わざわざ実装しなくとも、すべてのウィジェットでこのボタンが使えます。

§3.4. Pythonなので「つぶしがきく」

PythonでGUIを実装する利点、それはPythonが使えることです。そのソフトウェアでしか動かないマクロ言語とか覚える必要がないし、やりたいことがなんでもできて非常に体験がいい。

Ctrl+Shift+Cで下部にJupyter QtConsoleが出てきます。

00_qtconsole.png

ここの名前空間にはメインウィンドウのハンドラがuiという変数名で入っており、開いているデータすべてにアクセス可能です。例えば、

ui.tabs  # タブのリスト(みたいなオブジェクト)
ui.tabs[0]  # 最初のタブ=サブウィンドウのリスト(みたいなオブジェクト)
ui.tabs[0][i]  # 最初のタブのi番目のサブウィンドウ
ui.tabs[0][i].to_model()  # そのサブウィンドウの`WidgetDataModel`
ui.tabs[0][i].to_model().value  # その内部データ(`str`, `ndarray`など身近なもの)

かゆい所にとりあえず手が届くので安心。

§4. プラグインの例

§4.1. 今あるプラグイン

僕自身が日ごろ必要になる、少しニッチな機能はほかのパッケージでプラグインとして公開しています。

himena-image

多くの拡張子の画像の読み書きのサポート、scikit-imageのサンプルデータのロード、フィルタ・FFTなどベーシックな画像解析関数と、期待の3次元ビューアーndvを使ったヌルヌル動くウィジェットを提供しています。作った中では一番完成度が高い。目指せ打倒ImageJ。

window.png

himena-bio

生物系の解析をカバーしていきたい。遺伝子配列の基本的操作、アラインメント、PCRのシミュレーションなどができます。目指せ打倒ApE。

window.png

himena-seaborn

テーブルデータからseabornの関数を呼び出します。seabornは引数が多くてコードを書くときは不便だったんですが、GUIにしておくと楽ですね。

window.png

himena-stats

検定、確率分布からのサンプリングなど、統計関係の機能が入っています。

window.png

§4.2. 未完成・今後作りたいプラグインやアイデアなど

  • himena-terminal: VSCodeみたいにターミナル動かしたい。やり方全くわからん。
  • himena-lmfit: lmfitを使ったフィッティングや最適化...やりたい!テーブルをプロットしてそのままフィッティングとかやりたい!
  • himena-sklearn: scikit-learnでのテーブルデータの解析。PCAとかすぐできたら嬉しい。
  • himena-napari: napariのプラグイン全部吸収してやる...(napariはメインウィンドウで動かす前提の設計が多いので、一筋縄では行かない)
  • himena-micro-manager: 光学顕微鏡の操作をするMicroManagerをPythonから動かすpymmcore-plusを導入してイメージング技術を発展させたい。napariのプラグインではもうあるが、開発者本人も「別にnapariじゃなくてよくね」となっているようだ。
  • geohimena: geopandasで衛星データとかの解析(素人並感)
  • astrohimena: astropyで宇宙とかの解析(素人並感)

最後に

GUI開発は長くやってきましたが、なかなか良いアイデアだと思っていて、これまで作ってきたウィジェットや関数もどんどんプラグインとしてhimenaに移行していこうと考えています。
データサイエンスに取り組んでいる皆さんにも、ぜひユーザーとしてもプラグイン開発者としても使っていただければ幸いです!

  1. テトラヒメナの綴りならhymenaでは?となるが、キーボードでの打ちやすさと英語での処女膜hymenを連想しないようhymenaではなくhimenaにした。

  2. ここで挙げたものは、どれも他とかなり独立しているので、別々のソフトでもまあよいのだが、実は電子顕微鏡関係が現在非常にカオスで、ソフトウェアの統制が全く取れていない上にほとんど全部使いづらいのである。

  3. フロントエンドとバックエンドを分けているだけとも言えるが、従来の意味でのフロントエンド・バックエンドの分業かというとそうとも言えない。

  4. 「ほぼ」と言っているのは、例えばデータが手動で編集された場合にその追跡方法が定義されていない場合、クリップボードからの画像の貼り付けのように大きなデータをそのまま記録せざるを得ない場合、関数内部で乱数の取得やウィジェットの状態の取得を「暗に」行っている場合といった、記録が困難な場合があるため。しかし、そのような場合でも、解析の流れの記録は見返す際に非常に有用である。

  5. 鋭い読者は気づいたかもしれないが、このワークフローを表示しているウィンドウ自体も、ただの"workflow"型のWidgetDataModelから来ていて、ワークフローの実行もただのコマンドである(ともに標準実装のプラグイン)。

  6. ちょっとまだexperimental。

4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?