9 蛇使いな彼女 2022①

*アーカイブ*
2023年度 ①頁目 
②頁目 ③頁目

       

*①頁目 目次*
76. グラフ編 #3文字化け防止 / 77.アルゴリズム編 #1探索 /  78.アルゴリズム編 #1探索② / 
79.アルゴリズム編 #1探索③ / 80.netCDFを扱う #1 /  81.netCDFを扱う #2 / 82.番外編② / 
83.テンソルフロー編 #1 / 84.テンソルフロー編 #2 / 85.テンソルフロー編 #3

         

       


【第76回】 グラフ編#3 記号の文字化防止表示 ~グラフ文字化けでハマった時のお話~

ヘビカノ漫画新年明けましておめでとうございます。
皆様、今年の干支はうさぎですね!
うさぎはぴょんぴょん跳ねることから“飛躍”、“向上” の象徴と言われている
そうですよ 🐇
そして今年の環境システムのカレンダー下部に描かれたイラストは
うさぎではなく(笑)、船でも商材でもなく、カエルと猫です!
ヘビカノ推しありがとうございます!
モトハシも向上心を持って努力を続けていこうと思います。
本年度もどうぞよろしくお願い致します。



さて、今日の話題はグラフで特殊記号を表示する方法です。
私もPython、matplotlibを使いだしてから今日まで知らなかったのですが、
特殊文字にもきちんとフォントがあって、それは「stix」と呼ばれるそうです。
数学的シンボルとアルファベット全般に対応したフォントで、
「Tex」と呼ばれる文章整形システムで処理して特殊文字を表示します。
エクセルやワードの数式に当たる処理ですね。

Matplotlibには独自のTexエンジンが付いているため、stixフォントの
別途インストールは不要です。

Matplotlibガイドラインの文章項目にも

Mathtext can use DejaVu Sans (default), DejaVu Serif, the
Computer Modern fonts (from (La)TeX), STIX fonts (which are
designed to blend well with Times), or a Unicode font that you
provide.


日本語翻訳→「Mathtext では、DejaVu Sans (デフォルト)、
DejaVu Serif、Computer Modern フォント ((La)TeX から)、
STIXフォント (Times とうまく調和するように設計されています)。
またはユーザーが提供する Unicode フォントを使用できます。」

と記載があるので、どうやらmatplotlib, seabornのデフォルトフォントであるサンセリフ体のDejaVu Sansや、セリフ体ならDejaVu Serif、Timesなどでうまくシンボルが表示されるように設計されているのですね!
特殊文字を表示させたいとき文字化けしてしまう箇所については、通常matplotlibでアクティブになっている
フォントの一部をstix(TeX)に置き換えて表示させると改善されるという事です。

そして逆に、rcParams でフォントファミリーをDejaVu Sansに設定していれば、mathtextのデフォルトが
DejaVu Sansになっている為、わざわざstixフォントを使用することなく、特殊文字が反映される(文字化けが
防げる)という仕組みになっています!
(システムとして完成されてるからフォント弄るな!って事ですね(笑) 開発した人すごい)



フォントにこだわり無いからなるべく文字化けを防ぎたい!という人には、

rcParams[‘font.family’]= ‘DejaVu Sans’
rcParams[‘mathtext.fontset’]= ‘dejavusans’         

とコードに書き加えて、DejaVu Sans(デフォルト)を適応すると良いです↑。(cmとComputer Modernの組み合わせでもOK)
このフォント設定を行うと、これ以下のややこしい記事を見なくても
殆どの文字化けを防げます。(笑)


stixフォントを使用する場合、rcParam[mathtext.fontset]で使用できるフォントセットキーワードは
['dejavusans', 'dejavuserif', 'cm', 'stix', 'stixsans', 'custom']の6つです。
(※cmというフォントはComputer Modernの略です。)
このリストを見る限り、上記で説明したようなmathtextとフォントファミリーの設定のみで特殊文字の表示が
概ね機能するのは、モトハシが試した限りDejavu(今回はsansのみ検証。serifの方は試していませんが
おそらく機能しそう?)と cmです。

それ以外のキーワードとフォントの関係については結構曖昧でそれぞれ、stixはTimes(セリフのロマン体)
フォントに馴染むような設計をされていますし、stixsansはサンセリフに設計されています。
そのほか、customはmathtextを通常のテキストと同じように見せる為に用意されたキーワードで、
この効果を適応させるには、mathtext のフォントパラメータとして'mathtext.rm'の他、
'mathtext.it'、'mathtext.bf'なども必要に応じて表示させたいフォントに変更する必要があるそうです。
mathtext.fontsetでstix, stixsans, customのいずれかを選択した場合は、$マークで囲った領域に特殊記号や
シンボルの名称を入力してフォントを出力します。(シンボル名称についてはガイドラインに記載があります)


rcParamsで、 mathtext.fontset :stix、font.family:Times New Romanを設定した条件で以下のように
特殊記号を表示すると・・・


       ↓
     (表示結果)

この場合「※」と「▽」はmathtextで「℃αδ」は通常テキスト(Times New Roman)で表示されます。Times用に設計されているだけあって全体としてはうまくなじんでますね。

次に、mathtext.fontset : custom 、font.family:Times New Romanの条件下では、
シンボルのフォント(stix(Tex)表記に関するフォント)設定が可能となるので、下図のように試しにmathtext.rmのパラメータを IPAexGothic(デフォルトは確かsans)に設定して表示すると、



「¥n」で改行する以前のテキストは通常テキストと同じTimes New Romanなので文字化けしていますが、
改行後の$~$で囲ったmathtextと、mathrm{}で定義した日本語テキストはこの部分だけフォントが
変更され、文字化けなしで表示が可能です。

          ↓
     (表示結果)

おお~!うまく表示されました。
mathtext優秀です!

このとき、改行前のただのテキストはTimes New Roman。mathrm{}に内包されているテキストは
IPAexGothic、それ以外のmathtextはstixっぽいフォントになっています。(mathtext.rm のデフォルト
sansはmathtext.fontsetの値によって変化しないという事かもしれませんが、もしかしたらfont.familyに
影響を受けるのかな?とも思いました。)


今日まで文字化けとフォントについてご紹介しましたが、深掘りすればするほどデフォルトのサンセリフ
フォントでうまく動くように設計されている事が分かりました(笑)
今回作業に手間取った時間でまた一つ賢くなったと信じます。

では皆様良いお年をお過ごしください(^o^)

**77へ続く***   このページ最初に戻るこのページ最初に戻る ヘビカノTOPに戻るヘビカノTOPに戻る 環境システムTOPに戻る

0701**20230106


【第77回】 アルゴリズムを学ぼう!ー#1.探索編

ヘビカノ漫画皆様こんにちは。
冒頭突然ですが、年末年始に念願の90cmレプタイルゲージと
用品レイアウト用の棚を設置しました\(^o^)/
Pythonの影響か最近蛇ですら可愛く思えるほど爬虫類熱がキている
モトハシです。



こちらのゲージ、フトアゴ用に購入したドイツのエーハイムから出ている
オールガラスの製品です。
W90×D45×H50(cm)あるので大迫力です!(そしてスペースも取ります)
フレームのない見栄えも素敵ですがスライド開閉窓の接地面のクッションも
こだわりが詰まっていて、ガラスに馴染むよう透明シリコンで処理されていて
美しいです。
個人的にドイツ製は日本に負けないものづくりへのこだわりと実直さが
伝わってきて感心します。
タイミングのせいかECショップだとどこも入荷未定で円安も相まって手に入らないかと思いました。
そもそも専門店ですら取り扱いしている店が圧倒的に少ないような・・。
このサイズならモニターや大型のイグアナ、ボールパイソンなどフルアダルトで1m超えの生体以外なら
大抵何でも飼えると我が家一同歓喜しております。

そういうことでモトハシ的嬉しい&マニアックな話失礼致しました(笑)


さて、年明けの慌ただしさや高揚感も徐々に落ち着いてきた頃でしょうか。
毎年新春のこの時期はヘビカノ関連のスケジュールが割合多くなるので何を書こうかな~?と
考えていたのですが、ふと大学入学前にやっていたeラーニングの課題を思い出しました。

現在も同じ課題が導入されているのか分かりませんが、
当時は英語と理数系の基礎問題から、学部が生物系だったのでDNAアレイがどうのこうのという話や
探索アルゴリズムまで深いテーマを学習していました。(それ以外の科目ってあったのかな?覚えてないです)

当時は「ふーん」くらいで適当に流していたのですが、今実際にプログラミングをしていると、
あの時の課題って10代では高度な内容だったんだなあ・・と実感します。

そこで、今回は基本的なアルゴリズムとして、
線形探索と二分探索を例に、実際にPythonでコーディングしてみたいと思います。
正直、モトハシ普段仕事でプログラミングをしていてアルゴリズムを意識したことがないので(おい。)
理論的な説明は自信ないですが、以下広い心でご覧ください🤗


【線形探索】


探索する配列の先頭から最後まで順番に1つ1つ探索を行うアルゴリズムです。

アルゴリズム

探索配列は0~20までのランダムな整数を30個用意しました。
今回は「5」の数値を探索するために、配列の先頭から順番に真偽を問います。
このコードを実行すると、3箇所で「5」が発見できました。



Numpyのarray配列と繰り返し構文のrange(30)は暗黙的にrange(0,30)となり、
0から数えて30回繰り返しの意味なので、インデックスは必ず0から始まります。
なので、実際に数えたときは位置が1つズレます。
このアルゴリズムの身近な作業での使い道ですが、
例えばデータを整理するときにファイルサイズが○KB以下のデータは除外したり、
バラバラなファイルを同じ日付でまとめたいときなどに活躍します。

プログラミングの知識がない時にアルゴリズムを学んだ身からすると、
どこで使うんだろう??と困惑していましたが、
要領よく作業しようと心掛けていると意外と無意識にアルゴリズム的な考え方や行動しているもんだなあと、
今になって思います。

(次回に続く!)

     

***78へ続く***      このページ最初に戻るこのページ最初に戻る ヘビカノTOPに戻るヘビカノTOPに戻る 環境システムTOPに戻る

0701**20230120


【第78回】 アルゴリズムを学ぼう!ー#1.探索編②

漫画皆さんこんにちは、今日は前回に引き続き探索アルゴリズムの二分探索法を
Pythonでコーディングした例をご紹介します🤓
クレームが来ないよう最初に言っておきたいのですが(笑)……
“ソート済みの配列の中央値を基準に2つに分け、
目的の値がそのどちらのグループに所属するのかで繰り返し探索を行う“

という二分探索の基本原理は図や文章など既に様々なページで説明されて
います。
しかし、これをプログラムで組み立てた時、人によっては記述方式が微妙に
変化したり、プログラミングのルールと相まって表現がややこしい
ので、
なるべくポイントを噛み砕いてお話しようと思います。
そして多分モトハシが組み立てた以外にも色々な記述方法があるはずです。

ではコード全体を示します。



まず、前提ですが二分探索の計算量はlog2(n)回です。(※log2(n)のnは配列の個数です。
プログラム上の変数は探索範囲の終点を意味します。)




今回探索に用意した配列arrayは0~58まで合計30個あるので、
最大計算量は約5回となりますが、
本プログラムコードでは5回目の探索で反復条件を満たさなくなり、
探索が終了します。→


このように計算量が予め分かっていれば繰り返し構文はfor を使って記述できそうですが、
配列の中身が日付とか数値以外のものでもプログラムが機能するように、また1回の探索で必ず条件分岐と
探索範囲の確認を行う必要があることからコード40~58行目の探索はwhile構文で記述しました。
詳しい理由については後に記載しています。



以下の図は、プログラムコードと同じく探索目標を11、変数iを探索範囲の開始点、
nは探索範囲の終点として、iが成り立つ間反復処理を行う工程を表しています。



二分探索のアルゴリズムは配列の中央点の値を境として左右どちらに目標値が存在するかを探索します。
最初は配列の左側を先頭として0,1,2・・・29個(合計30個)
それぞれ数値の入ったボックスが並んでおり、その中央点は黄色で塗り分けをしています。
中央点は配列番号の最初と最後を足して2で割るか、
先頭から順番に数えてちょうど真ん中にくる値が中央点です。
今回のように配列個数が偶数であれば中央点が2つになるので、その2つの平均を取っても構いません。

このプログラムでは
初期配列の終点から始点方向(29→0)へ反復回数が増えるごとに探索範囲が半減していき、
(mdに格納された値)<(探索目標値) となった時点で
探索範囲が昇(0→29)方向に反転
するようになっています。
この“何回目かで探索範囲が反転する”という特徴を持っていることが、
なぜプログラム上で繰り返しをWhile構文で書いているかの理由にもなります。

もしかすると例外的な使い方もあるのかもしれませんが、
私が知る限りfor構文は線形探索のように回数が決まっている場合に使用することが多く
さらに(終点→始点)もしくは(始点→終点)の一方通行で反復処理を行うイメージです。
何回目の探索で目標を見つけられるか未知で、反復の時々で範囲が進んだり戻ったり変動する場合、
for構文での記述は不向きであり(というか逆に難しいので)、今回のようにWhile構文を使用します。

また初期配列個数が奇数の場合は1つの中央点に対して両サイドの配列個数は等しくなりますが、
今回のように偶数の場合、中央点mdは小数となり、これをintの整数に修正することで
探索範囲が(md-i)分反転した際インデックスのズレが生じます。
工程図では最後6番目の「10」が切り捨てられていますよね?
このズレによる探索エラーを防ぐためと、探索項目が存在しない場合の無限ループを回避するため
binary_search関数で探索範囲の始点、中央点、終点を毎回確認させています。
popup関数は探索目標が見つかった時と見つからない時で繰り返し探索を継続するか否か決定する関数です。


そして、コード全体を実行するとこのような結果が表示されます。



Low,Hight,Medianは毎回の探索範囲を表示しており、すべて初期配列から数えた配列のインデックス番号(i,n,md)です。
無事二分探索の実行ができましたね♪

では今日はここまでにします。
                  

     

***79へ続く***      このページ最初に戻るこのページ最初に戻る ヘビカノTOPに戻るヘビカノTOPに戻る 環境システムTOPに戻る

0701**20230203


【第79回】 アルゴリズムを学ぼう!ー#1.探索編③

漫画皆様こんにちは!
新年からしばらく探索アルゴリズムについて取り上げましたが、
今回紹介する「ハッシュ法」は導入の部分をお話して
最後にしようと思います。

私自身調べていると「探索アルゴリズム」は情報系の試験にも出題されている
ようですね!
この試験でも大抵ハッシュ関数は実際のアルゴリズムからmodなど四則演算に
置き換わっていることからかなり難易度が高いものと思われます。
最近SNSなどでよく「#」ハッシュダグを利用するケースが増えていますが、
この“ハッシュ(#)“とハッシュ法は意味が異なるようです。

ちなみにモトハシこのハッシュ法が気になって基本情報技術者試験と、
情報処理安全確保支援士のテキストを確認しましたが、後者のテキストには
実際のアルゴリズムに関して簡単に記載がありました。
興味のある方はテキストを手にとってみるか、ネットで調べても
情報は上がってくるので参考までに。


さて、本題に入りますね。
前述のように実際に現場で使用されているハッシュ法はかなり高度な計算の
ようで、Pythonでも有名なハッシュ関数(MD5、SHA224等‥)はhashlib
ライブラリとして提供されています。

例としてMD5(Message Digest 5)というアルゴリズムは任意の文字を
128bitに変換するハッシュ関数の1つであり、
bitとはコンピューター上の最小単位を表しています。
8bit=1byte であり、bitとは2進法(2を底にして)の1桁で表されます。
(byteは使用環境によってビット数が変わります)
MD5のアルゴリズムでは入力データのパディングを行うらしいのですが、
初見では何をしているか分かりません(笑)

そこで、bitや2進法について理解を深めたいと思います。
例えば、ひらがなの「あ」はUnicode番号がU+3042(HTMLコードでは12354) です。
Unicode番号とは文字に割り当てられた世界共通の番号で、この値は検索すると出てきますし、
Python標準モジュールでも確認することができます。



モジュールのord()はその文字のUnicodeコードポイントを整数で返し、
hex()は入力された整数から16進数に変換した文字列を返します。
16進数に変換された値の先頭に付いている「0x」は16進数であることの目印で添字のようなものです。
ここでutf-8≠Unicodeであることに注意してください。
utf-8はあくまでUnicodeで識別されたコードポイント(文字に割り振られた識別番号)を
別の16進数数値に変換する方法で、独自の変換ルールを持っています。

そのため上図で「あ」をutf-8に変換したとき、符号混じりの「e38182」というUnicode番号とは全く別の
数値になります。


utf-8の他にもShift_Jisなど多数存在する文字コードは、
符号化方式(URLエンコード) と言って、
文字列をバイト数に変換する方式の1つであり、
その方式のルールに従って「3042」→「e38182」
など、コードポイントが決定します。
これはUnicodeで対応付けされたコードポイントを、
実際にコンピューターで利用できる
形に変換するメソッドです。


以下参考として
「あ」を2進数で表した時の数値を載せました。
PCのメモ帳の文字コードはUtf-8なので
試しに「あ」を入力してプロパティを確認してみて
ください。3byteなはずです。




同じ文字でも文字コードが違うと数値化した結果が別物になるなんてややこしいですが、
このような変換ルール(≒ハッシュ関数の考え方)で、
結果として得られた数値を利用して識別や探索を行うのが平たく言うハッシュ法です。

とは言っても、エンコードの変換ルールを説明するのはナンセンス😨
もっと一般的な計算のベースとなっている部分に焦点を当てたいので、モトハシは考えました。
例として先程挙げた「あ」について、Unicode のHTMLコード「12354」は一般的な10進数表記
(10で桁が上がる表記)のようなのでこれを元に計算を確認しましょう。

【10進数(整数)→16進数への変換】

まず「12354」数値を16で割って16進数に変換してみます。
変換方法については調べれば情報が出てくるので割愛しますが、
私が大学時代の講義では公約数を求めるときのように連除法を使い、
除算で出た全ての余りを、計算順序とは逆に並べて変換を行っていました。
(ちなみに当時この講義の単位を落としています…😲 (不安でしか無い))

① 12354÷16=772…2 (1回目の割り算)
② 772÷16=48…4 (2回目の割り算)
③ 48÷16=3…0    (3回目の割り算)
④ 3÷16=0…3    (4回目の割り算)

2回目以降の割り算は1つ前の計算で得た商を割られる数にして再度計算します。
これを商が0になるまで繰り返し、最後に結果を④→①、商から先頭に並べると「3042」となります。
つまりこれがUnicode番号(=12354)で、この16進数表示の番号は文字コードの世界基準です。


【16進数→10進数(整数)への変換】

そして16進数「3042」から10進数に変換する際は、
(3×163)+(0×162)+(4×161)+(2×160)=12288+0+64+2
=12354

このように3042の各桁に基数(16倍ごとに桁が増えるというルール)を掛けた総和が10進数表記となります。
ここで基数は別名重みとも呼ばれますが、DeepLeaningのニューラルネットにも
重みという概念がありますね。
一見別物のようであっても入力されたデータをあるルールに基づいて変化するという考え方は
同じであることがよくわかります。


【16進数→2進数(bit)への変換】

同じように16進数「3042」から2進数に変換する場合、16進数の数え方は0~9までは10進数と同じで、
その後はA~Fのアルファベットで表されます。
また16進数表記の1桁は2進数表記の4桁分に相当する決まりがあるので、
「3」「0」「4」「2」の1桁ごとに先程と同じく①~④の計算を行い、商と余りを出します。

イ) 3÷2で連除法 =1…1、1÷2=0…1       (1桁目3の割り算)
ロ) 0÷2で連除法=0…0      (2桁目0の割り算)
ハ) 4÷2で連除法=2…0、2÷2=1…0 、1÷2=0…1  (3桁目4の割り算)
ニ) 2÷2で連除法=1…0、1÷2=0…1     (4桁目2の割り算)

この結果を並べると「11 0 100 10」となります。
ここでは16進数の1文字は4bitに相当するので、2桁目以降、連除法の余りの総数が4桁未満なら
左側を0パディングした値
「11000001000010」が答えとなります。

Utf-8の16進数表記「e38182」も同じように2進数に変換することができます。


今紹介した計算、特に2進数への変換がハッシュ関数のベースとなっている考え方ですが、
現実のハッシュ法は情報セキュリティで使用されることもあり、
ハッシュ値(計算結果)から元の値を逆算が難しい点や、
入力文字の長さが足りない場合でも一定の長さのハッシュ値を排出する処理が必要な点などから
計算の複雑さが容易に想像できます。
アメリカの国家安全保障局(NSA)によって開発されたハッシュ関数のアルゴリズム
「SHA256」に関してはその計算プロセスがWEB公開されているので、
詳しく勉強したい人は検索してみてください♪
いや~、単位落としたくせにここまで書けたらモトハシ的には上出来です(笑)
長くなりましたが今日はここまで($・・)/~~~

     

***80へ続く***      このページ最初に戻るこのページ最初に戻る ヘビカノTOPに戻るヘビカノTOPに戻る 環境システムTOPに戻る

0701**20230217


【第80回】 netCDFを扱う #1.

漫画皆さんこんにちは!
今回本橋は地形データを触ってみたので、その概要と使用データについて
書いていこうと思います。
そもそもこういうマッピングはGIS(地理情報システム)という
地図と空間データを組み合わせたものが有名ですね。
何年か前、ダムのエレベーション変更に国土交通省HPの国土地理院から
標高データを取得したことがありますが、
イメージと違って残念だった記憶があります。
というのも実際のダムは山の斜面を平地に均して建造されていて、
ダム湖内の機械が衛星から取得した座標ポイントを使って標高に変換
しましたが、どうもこの標高データというのは山間部だと山肌?を測量
しているのかあり得ない数値だったのを覚えています。
まあメジャー持って現地に出向いて測るわけにもいかないでしょうし、
これが限界だなあと(笑)

今日はそれと似たようなデータでアメリカ合衆国のNOAAからETOPO1という
世界の陸の標高と海底水深のデータを扱ってみることにしました。


↑View ETOPO1_Ice_g_geotiff.tif in Inkscape.

上のモノクロの世界地図はETOPO1を画像で表したときのデータです。
以下はこれと同じ地図をnetCDFというGISで利用されるデータ形式で
ダウンロードし、Pythonで表示した結果です。




netCDFの正式名称はNetwork Common Data Fromであり
気温・湿度・気圧などの多次元データを格納しているファイルで、
一般的にこのデータを図化する場合はPhotoshopを使うようなイメージでしょうか?
時間や場所などの次元をレイヤーのように投影することで表示出来るようです。
(私も使ったことがないのでさっぱりですが)

netCDFについてのプログラミング的な知見や詳細はユニデータというコミュニティで公開されています。
上記のスクリプトではNOAAのホームページからデスクトップ上にダウンロードした
netCDF形式のETOPO1Python上で読み込んでいます。
(※netCDFはダウンロード直後は圧縮状態のため、専用ソフトで解凍処理を行わないと読み込めませんよ!)

etopo1という変数を参照すると、データに関する説明書きが表示されるだけで、
データの中身や数値配列は直接出て来ないことがnumpyの配列参照や
pandasのデータフレームとは扱いが異なる点です。
netCDFの内部構造や格納値を参照するためには、
Python標準モジュールのinspectをインポートした状態で、
inspect.getmembers(dataset)を実行すると
以下のようにリストに格納された状態の内部関数と変数が
(‘属性’、<オブジェクト>)、(‘キー’:関数)などがコンマ区切りで確認できます。―(6行目)


↑netCDF内部構造参照結果

ここでキーに当たるタプル左側の値をetopo1.dimensionsやetopo1.variables、
etopo1.__class__など、個別に指定し実行すると、特定のデータ情報を参照することができます。



余談ですが__class____doc__などアンダーバー(アンダースコア)から始まる関数や変数は
クラス構造でよく見かけますが、関数の使い方を区別する意味があるようです。
例えばある関数の返り値が3つあって、そのうち1つ使わない物があれば、
a,b,_=function()と記述することでメモリの節約ができるんだとか。
     ※注 本文上記で赤く示されたアンダーバーは、画面表示上全角に見えますが、
          コードスクショで確認できるように、実際は2連半角です。


話が逸れましたが、先程のnetCDF内部構造参照結果の7行目以降、
float64 x(x)… float64 y(y)… int32 z(y,x) とありますが、
これがデータセットの値に紐づく変数になっています。
netCDFのDataset()で開いたデータは普段使用しているライブラリのように
階層的な構造化された状態で返ってくるので、
etopo1.variables[‘x’][:] 、etopo1.variables[‘y’][:]という風に記述するとデータが参照できます。

聞き慣れないデータセットと少し難しい話でしたが、次回は図化についてお話していきます。



     

     

***81へ続く***      このページ最初に戻るこのページ最初に戻る ヘビカノTOPに戻るヘビカノTOPに戻る 環境システムTOPに戻る

0701**20230303


【第81回】 netCDFを扱う #2

蛇使いな彼女皆さんこんにちは。
前回はnetCDFの構造と参照の仕方について簡単に説明を行いました。
今日はデータの詳細と図化をしていきたいと思います。

今見ているブログページを上スクロールしてもらうと
netCDF内部構造参照結果」というスクショ画像があります。
(※第80回本文)

これを見ると記載されていることですが、次元サイズはxが21601で
これは-180度から180度までの範囲の東経を経度としています。
yは次元サイズ10801で-90度から90度までの北緯を緯度としています。
これらの緯度(y)経度(x)を格子状で区切った時、
1つのセルに当てはめられる整数型の値がzで、つまり標高(m)ですね(^^)

例として世界マップから日本の位置を抽出して
標高マップを表示させてみます。
スクリプトコードは既に個人ブログ等で参考となる資料があったので、
今回はmemomemokunさんのコードを基本としてアレンジを加えました。

そう言えば、プログラムコードに関する著作権に関しては
丸パクリはダメだけど、元のコードを参考に新たなプログラムを作ったり
組み替える分には認められる
ようです。
ただ実際法律は曖昧でその区分も個人か企業かなど
時と場合で変わるでしょうから、
トラブルを避けるため最初に規約として提示したほうがいいみたいですね。

ちなみに本ブログのコードは個人・企業問わず誰でも
研究開発や趣味、勉強に使用することができます。
(写経をしないとコーディングは上手くなりません!)

商用利用については、公開しているプログラムコードと全く同じものを
販売目的で使用するのはご遠慮くだい。

その他は特に規制ありません。

では全コードを表示します。



1~19行目まではnetCDFからデータセットの読み込みを行っています。
そして20行目からは日本の座標位置を取得しています。

#japan:東経127-150度,北緯25-47度
lower_left_x=np.argmin(np.abs(x-127))
lower_left_y=np.argmin(np.abs(y-25))
height=np.argminnp.abs(y-47))
width=np.argmin(np.abs(x-150))

X=X[lower_left_y:height,lower_left_x:width]
Y=Y[lower_left_y:height,lower_left_x:width]
Z=Z[lower_left_y:height,lower_left_x:width]

コード内、小文字のはそれぞれ-180~180と-90~90の範囲を1次元のfloat型で表しています。
つまりこれは東経と北緯が何度なのか、地図上の位置を示します。
大文字のXYは、matplotlib によって実際に標高Zをマッピングさせるため、
np.meshgrid (x,y)で与えられた2つの配列から2次元の方眼線を作成します。
今回日本の東経は約127度~150度。北緯は25度~47度で切り取ることにしました。
切り取りの方法は
np.argmin()を使って、四角形の4カ所のポイントの東経北緯に等しいインデックスを抽出します。
後はマッピング用の変数X、Y、Zから日本の座標位置を切り出します。

各標高に対する配色については、元のコードと地理院地図を参考にして少し書き直しました。

# 配色設定
cmap = colors.ListedColormap(['blue', 'royalblue', 'cornflowerblue', 'skyblue', 'powderblue', 'g', 'khaki', 'wheat', 'tan', 'peru', 'chocolate', 'salmon', 'indianred', 'brown', 'snow'])
bounds = [-8000, -4000, -2000, -1000, -500, 0, 100, 300, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000]

その結果がこちら・・



上手く切り取れました!

参考までにETOPO1の他にもNOAAにはGSHHGという世界の海岸線や湖沼を含む高解像度地形の
ベクトルデータも公開されており、こちらも自由にダウンロードすることができます。
ただし、ダウンロードしたgshhg-shp-2.3.7データに関してはモトハシも普段目にしない
圧縮形式やファイル名だらけで、どうやらCADで扱うタイプのファイル?なのか詳しいことはよく分かっていません。
(ダムとか湖の解析に役立ちそうなのに・・・)

まだまだ勉強することがたくさんあるなあ~と痛感しましたが、ゆるゆる頑張っていこうと思います。
ではでは!

     

***82へ続く***      このページ最初に戻るこのページ最初に戻る ヘビカノTOPに戻るヘビカノTOPに戻る 環境システムTOPに戻る

0701**20230317


【第82回】 番外編②~SVGファイルとその表示(JavaScript)について

蛇使いな彼女皆さんこんにちは。
この前netCDF①の記事の始めに世界地図の画像を掲載していたと思います
が、あれ実はWEBサーバーにアップロードすると上手く表示できなかったと
相棒のCさんから連絡があり、急遽スクショを代替えしたときのお話です。

元々ダウンロードしたときの拡張子は.tif(ティフ)という雑誌など印刷用に
適した高画質の画像(←ファイルサイズが大きくWEBでの使用に向かない)
だったので、Inkscapeで一度地図画像を開き.svgという地図やアイコンに
よく使用されるベクタ形式に変換した状態で原稿を渡していたのにも
関わらず、表示ができないのは何ぞや?と不思議に思いました。

このとき変換したSVGという形式はテキストで開くことができる画像形式で、
HTMLやCSS、JavaScriptなどフロントエンドで動く言語で
アニメーションなど編集することも可能です。
結果を言ってしまえば、ベクタ形式に変換できたけど、
そもそも中身のパス(線)が無いデータのようでした。


ここで知っている人が多いかもしれないプチ情報ですが、
よく求人などで「フロントエンドエンジニア」、「バックエンドエンジニア」
などという文言がありますが、フロントエンドエンジニアとはWebサイトの
見た目やレイアウトなど、表示に関する開発を行う人の事で、
環境システムでは相棒のCさんのことですね!
対してバックエンドエンジニアはデータベースやサーバーからデータを取得
したり解析する人のことを指すらしいです。
Pythonはバックエンド側になるのですが、今思えば役割が理にかなっています(笑)

最近はフロントエンド言語のJavaScriptでも「Node.js」という
WEBサーバー側でJavaScriptを動かすことのできるパッケージがあるよう
なので、これを使ってSVG形式の画像がどのようにWEBに表示さるのか
実際に試してみようと思います。

■JavaScript実行環境の準備




テキストエディタ等の環境を使用している場合であれば拡張機能がついているので、JavaScriptの開発に
必要なツールをダウンロードします。
(ダウンロードの場所ですが、VisualStudioならプロジェクト作成画面で「JavaScript」を選択すると
対応するフレームワークが表示されます。
該当するものが無い場合は画面の右下あたりにリンクが表示されるので、ここから必要なツールを
ダウンロードできます。)



リンク先ではJavaScriptの実行環境であるNode.jsのダウンロードを忘れずに行ってください。
Node.jsはPythonでいうpip(パッケージ管理)の役割を担っていると同時に、
JavaScriptの開発をPythonやその他の言語と同じくローカルな環境で実行できるように
してくれるツールです。

(以下JavaScriptをJSと略して記載します。)
ダウンロード完了したら、「新しいプロジェクトの作成画面」に戻り、
Node.jsWebアプリケーションのフレームワークを選択します。



プロジェクトの先頭に「'use strict';」と表示された状態でservaer.jsが開くので、
Webサーバーへのリクエストはこの次の行から記述します。

■サーバーリクエスト



Pythonで変数を指定する場合、「=」の左辺側が上書き可能な変数に認識されますが、
JSでは「var」や「let」といった変数型を宣言しないと思わぬバグが生じるらしいです。
このあたり昔触ってたVBAを思い出しますね。
そして4行目~6行目はライブラリやモジュールのインポートと変数宣言を同時に行っているように見えます。
変数=require()はPythonで言う「import ライブラリ as 変数」に対応する構文で()の中には
モジュールを入力します。
6行目の変数htmlには、Webサーバーで表示させるHTMLを記載したファイルを指定します。
ここではプロジェクトと同じディレクトリにPage1.htmlを追加しました。

関数宣言はPythonでは「def 関数名(引数1,引数2):」という宣言でほぼ統一されていますが、
JSの場合はfunctionのほかに例えばアロー関数(=>)や
上のコードにあるようなhttp.createServer(ここに処理)モジュールの
オプションとしてfunction()で関数宣言させることができる
みたいです。
(Node.jsのガイドに載ってないオプション記述ですが問題なく走るようです。)

■イラストの用意

イラストレーター等のソフトでSVGファイルを作成し、
実行中のJavaScriptファイルと同じディレクトリに保存しておきます。



今回は上記のイラスト以外にもLottieFilesから個人・商用で無料で使用できるアニメーションを
JSONファイルで取得したのでこの表示方法についても説明します。



■HTML



・自作イラストのアニメーション(星)
16行目~31行目までは自作したイラストに回転のアニメーションを付与しています。
3つの星全てに対して1つのアニメーションを付けるので、それぞれのパスをグループとして扱います。
沢山ある数字はイラストのパス(線)です。imgタグでは表示できないので、SVGタグを使用します。
このタグはSVGファイルをテキストエディタで開いた時に確認する事ができるので、コピペして使用します。
こちらはタグだけで記述できます。


・LottieFilesのアニメーション
33行~45行まではLottieFilesの表示に必要なJavaScriptコードを記述します。
LottieFileの表示にはプラグインが必要なので、今回はlottie_webを利用します。
Gitで公開されている為、Node.jsがインストールされている状態であればプロンプトから
‘npm install -g lottie_web’と入力すればPythonのpipと同じ要領でモジュールのインストールが可能です。
ただAfterEffects関連の他のプラグインも必要なのか、
今回スクリプト上で上手くインポートできなかったので、33行目の
scr=’ https://cdnjs.cloudflare.com/…/lottie.min.js’ と記述し、サーバーからプラグインを借ります。
この機能はCDNサービスの一種らしいです。めっちゃ便利ですね。
42行目のpathにダウンロードしたLottieアニメーションのURLを記述します。

■実行



Ctrl+F5で実行します。上手くいくとプロンプト画面にログが表示され、インターネットタブが開きます。
動画でお見せできないのが残念ですが、サイズ調整をしていない状態ではLottieアニメーションは
画面いっぱい大きく表示されてしまうようです。




最後に、JavaScriptを触ってみた感想ですが、
Pythonで使い慣れているとかなり使いにくい、そしてややこしいです(笑)
サーバーリクエストはまだ意味が分かりますが、
関数の組み方が独特なのでHTMLに同化している部分はあんまり触りたくないなあ~というのが印象でした。
まあ、サーバー上で動く言語だから、問題が起きないように色々制限とか操作手順があるんでしょうね。

これにて番外編終了します。ではまた。

     

***83へ続く***      このページ最初に戻るこのページ最初に戻る ヘビカノTOPに戻るヘビカノTOPに戻る 環境システムTOPに戻る

0701**20230407


【第83回】 Tensorflowの時系列予測に関しての備忘録 #1.

蛇使いな彼女皆様こんにちは。
最近春とは思えないくらい暖かい陽気ですよね!
今年で入社5年目になりますが、研究に関するお仕事はこの春から
学会に向けて今集まっている水質データを約0.05~0.1の単位で見比べる
という大変地味な作業に取りかかろうとしています。
また、このブログを開設してからグッズ製作に始まり、対外的な
プロジェクトの広報にもヘビカノを起用して頂けるようになりました。
最近はプライベートでも研究内容について聞かれるのですが、
いざ何から話せばいいか分からない事を思えば、この数年間で多少は
プロとしての経験や知識が身についたのかなとも実感しました。
なにより興味を持ってくれるのは大変嬉しいことですね。

とまあ、ご挨拶はこれくらいにして。


今回は前に調べていたテンソルフローのデータ予測モデルの仕組みについて
気になったポイントをお話しようと思います。
研究会でもほんの少し内容紹介しましたが、ヘビカノの過去の記事の中でも
中級~上級の知識なので、当然モトハシもまだ理解していない部分はあります。
公式で紹介されているコードが具体的に何をしているかなぞりながら
話を進めますが、解釈に間違いが在ればすみません。大目に見てください。

Tensorflow (テンソルフロー)はscikit-lean (サイキット・ラーン)と同じ
機械学習用のライブラリですが、
サイキット・ラーンに比べてパラメーターの数が多く、モデルの層を自分で
調整
することが出来るしないといけない点で、より上級者向けのライブラリと言えます。

G●●gleが開発し、そのシステムにも使用されているらしいですが、
チュートリアルはあれど、確信に迫るような詳細情報が抜け落ちている部分も
見受けられます。
自分で応用するには付け焼き刃の知識では難しく、私自身結局、
元が3次元以上のデータのインプットってどうするの?
というところで止まっています。

チュートリアルではサンプルデータとして2次元で表せる気象データを使用していたので、
せめてこれを手持ちの水質データに置き換えて上手く動くか試してみました。
自動昇降装置のデータそのままを扱うことは出来ないので、どのような答えを求めているのか、
その結果を出すために適切なデータ編集は何か?を考える必要があります。


データの準備

以下の表はある地点の躍層形成に関わる特徴1~8で水質特徴9を予測できるか
テスト目的でデータの編集方法を試行錯誤した例です。
(読者の皆さんはどのようにデータ整形しますか?きっとモトハシより上手く出来る人も居ることでしょう…)



まず特徴2は次元が多すぎて扱えないのと、
特徴1は予測より分類に適したラベルなので学習には使用しません。
特徴9は3つの特徴を持っていますが、理想は予測結果の出力でこれらを1つの正解ラベルに設定したい
考えています。
そこでテンソルフローのfeature_columnモジュールで特徴を交差させる、
バケット化するなどの処理方法についてこれまで調べていましたが、
丁寧な解説が見つけられなかったので、後日図書館で資料を漁ってみたいと思います。

仕方ないので今回はpH、PCY、Chl-a、3つの特徴のうちひとまずPCYを正解ラベルに設定したいと思います。
以下データの要約を見てください。

このときのPCYは表層0.1m~5.0mまでの範囲で500cells/mL以上の値を示す割合を示しています。
(pH、Chl-aも同様に境界値を超える値の割合を示す)
元の形式はfloatで最小0~最大1の範囲に収まりますが、
念のためにデータ全体を正規化しました。(チュートリアルでは標準化されています)



その後の学習データ作成・モデル入力・評価に関してはチュートリアルで使用しているメソッドと引数を
使用して進めていきます。
実際のデータを使用した予測では、コードのどこを変更しなければならないのかについても
理解してもらえたら幸いです。


メソッドについて




このようにスクリーンショットには収まりきらないくらい長々とメソッドが定義されています。
メソッド間でデータや設定を共有する為には構造化されていた方が扱いやすいですが、
自分で予測モデルを立てる場合は注意点も出てきます。これについては後に示します。
※予測に必要なメソッドについて詳細は先ほど文中に示したチュートリアルを確認してください。

チュートリアルでは予測に必要な手順の説明をしながら、異なるモデル間の検証がされているので、
各メソッドで何をしているか分かりづらいです。
この記事では実際に使われているメソッドの働きと意味を整理していきます。

使用するメソッドとモデルは以下の通りです。

class WindowGenerator():    モデル学習用のデータ生成と各メッソドに紐付く設計図
split_window():          インプットとラベルを分離し、変形するメソッド
make_dataset:          時系列データを学習に適したデータセットに編集するメソッド
compile_and_fit:         トレーニング手順の指定とモデル代入用のメソッド
@property:            他のメソッドを呼び出す為のメソッドを定義する
train()/test()/val()/example():  各データセットの呼び出し

※描画メソッドはtrainデータセットから、1セットのインプットデータとラベルデータを抽出し、
モデルに代入した結果と合わせて図化するだけなので説明を省略します。


 使用モデル:線型モデル(単体/複数)



linearモデルのレイヤーは入力データの最後の次元をunits=1の次元に変換して返します。
チュートリアルでは単一時間のインプットデータと1つのラベルを入力対象にしていますが、このときモデルは時系列の流れに左右されない予測を行います。
multi_step_denseは複数の時間入力を可能にしたモデルです。
入力データを平滑化して結果はlinearと同じく1つ返されます。
平滑化することで、本来ある時系列前後の関係性を重要視していないモデルです。



     

***84へ続く***      このページ最初に戻るこのページ最初に戻る ヘビカノTOPに戻るヘビカノTOPに戻る 環境システムTOPに戻る

0701**20230421


【第84回】 Tensorflowの時系列予測に関しての備忘録 #2.

蛇使いな彼女【時系列に対応したデータセットの生成】
チュートリアルでは、
tf.keras.preprocessing.timeseries_dataset_from_arrayで3次元以上の
データセットが作成されています。





特に上記split_windowmake_datasetメソッドについて何をしているかと いうと、


例えば以下で(400,3)の連続したサンプルデータについて考えます。
ここではWindowGenerator直前の24個(時間)のデータをインプットデータとして
1つ下のインデックスにスライドさせた24個(時間)のデータを
ラベリング(予測)したい場合のデータセットを作成します。
(WindowGeneratorlabel_columns=[‘PCY’]について、この説明では
ダミーデータを例にしているので正解ラベルは関係ありません。)


dataset生成



辞書outputの結果を参照すると、下のスクリプト画像【output参照】の冒頭に(指定したバッチ数,合計窓数,
特徴量)という形で配列が返ってきますが、
おそらくこのシェイプの意味が分からないと思うので(というか勘違いしやすい謎な表示になっています)
以下イメージ図で補足します。

データセットとバッチの構造について

何段あるか分からない引き出しを思い浮かべてください。
下駄箱か薬品棚のように中に細かい仕切りが沢山あって
その中にデータが入っているとします。
このときのバッチ(batch_size=32)の意味は、25行×3列のデータ配列が32個で1セット排出されます。
(下図②)



②のデータの塊が何セット出来るかは不明(①:データセット全体)で、
このときoutput[n] は0~11まで指定可能で合計12セット作成されていました。
データ個数によって自動で変化しているような気はしますが、第一次元に何個生成されるか事前に分からない
うえ、シェイプ結果に反映されないので、 (32,25,3)という違和感のある表現になっているようです。
実際は在るのに反映されていない①の次元を合わせると4つ以上の次元を持ったデータセットが出来ます。

output参照





生成されたデータセットの各25×3のデータは
1つにつきインプットデータとラベルに分割する必要があります。
wide_windowは24時間の入力から行を1つスライドさせた24時間分のデータを予測するのに必要な
データセットを切り出す出すための設定で、
最初のバッチoutput[0][0]を例にあげると、591~662までの値に対するラベルとして594~665の値を
定義します。
当然この方法は時間方向のどこで分割するかを決めるだけで、
各特徴量から正解ラベルを区別する事はありません。

正解ラベルの分離




そこで

split_windowメソッドでは指定した特徴列(実際はPCY)を抽出してラベルを生成しますが、
インプットデータからラベルを除去する処理がされていないので、
このままモデルに代入すると正解ラベル(PCY)を含んだまま学習していることになります。


モデルによっては結果に影響しない場合もあるのでしょうか?
モトハシはそれってどうなのという素朴な疑問が湧きました。

こうして出来たtimeseries_dataset_from_arrayの整形配列trainは
チュートリアルでは直接中身を参照すること無くモデルに代入しており、モデルの精度評価はされていますが、テストデータの予測結果についてはほぼスルーされています。
正解ラベルと予測結果は自分で確認しなさいということですが、この理由について私の意見は、

def make_dataset(self, data):
data = np.array(data, dtype=np.float32)
ds =tf.keras.preprocessing.timeseries_dataset_from_array
(data=data,targets=None,sequence_length=self.total_window_size,
sequence_stride=1,shuffle=True,batch_size=32,)
 ds = ds.map(self.split_window)
 return ds


make_datasetメソッドでデータセットをバッチ数32で生成する際、
時間が連続することによる偏りを最適化するためのオプションshuffle=Trueが
モデル学習に良い働きかけをする一方で、
以下のようにメソッド間でデータセットを呼び出したときに
注意しないと思わぬ結果を招くからだと考えています。

     

***85へ続く***      このページ最初に戻るこのページ最初に戻る ヘビカノTOPに戻るヘビカノTOPに戻る 環境システムTOPに戻る

0701**20230505


【第85回】 Tensorflowの時系列予測に関しての備忘録 #3.

蛇使いな彼女

【予測結果の参照に関する注意点】




学習済みモデルの予測値とラベルの参照に関する注意点ですが、
チュートリアルではこのようにtrain_dftest_dfのデータ編集と呼び出しも
WindowGeneratorに関数で連結させてしまっています。
これのどこが問題になるのかというと、

サイキット・ラーンでよく目にする予測方法を例に挙げてみましょう。
以下のように学習済みモデルからテストデータの予測値を取得したとします。
answer=Model.predict(wide_window.test)

このときモデルの予測結果は変数代入によってanswerに固定されるが、
test_dfの正解ラベルは参照不能
となっているはずです。
なぜかと言うとwide_window.testを呼び出すことによって、
以下のtestメソッドが動きます。(train/valも同じ)

@property
def test(self):
 return self.make_dataset(self.test_df)


「@property」は値を呼び出す目的で直下の関数が動くようにハンドリング
します。
よって、この場合make_dataset関数とWindowGeneratorからtest_df
同時に呼び出され、shuffle=Trueの影響からWindowGenerator.testを実行
するたびに毎回シャッフルされたデータセットが出力されます(°°;)

買ったばかりのトランプは箱から出して表を見ると、カードの並びが統一されていますよね。
元のtestデータは時系列順なので新品のトランプAに例えると、
1回の予測につきAは裏向きで1度シャッフルされた状態からモデルによって
予測結果が返されます。
当然デッキは裏向きなので実際の答えはひっくり返さないと見れませんね?
私が言いたいのは、デッキの表面を見るとき(testデータを参照するとき)、WindowGenerator.testから
testデータを呼び出すことによって、予測結果answerの元となった正解のシャッフルデッキとは別物になってしまう
という事です。(これはAとは別物の新品のトランプBを1回シャッフルして表向きに開示したものです。)

チュートリアルのメソッドではこのようなルールでコードが組まれています。
この問題をどう処理するかについてですが、shuffle=Falseに設定を変更するか、
モデルに入力する前にデータセットの参照値を保存しておくかのどちらかの手段を取る必要があります。
(※ここでself、selfってなんやねん!と思う人も居るかと思いますが、
平たく言うとクラスのWindowGeneratorがselfで、self.test_df = test_dfという関係です。
WindowGenerator.testはtest_dfではなく@property直下のdef test(self)を意味しています。
ややこしいですね~。)

以下テンソルで作ったデータセットの参照方法について2通り示します。


【as_numpy_iterator()】



特徴3列目をラベルにした場合のデータセットを
as_numpy_iteratorでリスト化して取り出した場合、
(バッチ層数n,インプットラベル,バッチ数,Time行,特徴列数)
⇒(12,2,32,24,3)という形式で返ってきます。

←スクショでは
インプットに対応するラベルをセットで表示しています。
見て分かるとおりデータセット全体の階層を表す次元の次に
インプットデータとラベルデータ2つのデータが格納されています。
データを順番に取り出す場合は
繰り返し構文などを使うと良さそうですね。
次にnextとiter関数を使った参照方法ではコレクションの中から
1つ参照する事が出来ます。























【next(iter())】


この方法はexampleメソッドで使用されていて、
チュートリアルではシェイプの参照や描画用に
参照値が利用されています。
exampleメソッドでは内部でWindowGenerator.train=train
メソッドを呼び出し、
next(iter())で参照した値をresultに留めておくことで
データセットが自動的にシャッフルされるのを防ぐ
ことを可能にしています。


def example(self):
"""Get and cache an example batch of `inputs, labels` for plotting."""
 result = getattr(self, '_example', None)
if result is None:
  # No example batch was found, so get one from the `.train` dataset
  result = next(iter(self.train))
  # And cache it for next time
  self._example = result
return result



getattr(オブジェクト,‘名前’,デフォルト値)は
オブジェクトの値を取得することができるPython組み込み関数です。
最初の result = getattr(self, '_example', None)では
self._exampleは存在しないので、
デフォルトで設定されているNoneを返します。
result=Noneであるとき、trainメソッドが呼び出され
シャッフルされたデータセットの最初のバッチが
self._exampleに固定されます。

すると二回目以降の呼び出しはresultの値が出力されます。
testデータの参照目的では画期的に思いますが、
各バッチごとにモデルに代入しないと正解ラベルとの比較が
出来ませんね。
なのでチュートリアルではサンプルデータの描画用にのみexampleメソッドを使用しています。

     

***86へ続く***      このページ最初に戻るこのページ最初に戻る ヘビカノTOPに戻るヘビカノTOPに戻る 環境システムTOPに戻る

0701**20230519