今回はデータ出力。
データ出力は、前回の反復条件と同様に、よく使いそうなものは Groops 側でサポートします。 ただし、データを独自にビジュアライズしたい場合もあると思うので、自作のデータ出力もある程度簡単に作成が可能なようにできればいいなと思ってます。 まぁこれはおいおい考えることにしましょう。 データ出力に関しては、出力方法も大切ですが、それと同じくらい出力のタイミング、つまりどの時点で観測量を計算して出力するのかというのも気にかける必要があります。 Groops ではいくつかの出力タイミングを決め打ちでサポートしていて*1、シミュレーション構築時に DataOutputter のプロパティとして簡単に設定できるようにしています。 この設定方法(というかプロパティの設定方法)は次回にやる予定ですが、今回は決め打ちでサポートしている出力タイミングなどを見ていきます。
DataOutputter インターフェース
まずは DataOutputter インターフェースを見てみましょう。 独自のデータ出力を作成する場合でなければこのインターフェースの詳細を知っておく必要はありませんが、そんなに込み入った宣言でもないと思います:
public interface DataOutputter extends SimulationComponent{ OutputTiming getOutputTiming(); void setOutputTiming(OutputTiming timing); void output(DataStore dataStore) throws SimulationException; }
スーパーインターフェースの SimulationComponent は IterationCondition インターフェースのときも出てきましたが、いくつかのフック・メソッドが定義されています:
- onIterationStart() / onIterationEnd()
- onEvolutionStart() / onEvolutionEnd()
実際には他にもいくつかメソッドが宣言されてますが、詳細はそのうちに。 outputTiming プロパティへのアクセッサメソッド(getOutputTiming() / setOutputTiming()) は出力のタイミングを設定するためのメソッドです。 出力タイミングについては後ほど。 最後はoutput() メソッド。 このメソッドにデータ出力の仕方を実装します。 引数の DataStore オブジェクトは観測量の値をストックしていているオブジェクトです。 DataStore クラスは今までも何度か出て来てますね。
出力のタイミング
では、出力タイミングの基本概念(ちょっと大仰な言い方かな?)を見ていきましょう。 前回見たように、プッシュ・シミュレートでは、イメージとして以下のような反復を行っているのでした:
IterationCondition ic = ... DataStore store = ... onIterationStart() while(ic.continueIteration(store)){ onEvolutionStart() // 状態更新 onEvolutionEnd() } onIterationEnd()
onXxxxStart() / onXxxxEnd() メソッドは処理を柔軟にするためのフック・メソッドでした。 前回はこれらのメソッドは IterationCondition オブジェクトに対して呼び出していましたが、それを拡張して、登録されている全ての DataOutputter に対しても呼ばれるようにします。 で、これらのフック・メソッドの呼び出し位置でデータ出力も行われるようにします*2。 ただし、これら4つの位置を好き勝手に指定してデータ出力すると1つの状態が2度出力されることがあるので*3、これを防ぐために、以下のようによく使いそうな出力タイミングを Groops 側で定義して、それをユーザーが指定するようにします:
出力タイミング | onIterationStart | onEvolutionStart | onEvolutionEnd | onIterationEnd |
---|---|---|---|---|
ALL | ○ | ○ | ||
FINAL | ○ | |||
INITIAL_FINAL | ○ | ○ | ||
AFTER_EVOLUTION | ○ | |||
OBSERVE_ALL | ○ | |||
OBSERVE_FINAL | △ | |||
OBSERVE_INITIAL_FINAL | ○ | △ | ||
NONE |
これらは列挙型 OutputTiming に定義されています。 一応、それぞれの出力タイミングを説明しておくと
出力タイミング | 説明 |
---|---|
ALL | 全ての状態を出力する |
FINAL | 終状態を出力する |
INITIAL_FINAL | 始状態、終状態を出力する |
AFTER_EVOLUTION | 状態を更新した後のみ出力する |
OBSERVE_ALL | 物理量の値によって反復条件を決めている場合に、全ての状態を出力する |
OBSERVE_FINAL | 物理量の値によって反復条件を決めている場合に、終状態を出力する |
OBSERVE_FINAL | 物理量の値によって反復条件を決めている場合に、始状態、終状態を出力する |
NONE | 出力しない |
OBSERVE_XXXX を別途定義しているのは、状態を更新した後に反復条件が満たされなくなった場合は、その前の状態が終状態であるためです。 上記の表中の△は出力するタイミングとその時の物理系の状態がずれていることを示しています。 これ以上は実装の詳細なので省略。
サポート予定のデータ出力
では、Groops でサポート予定のデータ出力をいくつか見ていきましょう。 普通に考えて、データをチャート(グラフ)にして出力するのが一番必要そうな気がしますが、結構 GUI が絡んできてゴチャゴチャするので、そのうち別途 Griffon ででも実装します。 ってことで、サポート予定のデータ出力は以下のようなものを想定しています:
タイプ | 説明 |
---|---|
console | 標準出力への出力 |
csv | CSV (comma-separated value) 形式でファイルへ出力 |
ssv | SSV (space-separated value) 形式でファイルへ出力 |
tsv | TSV (tab-separated value) 形式でファイルへ出力 |
map | java.util.Map オブジェクトへデータをセット |
database | データベースへ出力 |
以下で、それぞれについて設定できるプロパティを整理しておきます。 ただし、全ての DataOutputter に対して共通のプロパティ outputTiming がありますが、これについては省略しています。
標準出力への出力 : console
まずは標準出力へ出力する console。 設定できるプロパティはこんなの:
プロパティ | 型 | デフォルト値 | プロパティの説明 |
---|---|---|---|
outputHeader | boolean | true | ヘッダを出力するかどうか |
delimiter | String | ' '(半角スペース) | データの区切り文字列 |
lineSeparator | String | System.getProperty("line.separator") | 行の区切り文字列 |
dataEntries | List |
登録されている物理量全て | データとして出力する物理量のリスト |
additionalWriters | List<Writer> | 空リスト | 標準出力以外に付加的に出力したい Writer |
ファイルへの出力 : csv, ssv, tsv
次は csv, ssv, tsv タイプの出力。 console と同じものもいくつか含まれています。
プロパティ | 型 | デフォルト値 | プロパティの説明 |
---|---|---|---|
fileName | String | - | 出力するファイルの名前 |
encoding | String | System.getProperty("file.encoding") | ファイルのエンコーディング |
overwrite | boolean | false | ファイルが既に存在する場合に上書きするかどうか |
outputHeader | boolean | true | ヘッダを出力するかどうか |
lineSeparator | String | System.getProperty("line.separator") | 行の区切り文字列 |
dataEntries | List |
登録されている物理量全て | データとして出力する物理量のリスト |
Map オブジェクトへの出力 : map
観測データをオブジェクトとして取得し、別途プログラム中で処理したい場合などに map 出力を使用します。 Map オブジェクト自体はユーザー側がインスタンス化してセットする必要があります。
プロパティ | 型 | デフォルト値 | プロパティの説明 |
---|---|---|---|
target | Map<String, Object> | - | データをセットする Map オブジェクト |
dataEntries | List |
登録されている物理量全て | データとして出力する物理量のリスト |
containsEntries | boolean | false | dataEntries が指定されていないとき、出力するデータにエイリアス(別名)も含めるか |
データベースへの出力 : database
最後はデータベースへの出力。 これはデータベースプログラミングの練習に実装したようなもので、本格的な使用は想定してません ^ ^;) パフォーマンスもさることながら、パスワードの扱いや SQL インジェクションのようなセキュリティ関連の事項には注意を払ってません。 「とりあえず動く」って程度のモノです。 一応、プロパティの一覧:
プロパティ | 型 | デフォルト値 | プロパティの説明 |
---|---|---|---|
url | String | - | データベースの URL |
user | String | - | データベースアクセスのためのユーザー名 |
password | String | - | データベースアクセスのためのパスワード |
driver | String (Class) | - | ドライバクラス名 |
table | String | - | テーブル名 |
createTable | String | - | データを作成する SQL 文 |
dataEntries | Map |
登録されている物理量全て | データとして出力する物理量の名前と型のマップ |
overwrite | boolean | false | 既にテーブルが存在する場合に上書き(というより削除して新規に作成)するかどうか |
おそらくデータベース出力に関してはもう触れないので、先走っていくつかの使い方を載せておきます。 以下のようにデータベースにアクセスするとしましょう:
プロパティ | 値 | 説明 |
---|---|---|
url | jdbc:hsqldb:mem:groops | データベースの URL |
user | waman | ユーザー名 |
password | (空文字列) | パスワード |
driver | org.hsqldb.jdbcDriver | ドライバークラス |
table | test | テーブル |
これを踏まえて、データベースへ出力へ出力する DataOutputter の生成方法を見てみましょう。 以下のコードで SimulationBuilder はシミュレーションを構築するビルダーです(次回にでも真面目にやります)。 SimulationBuilder に対して database ノードによってデータベース出力を生成できます:
@Grab('org.hsqldb:hsqldb:2.0.0') // import 文省略 new SimulationBuilder().simulation{ // その1 : createTable ノードによってテーブルを作成するバージョン database(url:'jdbc:hsqldb:mem:groops', user:'waman', password:'', driver:'org.hsqldb.jdbcDriver'){ createTable ''' DROP TABLE test IF EXISTS; CREATE TABLE test (step INTEGER, time DOUBLE, T DOUBLE); ''' } // その2 : dataEntries プロパティによってテーブルを作成するバージョン (1) database(url:'jdbc:hsqldb:mem:groops', user:'waman', password:'', driver:'org.hsqldb.jdbcDriver', table:'test', overwrite:true, dataEntries:[step:'INTEGER', time:'DOUBLE', T:'DOUBLE']) // その3 : dataEntries プロパティによってテーブルを作成するバージョン (2) database(url:'jdbc:hsqldb:mem:groops', user:'waman', password:'', driver:'org.hsqldb.jdbcDriver', table:'test', overwrite:true, dataEntries:[step:Types.INTEGER, time:Types.DOUBLE]) // その4 : 登録されている観測量全てを観測するバージョン database(url:'jdbc:hsqldb:mem:groops', user:'waman', password:'', driver:'org.hsqldb.jdbcDriver', table:'test', overwrite:true) }
JDBC ドライバは別途インストール*4が必要です。 これにて反復シミュレーションを構成するコンポーネントは大雑把に見てきたので、次回にこれらのコンポーネントを組み立てて反復シミュレーションを構築する方法を見ていきます。
- 作者: ハーベイゴールド,ジャントボチニク,Harvey Gould,Jan Tobochnik,鈴木増雄,石川正勝,溜渕継博,宮島佐介
- 出版社/メーカー: ピアソンエデュケーション
- 発売日: 2000/12
- メディア: 単行本
- クリック: 5回
- この商品を含むブログ (45件) を見る