(農業)情報工学・課題 (2016/11/10)

水稲の生育予測プログラムの作成(3)

パラメータの指定
今回の講義では(先週までの課題が完成しているものとして)イネの生育モデリングについての学習は行わない。
その代わり、JAVAプログラミングについて、更に学習を深める。
具体的には、パラメータを、コマンドライン引数として、与える方法についてである。
コマンドライン引数
前回の課題では、各種のパラメータを、パラメータファイル[Parameters2.txt]として与えた。この方法では、プログラムが完成していても、異なった年度の気温データを使ったり、違う品種名を指定したりする場合には、パラメータファイルを編集して再度指定しなければならず、かなり煩雑であった。しかも、ウェブアプリケーションの場合には、いちいち手元のパラメータファイルを編集する手法は使い勝手も悪く、複数の条件を指定する際などにかなり不便である。このため、プログラムを実行する際に、動的にパラメータを与える方法として、コマンドライン引数を導入する。これは、実行時のオプションを与えるための方式で、
などのような実行方法を可能にするものである。(ちなみに、上は、地域(-a)を東京(Tokyo)、 気温データファイル名(-t)を2003年の東京(2003tokyo.txt)、品種(-v)をコシヒカリ(Koshihikari) として、指定した例である。なお赤や青の文字色は説明のためのもので、実際には色がついているわけでは無い。)

プログラミング方法
今回のプログラムでは、コマンドラインのオプションに付けた最初の2文字を判定し、その内容に応じて、どのパラメータの指定であるかを判別している。つまり、-aTokyo という文字列の場合、最初の2文字(-a)を、地域の指定と判定し、それに続く文字列(Tokyo)を、地域名として利用しているのである。今回の課題プログラムでは、次のような手順でこの部分を実装(implementation:プログラミング)している。
  1. キーボードから指示したコマンドライン(java Model03 -aTokyo -t2003tokyo.txt -vKoshihikari)の引数部分(java Model03 のスペースから後の部分)が、スペース(空白文字)で分割され、3つの文字列として配列(args[ ])に格納される。また、引数の総数は、変数args.lengthに収納される。この例の場合、args.length=3 である。
  2. その結果、args配列の各要素args[0], args[1], args[2]には、次の文字列が順に収納される。
    args[0] = -aTokyo
    args[1] = -t2003tokyo.txt
    args[2] = -vKoshihikari
  3. 以下の操作を、引数の数args.lengthだけ繰り替えす。まず、最初の引数args[0]についての実例を示す。
    なお、引数が無い時(即ちargs.length==0の時)は、このプログラムの使い方(Usage())を表示させて、実際の計算はしない。
  4. 各配列の要素を、パラメータの種類の指示部分文字列s1( 即ち、 -aの部分:地域名の指定)と、実際の内容部分文字列s2(ここでは、 Tokyoと指定している。)に分割する。実際には.substring(開始位置,文字数)というメソッドをストリング列に適用して、s1,s2 を切り出している。
    s1 = args[0].substring(0,2); (これによって、 s1=-a となる)
    s2 = args[0].substring(2); (これによって、s2=Tokyo となる)
  5. s1の内容が、どのパラメータに相当するかを、文字列比較関数(s1.equals())を用いて判定することととなる。
    ここでJavaの場合、比較演算子==では文字列の比較ができない事を知っておく必要がある。
    具体的には、s1が-aであるかはs1.equals("-a") という関数の真偽で判定する。
  6. s1が-aであった場合には、変数Areaに先ほど分割した文字列s2を代入する。
    なお、ここでは変数(Area)が文字列であるため、そのままs2を代入できるが、数値データを指定する場合には、文字列から数値型への変換が必要になる。
    例1:田植え日(変数taue)は、整数型であるため、taue = Integer.parseInt(s2);のように行う。
    例2:出穂積算気温(変数at_ear)は、倍精度実数型であるため、at_ear = Double.parseDouble(s2);のように行う。

プログラムの全体構成
今回の課題プログラム(サンプルプログラム)は、次のような構成になっている。
  1. 使い方の表示クラス (usageクラス)
  2. 日付計算プログラム (calクラス)--日付連番を月日の形式に変換する。
    応用課題の一つの解答例である。
  3. 初期設定・変数の定義
  4. 地域・品種データ・定植日の読み込み(入力パラメータの解釈)
    前の章で解説した部分が、ここに相当する。
  5. 気温データの読み込み(ファイル入力)
  6. 積算気温の算出、各種ファクターの計算
  7. 結果の出力(標準出力)
  8. 終了

今回のサンプルログラムは、実は、このままで正常に実行でき、結果の算出もできる。しかし、このまま、ただ単にコンパイルして実行するだけではプログラミングの技量の向上は望めないので、諸君には次の課題をこなしていただきたい。
今回の課題
自分の独自のパラメータとは、前回の課題で行ったような稲の生育障害などのモデルをプログラムに組み込むことである。
例えば、次のような例が考えられる。 プログラム中で、これらをモデルすると言うことは、具体的には次の用にコーディング(プログラムを書くこと)することである。
幼苗の生育障害(-b15)を例にとって説明する。
  1. double youbyou=0;
    まず、幼苗の生育障害のトリガー気温データを格納する変数(例:youbyou)を宣言しておく。初期値を0にしておく。
    変数の宣言部分(ステップ3の部分)に書くことが望ましい。必ず、パラメータデータの読み込み前に宣言することが必要である。
  2. else if( s1.equals("-b") ){ youbyou = Double.parseDouble(s2); }
    コマンドラインの指定内容("-b15")を、変数youbyouとして読み取る部分である。ここで、s1は、"-b"に相当し、s2は、"15"に相当する。
    この部分の実行の結果、youbyouに15が代入される。
    先ほども説明したが、コマンドライン引数("-b15")をs1("-b")とs2("15")に切り分けるのは
    s1 = args[i].substring(0,2); // identify the parameter
    s2 = args[i].substring(2); // get the parameter body
    の部分で行っている。
その後、田植え後20日以内(この日数もモデル化できる。)の積算温度の計算を行う際に、youbyouが0 であるかどうかの判断を行う部分を追加すればよい。youbyouが0で無い(即ち-bオプションで数値が指定されている)場合で、かつ、気温がトリガー温度より低ければ、幼苗の生育障害計算を行うという事である。
ここでは、幼苗の生育障害の発生をトリガー温度を指定する方式で行ったが、課題としては、いもち病の引き金となる温度や、高温障害の場合の引き金となる温度を指定するという方法が直ぐに活用できるであろう。諸君は、更に、その際の効果の係数も数値で指定するとか、様々なバリエーションが考えられる。自分で工夫して欲しい。
なお、プログラムを読めば分かるように、パラメータの判別部分は、「最初の2文字」が - と、何か一文字( a など)という形式を用いている。が、これは使用する人間のための配慮(Unixのコマンドライン形式に合わせた)であるので、諸君は別の方式を用いても構わない。
現在のパラメータの内容は次のようになっている
コマンドライン引数
パラメータ指定方法(内部)変数名変数の型初期値備 考
-aareaStringTOKYO栽培地域名(表示のみ:計算には使用していない)
-careacodeinteger13地域番号名(表示のみ:計算には使用していない)
-ttempfileString1995tokyo.txt気象データファイル名
-vvarietyStringKOSHIHIKARI品種名(表示のみ:計算には使用していない)
-utaueinteger109田植え日(日付連番)
-eat_eardouble1650出穂に必要な積算温度
-hat_harvestdouble950登熟に必要な出穂後の積算温度
-yyielddouble580平均的な収量(kg/10a)
-ppricedouble15000平均的な価格(円:60kgあたり)

モデル構築用に追加するパラメータ(参考例)
パラメータ指定方法(内部)変数名変数の型初期値備 考
-byoubyoudouble0幼苗期の低温障害をモデル化し、その時のトリガー温度を指定する。
-nnissuint0幼苗期の低温障害の感受期間(影響の出る日数)をモデル化し、その時の期間日数を指定する。
-iImochidouble0穂イモチ病をモデル化し、その時のトリガー温度を指定する。
-jJint0穂イモチ病で、別のモデルを作り、その時のトリガー温度を指定する。(例:j=1のモデルでは、平均気温がトリガー温度を下回ったら発動させるが、j=2のモデルでは、最低気温が下回ったときだけ発動する。j=3では、最高気温とする。。など)(この場合でも、いもち病発病のトリガー温度は -ixx で指定する。)

<課題ファイルへのリンク>

課題の提出にあたって
プログラムはJAVAで作成する。
JAVAの使い方は、はいぱーワークブックなどで復習して欲しい。最も単純な使い方は、ターミナルから、
  1. javac Model03.java
  2. java Model03 -aTokyo -t2003tokyo.txt
のように、1.コンパイルして、2.引数をつけて実行、することである。なお、上記の2.の段階で、他のファイルにリダイレクトすれば、結果がファイル出力として入手できる。
複数の実行結果を一つのファイルとして保存するには、次のように行う。 結果ファイル「kekka3.txt」の中に、2番目の実行結果を追加する場合
のように、2回目には、>を二つ書く。 この例では、1997年の東京の気象データの実行結果の後に、2003年の東京の気象データを用いた結果を追加している。
今回の課題の提出は、
  1. 作成したプログラム(ソースコード)。コードには、必要に応じて注釈(コメント)を付けること。
  2. 実行結果(自分の追加したパラメータが動作することを確認したもの) ← 必要に応じて、複数の実行結果を添付して、解説すること。
の2点を、ICT-LMSを用いて、提出することである。
健闘を祈る。
応用:シェルスクリプトの活用
  1. シェルスクリプトについて
    シェルの意味は「殻(から)」であるが、ソフトウェアの世界ではOSの中核(種:カーネル)を包んでいて、人間の指示を受け取るための外側部分を差す。いわゆるコマンド(cd, ls, javac, cal など)は、シェルによって解釈され、コンピュータがその指示を受け取って実行する。
    シェルスクリプトとは、コマンドラインで実行する複数のコマンドを一括して行なう仕組みである。こうした一連のコマンドを記述したものを、「スクリプト」と呼ぶ。Windowsの環境では、バッチファイル(.bat)で、同種の機能を実現することが出来る。
  2. シェルスクリプトの書き方
    最も簡単なシェルスクリプト(以降シェル)は、実行したいコマンドをそのまま並べて記述することである。例えば、カレンダーを11月から1月までの3ヶ月分を1つづつ出力したいとする。それを、シェル「cal_11_1.sh」とすると、次のようになる。
    シェルファイル名 ファイルの内容
    cal_11_1.sh
    1. #!/bin/sh
    2. echo "----- Start of Shell ----"
    3. cal 11 2016    
    4. cal 12 2016
    5. cal 1 2017
    6. echo "*** End of Shell ****"
    「#!/bin/sh」(または、#!/usr/bin/sh)はシェルプログラムの本体の場所を指定しているが、お約束で必ず1行目に記述する。「#!」以後に書かれたプログラムでスクリプトを実行するという意味である。続いて、実行したいコマンドを、実行順に記述する。ここで、echoは、コマンド画面にメッセージを表示させるコマンドである。
  3. シェルの作成・編集
    シェルの作成・編集には、テキストエディターを用いる。Macの場合には、テキストエディット、又は、mi、Emacsなどを用いる事となる。
  4. ファイルを実行可能にする
    上記で作成した「cal_11_1.sh」を実行するには、まず、ファイルに実行属性を付与する。
    chmod u+x cal_11_1.sh
    これで、シェルファイル「cal_11_1.sh」が、実行可能になった。通常のファイルは、実行属性が無い。現在のファイルの一覧を長い形式(ls -l)で表示した時に、rwxr--r--の様に表示されるのが、実行属性のあるファイルである。(r, w, xは、それぞれ、読み、書き、実行属性である。また、所有者・グループ・すべての人間に対して、これらのファイル属性が定義されているので、rwxの組は3回繰り返されている。 )
    ファイルを実行可能にするには、次のコマンドでも良い。
    chmod 755 cal_11_1.sh
    この場合には、新たに755という属性を与える。属性の数字はrが4, wが2, xが1 となっていて、rw-r--r--なら644となっている。これを755にすると、rwxr-xr-xと指定したことになる。つまり、所有者は7(rwx:4+2+1)、グループとすべての人間は5(r−x:4+1)となっているのである。
  5. シェルプログラムの実行
    実行は、コマンドラインで、ファイル名をそのまま入力することである。但し、ファイル名の前に ./ を付与する。
    ./cal11_1.sh
  6. Model3での実例
    様々な気象モデルで実施する
    kion.sh
    積算気温を少し変化させる。
    sekisan.sh
    田植え日を少しずつずらす。
    taue.sh
    #!/bin/sh
    echo "東京の気温1993年〜2003年"
    java Model03 -t1993tokyo.txt
    java Model03 -t1994tokyo.txt
    java Model03 -t1995tokyo.txt
    java Model03 -t1997tokyo.txt
    java Model03 -t2003tokyo.txt
    #!/bin/sh
    echo "積算気温変更(1600-1700)"
    for ear in 1600 1625 1650 1675 1700
    do
     echo "-------- " $ear " -----------"
     java Model03 -e$ear
    done
    echo "****** Done! **********"
    #!/bin/sh
    echo "田植え日の変更計算(105-135)"
    taue=105
    while [ $taue -ne 135 ]
    do
     echo "-------- " $taue " -----------"
     java Model03 -u$taue
     taue=`expr $taue + 1`
    done
    echo "****** Done! **********"
    ここで、積算気温の変更と、田植え日の変更では、どちらもループ計算を利用している。まず、積算気温の時には、for [変数] in [値1 値2 ... ] do [実行] doneの構文を用いている。ここでは、[変数]として ear を用いた。なおこの変数は、値を代入するときはそのまま ear として書くが、値を参照するときには、$earとして書く。
    田植え日の計算の時には、while [条件] do [実行] done のループを用いている。条件の判定は、-ne で(同じ値で無いとき)を意味している。また、taueの数値を一つずつ増加させるときには、$taueの値を評価した(expr)結果に1を加えて、新しいtaue値として更新するという手順を取っている。
  7. 感度解析の実例
    田植え日をずらしたときに、モデルの収量がどのように変化するかのデータを入手し、グラフ化して追跡してみる。
    このように、自分の構築したモデルで、入力値の変化が結果にどのような応答を示すかを調べる作業を「感度解析」=sensitivity studyと言う。後は、そのモデルが現実の栽培データと一致するかどうかを検証する手順が必要になる。
    手順としては、次の様に行った。
    1. 田植えデータの変化とその結果を「kekka.txt」としてファイル化する。
    2. 「kekka.txt」から、収量の部分だけを、grepで抜き出して「kando.txt」としてファイル化する。
      grepは、ファイルなどから文字列を検索するためのプログラムである。-e オプションで検索文字列を指定する。
    3. 「kando.txt」をエクセルで読み込み、グラフ化する。
    ./taue.sh > kekka.txt
    grep -e "shuryo" kekka.txt > kando.txt
    エクセルファイル
参考1:はいぱーワークブック:シェルスクリプト
参考2:シェルスクリプト入門