就労継続支援A型事業所 わーくぷらすin大阪

就労継続支援A型事業所 わーくぷらすin大阪

大阪府大阪市にある就労継続支援A型事業所わーくぷらすの活動の履歴

Amebaでブログを始めよう!

利用者スタッフのrickyだ。

 

この記事は本事業所内の、仮想環境のマイクロブログに書かれたものを一般公開のために一部修正したものである。

オリジナルの原文は、所内ローカルネットワークからのみ読むことができることをここでお断りさせてもらう。

なぜ、以前の記事をここで紹介できなかったのか?

その理由は、バックナンバーの記事は、所内PCについての情報が多いことも理由の一つとして挙げられるが、

仮想環境のエミュレーター、インストールするOSの種類やバージョンのナンバリングによって、

外部に紹介するには、あまりにも再現性に乏しいと思われるからだ。

(実際、前回のpython3の記事がそうだった。

 python3のmatplotlibの日本語文字列ラベルの表示が、別の環境ではリストごと入れ替わってしまうのを確認した。

 それがpython3のバージョンによるものなのか、それかパッケージの違いか、原因は分からない。

)

 

以下本文

 

仮想環境の記事で、コマンドプロンプトからでも、
windowsで行っている基本的な操作自体は扱えることを説明した。

 

その中でも、編集内容を、新規テキストに名前を付けて保存、という項目において 

 

echo “編集内容” > “ファイル名” 

 

とのコマンドから行えると記述したことだろう。 

 

しかしながら、Windowsにおいても、Linuxにおいても、
テキストの編集は、基本的な動作であり、
本来は、ユーザーの手によって、このようなコマンドを打ち込んで行うものではないのだ。 

 

windowsにおいて、テキストファイルを編集したい場合、
まずはメモ帳のアイコンをクリックして、編集するのと同じことだ。
コマンドから、わざわざテキストを 標 準 出 力 に 表 示 さ せ て か ら 保 存 するなんて奇特な奴が居るのなら、是非お目にかかりたいものだ。 

 

(ただし、こういった手法は、マクロを動かした時に、得られた結果をログを残すという動作におい ては、非常に役に立つことを忘れてはならない。) 

 

そこで、本記事は
メモ帳を開いて、テキスト編集を行うというところを
Linuxの “端末” においてどうするかを記載する。
(テキストの編集においてさえも、きっちり一項目使ってまで説明するのは、
テキストの編集自体のハウツー自体が、Linux初学者の大きなハードルとなっているからだ。
これがないと、サーバの設定や、コンフィグファイルさえ触れやしない。)

 

Linuxにおいて、コマンドプロンプトを通しても利用できるメモ帳の中で
おそらく最初からインストールされているものでは、
以下のアプリケーションが上げられる。 

 

・nano(初心者向け)
・vi(サーバ管理者や、プログラマ希望者向け) 

 

まずは、多くの者にとって需要のあるであろう nano から説明する。 

 

nanoは、コマンドから使えるメモ帳の中では、一番windowsのメモ帳に近い操作感覚で編集できるこ とから、
Linux初学者向けのメモ帳として、広く知られている。 

 

nano “ファイルの名前”

 

これだけで簡単に立ち上がる。
“ファイルの名前” が同じフォルダの中に既に存在する場合、保存の動作は上書き保存となり、
存在しない場合、保存の動作を行ったときに、名前を付けて保存となる。 

 

テキスト編集画面自体は、windowsのメモ帳の操作感と大差がないだろう。 

 

ファイルの保存、文字列の検索といった操作自体はコマンドからとなるので、ショートカットキーが必須となるが
それも、常に下に表示されるので、覚える必要もない。 

 

コマンドに、^ という記号がアルファベットについているのが見えるだろう。
これは、コントロールキー(Ctrl)を押しながらという意味であり、
コントロールを押しながら指定のアルファベットのキーを押せば、
期待通りの動作が行えることだろう。

 

例えば、以下の本文

 

いろはにほへと ちりぬるを
わかよたれそ  つねならむ
うゐのおくやま けふこえて
あさきゆめみし ゑひもせすん

 

を、waka.txt という名前で保存したい場合を想定して考えてみる。

 

① nano waka.txt とコマンドを打つ

 

② 上記本文を書き込む 

 

③ 書き込み( ctrl + O ) を打つ。 

 

④ その状態でエンターを打つと、書き込まれる(下の表示に惑わされないこと)。 

 

⑤ 終了( ctrl + X ) を打つ。 

 

そうすると、現在のフォルダの中に、waka.txt というファイルが増え(ls) 

 

中身を見ると、先ほどの編集内容が書き込まれていることが分かる。(cat waka.txt)

 

今度は、vi vimの説明だ。 

 

viは、Unix/Linuxにおいて、その発祥からずっと使われてきたテキストエディタだ。
(Unixの歴史が、80年代後半から90年代前半からなので、
ざっと30年近くの歴史があるとされている。)

(※今調べてみたら、1976年が初版らしい。憶測だけで適当な事を言うものではないとはこのことである。)

そんな背景なので、Unix/LinuxのOSにはほぼ、最初から入っている。
(入っていないOSを見た場合、そのOSの開発者はモグリか、よっぽどのEmacs信者なのだろう。)

 

つまり、nanoや他の使いやすいエディタが最初から入っていればそれでいいが、
それが入っていないぐらい古い環境において、
これが使えなければ、OSのセットアップすらできないという事態に十分なりうる。
(他のテキストエディタを入れたくても、ネット回線に繋ぐための設定ファイルを編集できない。vi が使えなければ、ここで手詰まりだ。) 

 

しかしながら、純粋な vi の使い方は非常に難しい。
この編集者をもってしてさえ、プラグインや環境設定によってカスタムされていない状態の
vi を使いこなせていない状態だというのが正直なところだ。 

 

勿論、自分が使いこなせていないものを教えるわけには行かないので、
その代替として、 ここでは vim の使い方について記述していく。

 

vim とは、viの操作をより発展させ、高度なプラグインによる拡張と、
ユーザー自身が、環境設定を自分でカスタマイズさせていき、
より使いやすい環境を整えていけるというエディタだ。 

 

これはただのメモ帳としてだけでなく、コード補完機能、
自動入力マクロの充実化、シンタクスハイライト(プログラミングの文法で違和感のある部分を光らせてくれる機能のことだ)
といった、統合開発環境(IDE)としても使える。

 

vi特有の馴染みにくい挙動を緩衝してくれるように可能な限りの設定をしている(つもりである)が、
それでもまだ扱いが難しいのは否定できない。

 

これ以降の記事は、それを承知で読んでもらいたい。 

 

vim を起動するには、 

 

vim “作成したいファイル名” 

 

だ。

 

 

vimにおいては、いきなりキーボードを押してテキストを入力できるなどということはない。
vimに、テキストを編集したいという意思を示さなければいけない。
キーボードの a を押せば、テキスト挿入モードに入ることができる。

 

 

すると、左下のファイル名の下に — 挿入 — という表示がされるはずだ。 

この状態は、キーボードからのテキストの編集を受け付ける “挿入モード” である。 

もうお分かりの通り、vimには操作に応じて複数のモードがあるのだ(ややこしい限りだ)。

ここで、適当な文章を打ち込んでみる。

 

 

テキストを書きこんだ状態だ。
しかしながら、このままファイルを簡単に書き込ませてくれるほどvimは易しくない。
まずは挿入モードから出る必要がある。 

 

Escキー(キーボードの一番左上のキーのことだ)を押すと現在のモードから抜けることができる。
すると画面左下にあった — 挿入 — が消える。

 

 

ちなみにEscキーは、挿入モードだけではなく、あらゆるモードから抜け出すことにも使える。
自分がもし何か変なキーを押してしまい、コントロールが効かなくなってしまった場合は、
とりあえずEscキーを連打してみればよい。

 

ファイルの保存を完遂するには、 

 

:w

 

をタイプしてからエンターを押してやればいい。 

 

 

通 常 モ ー ド でのコロンキーは、vimの関数の利用や、ファイルの保存、vim自体の起動や終了な どのコマンドに使うことができると覚えておけばいい。 

(もしここで、テキストの中にコロンが混入するようであれば、まだ挿入モードから抜け切れていないことを意味する。

 コロンキー自体が効かないようであれば、全角で入力しようとしていることが問題の可能性が高い。入力を半角英数に戻すのだ。)

 

そしてvimを終了させるには、通常モードの状態で 

 

:q

 

とタイプしてからエンターだ。 

 

 

すると、いつものコマンドプロンプトに戻り、
memo.txt という名前のファイルと、先ほどの編集内容が書き込まれたファイルが見つかるはずだ。

途中で変なキーを押してしまって、いつもの操作が効かなくなり、自分のモードも把握できなくなった場合、
Escキーを連打してから :w :q と打てば、とりあえず現在の状態を保存しつつvimを終了させられる 。

 

なぜ、わざわざviの代替として、vimを紹介したか?
最初からviを紹介していたら良いのではないのかと言う者が仮に居るとするなら、
viを実際に使ってみてもらいたい。(vi memo.txt だ) 

 

オリジナルのviを薦めなかった理由、その1.
見てみれば一目瞭然だが、さっき日本語だった表示が英語表示となっている。 

 

次にテキストの編集だ。
適当な文字を挿入してみて、いくつか改行してから、矢印キーを打ってもらいたい。
そこでカーソルが上下左右に動くことを期待するだろう。
しかしながら、viはカーソルの移動を矢印キーでは行わないという仕様がある。
なので矢印キーを押す度に、アルファベットの文字列が挿入されるのがはっきりとわかることだろう。
しかも厄介なことに、文字を間違えてバックスペースを押したとしても、文字が削除できないのだ。
文字の削除は、デリートや、通常モードのXキーにて行うことができる。 

 

オリジナルのviを薦めなかった理由、その2.
カーソルの移動や文字の削除すら、メモ帳に慣れたユーザーにとって直感的でない。

 

今はまだ未実装だが、一部プログラミング言語やマークアップ言語(HTML等)の補完、
エラーチェックを行うプラグインが豊富に存在していることが上げられる。
これは、vimが開発環境として、非常に有用な可能性となりうることだと言いたいのだ。 

 

勿論、vimに頼らなくても、インストールするだけで簡単に利用するだけならIDEの方が
手間がかからないというメリットもある。(一人だけで利用するなら、こっちの方が良いかもしれない。)
だが、こちらはリモートで簡単に開発環境をシェアできるというメリットもある。 

 

オリジナルのviを薦めなかった理由、その3.
プラグイン次第では、あらゆるIDEを凌ぐ開発環境となる。 

 

そして、編集者が先ほど言ったように、viはUnix/Linuxにはまず入っているアプリケーションだと述べた。
しかしながら、その半面扱いは難しく、操作にはvim以上の慣れが必要となる。
vimは、元々viの機能を拡張して開発されたものであり、
vimを使っていくうちに、vi本来の機能に習熟することが期待される。
もし希望する職種がサーバエンジニアであるならば、きっと助けになってくれるだろう。

 

オリジナルのviを薦めなかった理由、その4.
vimを使うことによって、viに慣れることができる。 

 

考えられる限り、以上となる。
無論、nanoやvi,vimのみが、Linuxで使えるテキストエディタではない。
emacs という、vimとはまた別の選択肢もあることを記載しておく。
(有用らしいが、自分に扱えるだけの知識がない上に、.vimrcや、dein.vimの資産を失ってしまうのと、
自分が使ってきたOS全てにおいて最初から入っていないのを見たので、
ここでは採用していない。)

 

さて、ここまで読んでくれた物に課題を出そう。 

 

nano の 例題の waka.txt の内容を 

 

①vimにおいて書き込め

 

②viにおいても書き込んで、vimとの挙動の違いを確かめよ。
(方向キーは、↑:k , ↓:j , ←:h , →:l だ
文字を消すには、バックスペースではなく、デリートで消える。
ファイル名は何でもいいが、(①)とは違う名前で保存せよ。)

 

③(①、②)で書き込んだ文章が、同じものであるかどうかを diff コマンドにおいて検証せよ。
(diff コマンドは、ファイルの中身の違いを確かめることができる。

diff 比較対象① 比較対象② 

これで使える。
ファイルの中身に食い違いがあった場合、
そこが何行目で、どういう風に違っているのかを
ピックアップして伝えてくれるというものだ。
もし、完全一致していれば何も表示されない。
ここまでの流れをよく理解しているのなら、ここで何も表示されない優秀な者もいるだろう。
だが、 diff コマンドがどういった形でユーザーに通知するのかを一度見ておいたほうがいいかもしれないので、
何も表示されなかった者は、わざと文章を間違えて打ってから再度diffをかけてみる課題を追加する。) 

 

④今までの課題で自分が操作を行ったファイルを、全て削除せよ。
後にログインして、同じ課題に挑む者のために、操作したテキストを削除し、環境を元に戻すためだ。
(ファイルの削除の方法、及びその操作の危険性について、知らない者、覚えていない者は、仮想環境内の別記事にまとめてある。

 必ず、目を通してくれ。

)

利用者スタッフ の rickyだ。

 

今日は、機械学習アルゴリズムの一つサポートベクトルマシーン(以下、SVM)について説明していこうと思う。

 

このアルゴリズムは2つ以上の特徴から、それらがどんな要素に当てはまるか判別するのに長けている。

 

例えば、初代のいわタイプのポケモンはぼうぎょの値は高いが、とくぼうの値が低い
かくとうタイプのポケモンは、こうげきが高いが、とくこうは低い、
エスパータイプのポケモンは、とくこうが高いが、ぼうぎょは低い

 

と、初代ポケモンを経験したなら分かると思うが、その差は非常にピーキーであった(そ んな環境の中でも、どういうわけか編集者はカイリキーを使うのをやめなかった)。

 

今回の記事の内容は、もうお分かりであろう、
ポケモンのステータスの内容から、そのポケモンのタイプを予測することだ。

 

 

In[1] :

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

import seaborn as sns
sns.set()

from matplotlib import rcParams
rcParams['font.family'] = 'IPAGothic'

 

 

In[2] :

pokemon = pd.read_csv("pokemon.csv")
pokemon

 

 

データ分析に必要なラベルを絞り込む。 前回のカラム絞り込みに、更に種族値合計を加えている。

 

 

In[3] :

pokemon = pokemon[['pokedex_number','name','japanese_name','type1', 'type2', \
'hp','attack','defense','sp_attack', 'sp_defense', 'speed', \
'is_legendary','generation',"base_total"]] pokemon

 

 

 

さて、このままデータの表示まで行きたいところだが、 このままでは、最終進化形態を含んでいるため 正確なデータが図れるとは言い難い。

例えば、 かくとうタイプのワンリキー(A:80,SA:35)と カイリキー(A:130,SA:65)は、 綺麗に初代のかくとうタイプの特徴に当てはまっているように見える。 が、その特徴値を相対量ではなく絶対量で取っているので、 他のポケモンも入ってくるということを考えると、 最終進化形態のみを評価に加えた方がよいのだ。

ここでは、CSVの中身の種族値合計が370を足きりとして それを超える初代のポケモンのみを評価に入れている。 (なお、一部中間進化形態のポケモンも入っている)

 

 

In[4] :

# 総種族値が370以上、初代のポケモンを検索

dt = pokemon[(pokemon["base_total"] > 370) & (pokemon['generation'] == 1)]
dt

 

 

ポケモンを分類するために、まずかくとうタイプかそうでないかを見分けるためにラベルを付けたい。 しかし、ポケモンはタイプを二つ併せ持っている場合がある。(CSVの中のtype1とtype2が、それだ。) そこで、type1,type2のどちらかに "fighting" を持っているものに、 新しくlabelというカラムにその真偽を追加する。 np.where(条件,True,False)は、既存のCSVカラムに新しく要素を足すのに持ってこいの機能である。 ちなみに、ここでは 1 をかくとうタイプのポケモン、0をかくとうタイプでないポケモンとしておいている。

 

 

In[5] :

#タイプ1 もしくは タイプ2がかくとうのものを絞り込み、
#新しくlabelという項目を追加する。
dt["label"] = np.where((dt["type1"] == "fighting") | (dt["type2"] == "fighting"),1,0)
dt

 

In[6] :

#labelが1(格闘ポケモン)を検索 fighting = dt[dt["label"] == 1] fighting

 

 

out[6]:

  pokedex_number name japanese_name type1 type2 hp attack defense sp_attack sp_defense speed is_legendary generation label
56 57 Primeape Okorizaruオコリザル fighting NaN 65 105 60 60 70 95 0 1 1
61 62 Poliwrath Nyorobonニョロボン water fighting 90 95 95 70 90 70 0 1 1
66 67 Machoke Gorikyゴーリキー fighting NaN 80 100 70 50 60 45 0 1 1
67 68 Machamp Kairikyカイリキー fighting NaN 90 130 80 65 85 55 0 1 1
105 106 Hitmonlee Sawamularサワムラー fighting NaN 50 120 53 35 110 87 0 1 1
106 107 Hitmonchan Ebiwalarエビワラー fighting NaN 50 105 79 35 110 76 0 1 1

 

かくとうポケモンでないポケモンを比較対象としてかくとうポケモンの数と同数サンプリングして加える。

 

 

In[7] :

#格闘タイプ以外のポケモンから、labelが0のものを検索し、
#fightingの個数分だけサンプリングする。
other = dt[dt["label"] == 0].sample(fighting.name.count())
other

 

Out[7]:

  pokedex_number name japanese_name type1 type2 hp attack defense sp_attack sp_defense speed is_legendary generation base_total label
52 53 Persian Persianペルシアン normal dark 65 60 60 75 65 115 0 1 440 0
37 38 Ninetales Kyukonキュウコン fire ice 73 67 75 81 100 109 0 1 505 0
21 22 Fearow Onidrillオニドリル normal flying 65 90 65 61 61 100 0 1 442 0
60 61 Poliwhirl Nyorozoニョロゾ water NaN 65 65 65 50 50 90 0 1 385 0
25 26 Raichu Raichuライチュウ electric electric 60 85 50 95 85 110 0 1 485 0
72 73 Tentacruel Dokukurageドククラゲ water poison 80 70 65 80 120 100 0 1 515

 

In[8] :

#格闘タイプのポケモンのデータと、そうでないデータを結合する。
train_xy = np.vstack([ np.array(fighting.loc[:,["attack","sp_attack","label"]]), np.array(other.loc[:,["attack","sp_attack","label"]]) ])
train_xy

 

out[8]: array([[105, 60, 1], [ 95, 70, 1], [100, 50, 1], [130, 65, 1], [120, 35, 1], [105, 35, 1], [155, 70, 0], [ 75, 100, 0], [ 80, 135, 0], [134, 100, 0], [ 50, 115, 0], [100, 25, 0]])

 

In[9] :

fig, ax = plt.subplots()
ax.scatter(
x = train_xy[:,0], y = train_xy[:,1], c = ["maroon" if (i == 1) else "mediumaquamarine" for i in train_xy[:,2]], )
ax.set( title = "かくとうポケモンとその他ポケモンの、こうげき と とくこうの比較", xlabel = "こうげき", ylabel = "とくこう" )
ax.axis = "tight"

 

 

上記の図を見てみよう。 茶色の点がかくとうポケモンで、水色の点がそれ以外のポケモンである。 x軸こうげき、y軸とくこうで取っているので、 図の右下に寄っていればいるほど、それはかくとうタイプである可能性が高くなる。 これなら、うまく線形分離できるだろう。 (本当はいけないことなのだろうが、 これでも、望みのサンプリングデータが来るまで何度か繰り返したのだ。)

今回使うアルゴリズムはscikit-learnのsvmクラスを使う。

In[10] :

from sklearn import svm

 

 

In[11] :

model = svm.SVC(C = 1.0, kernel = "linear")

 

 

(追記予定)

 

 

In[12] :

model.fit(train_xy[:,:2],train_xy[:,2])

 

out[12]: SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0, decision_function_shape='ovr', degree=3, gamma='auto_deprecated', kernel='linear', max_iter=-1, probability=False, random_state=None, shrinking=True, tol=0.001, verbose=False)

 

学習したモデルの評価

In[13] :

#データを変えるために、その他のデータセットをリサンプリングする。
other = dt[dt["label"] == 0].sample(fighting.name.count())
test = pd.concat([fighting,other]) test

out[13]:

  pokedex_number name japanese_name type1 type2 hp attack defense sp_attack sp_defense speed is_legendary generation base_total label
56 57 Primeape Okorizaruオコリザル fighting NaN 65 105 60 60 70 95 0 1 455 1
61 62 Poliwrath Nyorobonニョロボン water fighting 90 95 95 70 90 70 0 1 510 1
66 67 Machoke Gorikyゴーリキー fighting NaN 80 100 70 50 60 45 0 1 405 1
67 68 Machamp Kairikyカイリキー fighting NaN 90 130 80 65 85 55 0 1 505 1
105 106 Hitmonlee Sawamularサワムラー fighting NaN 50 120 53 35 110 87 0 1 455 1
106 107 Hitmonchan Ebiwalarエビワラー fighting NaN 50 105 79 35 110 76 0 1 455 1
11 12 Butterfree Butterfreeバタフリー bug flying 60 45 50 90 80 70 0 1 395 0
104 105 Marowak Garagaraガラガラ ground fire 60 80 110 50 80 45 0 1 425 0
118 119 Seaking Azumaoアズマオウ water NaN 80 92 65 65 80 68 0 1 450 0
142 143 Snorlax Kabigonカビゴン normal NaN 160 110 65 65 110 30 0 1 540 0
148 149 Dragonite Kairyuカイリュー dragon flying 91 134 95 100 100 80 0 1 600 0
70 71 Victreebel Utsubotウツボット grass poison 80 105 65 100 70 70 0 1 490 0

In[14] :

test_xy = np.array([test["attack"],test["sp_attack"],test["label"]]).T
test_xy

out[14]: array([[105, 60, 1], [ 95, 70, 1], [100, 50, 1], [130, 65, 1], [120, 35, 1], [105, 35, 1], [ 45, 90, 0], [ 80, 50, 0], [ 92, 65, 0], [110, 65, 0], [134, 100, 0], [105, 100, 0]])

立てたmodelに対して、テストデータが一致するかどうか?

In[15] :

pred_y = model.predict(test_xy[:,:2])
pred_y

out[15]: array([1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0])

out[15]の出力が、予測データの結果だ。 1がかくとうで、0がかくとう以外のポケモンということを示す。 これを使ってデータがあってるかどうかをテストデータのカラムに入れてやる。

In[16] :

#予測データの成否を、結果に追加する。
test["結果"] = [(a == b) for a,b in zip(pred_y,test_xy[:,2])]
test[["japanese_name","attack","sp_attack","label","結果"]]

結果のカラムのTrueが正解で、Falseがはずれである。 上の結果から、ニョロボン以外のすべての分類に成功していることがわかる。 さて、分類アルゴリズムは、このデータをどう考えたのだろうか? それを分かりやすくするのに、図に起こしてみたい。

out[16]:

  japanese_name attack sp_attack label 結果
56 Okorizaruオ コリザル 105 60 1 True
61 Nyorobonニョロボン 95 70 1 False
66 Gorikyゴーリキー 100 50 1 True
67 Kairikyカイリキー 130 65 1 True
105 Sawamularサワムラー 120 35 1 True
106 Ebiwalarエビワラー 105 35 1 True
11 Butterfreeバタフリー 45 90 0 True
104 Garagaraガラガラ 80 50 0 True
118 Azumaoアズマオウ 92 65 0 True
142 Kabigonカビゴン 110 65 0 True
148 Kairyuカイリュー 134 100 0 True
70 Utsubotウツボット 105 100 0 True

In[17] :

#テストデータのこうげきととくこうの最大値から、meshgridを得る
x_range = np.linspace(0,test_xy[:,0].max() * 1.1)
y_range = np.linspace(0,test_xy[:,1].max() * 1.1)
Y,X = np.meshgrid(y_range,x_range)
Y,X

In[18] :

mesh = np.vstack([X.ravel(),Y.ravel()]).T
mesh

out[18]: array([[ 0. , 0. ], [ 0. , 2.46938776], [ 0. , 4.93877551], ..., [143. , 116.06122449], [143. , 118.53061224], [143. , 121. ]])

In[19] :

#modelから、サポートベクトルの範囲を得る
P = model.decision_function(mesh).reshape(X.shape)
P

out[19]: array([[-10.98151827, -11.04736861, -11.11321895, ..., -14.07648425, -14.14233459, -14.20818493], [-10.59295113, -10.65880147, -10.72465181, ..., -13.68791711, -13.75376745, -13.81961779], [-10.20438399, -10.27023433, -10.33608467, ..., -13.29934997, -13.36520032, -13.43105066], ..., [ 7.2811373 , 7.21528696, 7.14943662, ..., 4.18617131, 4.12032097, 4.05447063], [ 7.66970444, 7.60385409, 7.53800375, ..., 4.57473845, 4.50888811, 4.44303777], [ 8.05827157, 7.99242123, 7.92657089, ..., 4.96330559, 4.89745525, 4.83160491]])

In[20] :

fig, ax = plt.subplots()
ax.scatter(
x = test_xy[:,0],y = test_xy[:,1],
c = ["maroon" if (i == 1) else "mediumaquamarine" for i in test_xy[:,2]] )
ax.contour( X,Y,P,colors=["gray","gray","gray"], levels = [-1,0,1], linestyles = ["--","-","--"] )
ax.set( title = "かくとうポケモンとその他ポケモンの、こうげき と とくこうの比較 + svm予測値", xlabel = "こうげき", ylabel = "とくこう", xlim = (test_xy[:,0].min() * 0.9,test_xy[:,0].max()), ylim = (test_xy[:,1].min() * 0.9,test_xy[:,1].max()) )
fig.axis = "tight"

 

 

 

上の図が、まさにSVMがどう動いているかの種明かしだ。 50-90までの間に三本の直線と点線があるだろう。 その直線が、こうげきととくこうのステータスからタイプを分離するための基本の直線だ。 しかし、かくとうタイプの全てがこうげきが高く、とくこうが低いとは限らない。(実際 にニョロボンのデータは誤分類していた。) また、こうげきが高く、とくこうが低いポケモンだからといって必ずしもかくとうタイプとは限らない。(例をあげていてはきりがない。)

 

 

そこで、そのデータの誤差や外れ値をある程度許容するために、SVMはペナルティライン を引く。 それが直線の両端に位置する、二本の点線というわけだ。

 

 

そこでこのモデルの妥当性を検証するべく、2-4世代のデータも加えて 再度検証してみたい。(後半へ続く)

利用者スタッフのrickyだ。 

 

今回は、人工知能や機械学習という分野をよく知ってもらうべく、
例によってポケモンのステータスを使って、その一部を公開していこうと思う。 

 

まず、初めに断っておくが、これはpython及びjupyter-notebookの経験者には、

見て一発で理解できるように書いている(つもり)だが、

重要なステップについての説明をところどころはしょっているのは否定できない。

他の利用者やまったくの未経験者が、これを読んでいて理解できるということを、考えて書いていない(今のところは)。

 

無論、最終的には、ある程度の習熟している利用者のさらなる理解につながることを最終的な目標としている。

 

が、編集者の時間も有限である。

pythonの基本の更に基本までを事細やかに書いていたら、流石にキリがない。(本当は時間があれば書きたかったのだ)

 

すまないが、今のところは、ある程度は自分で学習してもらいたい。

 

ささやかながら、事業所内の本棚に、何冊か使い古しの本を置いている。(本記事に興味を持ってくれた。利用者へのせめてものはなむけだ。)

 

In[1] :

import pandas as pd
import numpy as np
dt = pd.read_csv("pokemon.csv")
dt = dt[['pokedex_number','name','japanese_name','type1', 'type2', \
'hp','attack','defense','sp_attack', 'sp_defense', 'speed', \
'is_legendary','generation'
]]
dt

 

 

Out[1] :

 

 

 

今回は、図や表を用いて説明していきたいと思う。
そのために、matplotlibとseaborn(グラフを表示するためのライブラリ)を用いる。

 

In[2] :

import matplotlib.pyplot as plt
%matplotlib inline

import seaborn as sns
sns.set()

 

matplotlibはデフォルトで日本語を読み込まないので、
デフォルトでIPAGothicを設定する。 

 

In[3] :

from matplotlib import rcParams
rcParams['font.family'] = 'IPAGothic'

 

例題に入る前に、リザードンのデータを抽出してみようと思う。
抽出の条件はOut[1]からindexの3:5を取る。

 

In[4] :

charmander = dt.loc[3:5]
charmander

 

Out[4] :

  pokedex_number name japanese_name type1 type2 hp attack defense sp_attack sp_defense speed is_legendary generation
3 4 Charmander Hitokageヒトカゲ fire NaN 39 52 43 60 50 65 0 1
4 5 Charmeleon Lizardoリザード fire NaN 58 64 58 80 65 80 0 1
5 6 Charizard Lizardonリザードン fire flying 78 104 78 159 115 100 0 1

 

ここで問題点が明らかになっただろう。
例によって例のごとく、リザードンの列がどう見てもリザードンのものではないのだ。(リザードンYのものとなっている。)

 

ここでのミッションはヒトカゲ、リザード、リザードンYのステータスから、
リザードンのとくこうを予想せよという問いだ(いかにも人工知能、機械学習らしい響きだろう)。

 

In[5] :

charmander_SA = charmander["sp_attack"]
charmander_SA

 

Out[5] :

3 60
4 80
5 159
Name: sp_attack, dtype: int64

 

今の状態で、ヒトカゲ、リザード、リザードンYの順番で配列に並んでいるので、
本来はリザードンのステータスが入る部分に欠損値を入れてやる。 

 

In[6] :

charmander_SA = np.array([charmander_SA.loc[3],charmander_SA.loc[4],np.nan,charmander_SA.loc[5]])
charmander_SA

 

Out[6] :

array([ 60., 80., nan, 159.])

 

分かりやすく、上記を図表へとまとめる。 

 

In[7] :

fig, ax = plt.subplots()

ax.scatter(x = ["ヒトカゲ","リザード","リザードン","メガリザードンY"],
y = charmander_SA)


ax.set(
title = "ヒトカゲからメガリザードンYのとくこうの遷移",
ylabel = "とくこうの種族値",
ylim = (0,200)
)

 

Out[7] :

 

 

 

数列の状態だとピンとこなかった人もいるかもしれないが、
リザードンのとくこうのステータスは 

 

 

おそらくこのへんだろうと予想できるだろう人が大多数を占めるに違いない。(実際には既に知っている人もいるだろうと思う。)

そう、これを見た閲覧者のとった思考こそが、まさに線形回帰そのものなのである。 

 

それでは実際に、線形回帰アルゴリズムを使ってみよう。

この過程には、scikit-learnという機械学習ライブラリを使う。 

 

In[8] :

from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression

 

 

なお、これからためすアルゴリズムは線形回帰というものだが、
その形が必ず一直線(1次式)であるとは限らない。
二次式や、それ以上の次数を取るものも、線形回帰のうちに入るのだ。(教科書にそう書いてあった)

ゆえに、線形回帰(1次式)を多次式へと応用するための関数を作成する。

 

In[9] :

def PolynomialRegression(degree = 2,**kwargs):
    return make_pipeline(PolynomialFeatures(degree),LinearRegression(**kwargs))

 

 

(追記予定) 

 

In[10] :

model = PolynomialRegression()
model.fit(np.array([0,10,30])[:,np.newaxis],charmander_SA[~np.isnan(charmander_SA)])

 

Out[10] :

Pipeline(memory=None,
steps=[('polynomialfeatures', PolynomialFeatures(degree=2, include_bias=True, interaction_only=False)), ('linearregression', LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None,
normalize=False))])

 

なお、今まで x軸 をポケモン名で置いてきたが、
これは、一連の数列に対する関数の結果である。
つまり、連続した値でなければ意味がない。

ここでは、ヒトカゲからリザードンYまでの値が linear であると仮定して、
そのxの値を 0 ~ 30までの10刻みとしておく。
(ヒトカゲ : 0, リザード : 10,リザードン : 20, リザードンY : 30 だ) 

 

In[11] :

x_fit = np.linspace(0,30,30)

y_fit = model.predict(x_fit[:,np.newaxis])

y_fit

 

Out[11] :

array([ 60. , 61.46611177, 63.07134364, 64.8156956 ,
66.69916766, 68.72175981, 70.88347206, 73.1843044 ,
75.62425684, 78.20332937, 80.921522 , 83.77883472,
86.77526754, 89.91082045, 93.18549346, 96.59928656,
100.15219976, 103.84423306, 107.67538644, 111.64565993,
115.75505351, 120.00356718, 124.39120095, 128.91795482,
133.58382878, 138.38882283, 143.33293698, 148.41617122,
153.63852556, 159. ])

 

 

立てた線形回帰モデルにリザードンの値を当てはめて、四捨五入した値が、
scikit-learnが出したリザードンのとくこうの予測値だ。 

 

In[12] :

result = int(model.predict([[20]]).round())

"線形回帰アルゴリズムによって予測された、リザードンのとくこうは %s です" % result

 

Out[12] :

'線形回帰アルゴリズムによって予測された、リザードンのとくこうは 113 です'

 

In[13] :

fig, ax = plt.subplots()

ax.scatter(x = np.array([0,10,20,30]),y = charmander_SA)
ax.plot(x_fit,y_fit,color='tab:blue')

ax.set_xticks(np.arange(0,30 + 1, 10))

ax.annotate(
"予測された値 : %d" % result,
xy = (20,result),
xycoords = "data",
xytext = (-30,-30),
textcoords = "offset points",
bbox = dict(
boxstyle = "round4,pad=.5",
fc = "0.9",
ec = "black"
),
arrowprops = dict(
arrowstyle="->",
connectionstyle="angle,angleA=0,angleB=80,rad=20",
color = "black"
)
)

ax.set( title = "ヒトカゲからメガリザードンYのとくこうの遷移 + 予測データ",
ylabel = "とくこうの種族値",
ylim = (0,200)
)

 

 

Out[13] :

 

 

 

 

上の図をよく見てほしい。
Out[7]の図を見たときに、閲覧者の脳裏にひらめいたグラフと一致してないだろうか?

勿論、これは予測値であるので、実際のリザードンのステータスとは多少誤差がある。 

 

In[14] :

"リザードンのとくこうのステータス 予測値 : %d と 実数値 %d には %d の誤差があります。" % (result,109,result - 109)

 

Out[14] :

'リザードンのとくこうのステータス 予測値 : 113 と 実数値 109 には 4 の誤差があります。'

 

 

いかがだっただろうか?

人工知能や機械学習などと、やれ大層な名前がまかり通っているが、
その種明かしをしてみると、

今あるデータで
どのアルゴリズムを用いて
未知の数を 導き出すか?

という手順を踏んでいるにすぎない。 

 

そして、それはモノと環境さえあれば、今や素人でも簡単に使えるものとなっているのだ。

 

できればもっと追記したい。

(2019-11-26 ガブリアスのげきりんの威力が間違えていることに気が付いたので、一部修正)

 

利用者スタッフのrickyだ。
ネットショップ運営の業務の中で、前からCSVデータの操作法については、要望の声が多かったように思う。

 

CSVファイルは、一応Excelでも読み込むことが出来るし、
基本的な操作なら、それで事足りるかもしれない。 

 

が、かれこれExcelというものはいちいちおせっかいなものだ。
読み込むのに、ヘッダーを選択する必要がある。
望まない数列を小数点に置き換えて、元に戻さない。
Janコードや郵便番号といった膨大な数を指数表記にしてしまう。
そもそもエンコーディングが一致していなくて、読み込めない。(読み込めても、文字化けしている)

 

イライラポイントを挙げていれば、枚挙にいとまがないのだ。(若干自分も、書いていていらついてきている。) 

 

そこで、別の操作方法として、python3とpandasとJupyter-notebookを用いて、CSVを読み込み、操作する方法までを紹介していきたい。

 

jupyter-notebookは、事業所内の仮想環境の中に、誰でもアクセス可能な形で置いている。
不明な点があれば、問い合わせてくれ。

 

さて、いきなり無味乾燥なネットショップの商材データに利用者を向い合せるのも些か酷な話でもあるので、
この記事に興味を持つであろう世代に最も親しみやすいと思われる例として
ポケモンのCSVデータの解析、及びダメージ計算、
これを基として説明していきたいと思う。 

 

ポケモンのCSVデータは kaggle という
機械学習、及びデータ分析のためのサイトが
配布しているものを用意した。 

 

https://www.kaggle.com/
(※要アカウント登録) 

 

 

上記のトピックスからダウンロード。 

 

CSVファイル自体は仮想環境内に、誰でもアクセス可能な状態で置いてある。
各々がダウンロードする必要はない。 

 

このCSVデータを用いて、自分がどういう操作をしたのか紹介していこう。

 

入力は In:[n] 、それに対する出力を Out:[n]として記述している。
詳しくは、Jupyter-Notebookの /pokemon/pokemon.ipynb に入っているのを見てほしい。(恐らくは、そっちの方が分かりやすいはずだ。) 

 

 

In[1] :

import pandas as pd
#行列、表を扱うためのプログラムの集合
#読み込み時に、pdという名前を付けている。

pandas とは、CSVをデータフレームという形に変更して、pythonの中で扱いやすくするモノである。(こういったものを、ライブラリやモジュールと呼ぶ。) これらの基本的な使い方は、仮想環境内の過去記事に置いてある。

 

 

In[2] :

dt = pd.read_csv("pokemon.csv")
#pandasの中のcsvを読み込むための関数を使用。
#括弧の中の文字は、読み込むcsvの名前を入れる。

 

 

Out[2] :

 

 

CSVは、一行目がヘッダーであることが多い。
Out[2] : の一番上の行、そのポケモンの個性を説明するためのセルが、この場合のヘッダーに当たる。
しかしながら、Jupyter-notebookでは、pandasのデータの表示は折りたたまれており、
非常に見にくいのだ。(一応、全部の列を表示する設定もあるが、スクロールバーが追加されて重くなり、 余計に不便になるだけなのだ。)
ここでCSVがどんな項目を持っているのかを見てやる。

 

In[3] :

 

dt.columns
#CSVがどんなヘッダーがあるかを調べる

 

Out[3] :

Index(['abilities', 'against_bug', 'against_dark', 'against_dragon',
'against_electric', 'against_fairy', 'against_fight', 'against_fire',
'against_flying', 'against_ghost', 'against_grass', 'against_ground',
'against_ice', 'against_normal', 'against_poison', 'against_psychic',
'against_rock', 'against_steel', 'against_water', 'attack',
'base_egg_steps', 'base_happiness', 'base_total', 'capture_rate',
'classfication', 'defense', 'experience_growth', 'height_m', 'hp',
'japanese_name', 'name', 'percentage_male', 'pokedex_number',
'sp_attack', 'sp_defense', 'speed', 'type1', 'type2', 'weight_kg',
'generation', 'is_legendary'],
dtype='object')

 

Out[3] を見るに、これだと表が見切れてしまうので、
ダメージの計算に必要な本質的なカラム、もしくはこれから
必要になるカラムを選定していこうと思う。
pokedex_number,name,japanese_name は、それぞれ図鑑番号,英名,和名である(ポケモンの識別に必要)
type1,type2 は、そのポケモンのタイプである。(ダメージの計算に必要)
hp,attack,defense,sp_attack,sp_defense,speed はそれぞれH,A,D,SA,SD,Sである。(ステータス計算に必要)
against_*** は、そのタイプの攻撃を食らった場合、ダメージにどれだけの補正がかかるのかを示す。(ダメージ計算に必要そうに見えるが、こんなものをフレームに含んでいるときりがないので除外。)
is_legendary は、そのポケモンが伝説のポケモンであるかどうかを示す。(今回は必要ないが、この先使うかもしれないので加入)
generation は、そのポケモンが何世代目のポケモンであるかを示す(今回は必要ないが、この先使うかもしれないので加入)
今回は以上のカラムを、データ表示に使っている。

 

In[4] :

dt = dt[['pokedex_number','name','japanese_name','type1', 'type2', \
'hp','attack','defense','sp_attack', 'sp_defense', 'speed', \
'is_legendary','generation'
]]

dt
#csvのカラムの中から、必要な情報だけを取り出す。
# 図鑑番号、英名、和名、タイプ1、タイプ2、
#HP,こうげき、ぼうぎょ、とくこう、とくぼう、すばやさ、伝説ポケモンかどうか、バージョン

 

Out[4] :

 

 

 

さて、ここで一問、例題を挙げてみたいと思う。

 

(問1) レベル50(全個体値0,全努力値0,性格補正なし)のピカチュウが、 レベル50(全個体値0,全努力値0,性格補正なし) のニャースに10万ボルト(威力 : 90, タイプ : でんき)を使った場合、 与えられると予想されるダメージと、ニャースの残りHPを計算せよ。

 

ピカチュウが、ニャースに10万ボルトを放って、ロケット団の2人ごと吹っ飛ばされていく有様は、 昔のポケモンアニメでよく見られた光景だった。 では、この現象を実際に計算したら、どうなのだろうか?

 

In[5] :

#csvの中から、ピカチュウを探す。

pikachu = dt[ dt["japanese_name"].str.contains("ピカチュウ") ]
#テーブルの中の、和名のカラムから、"ピカチュウ"という文字列を含むインデックスを検索する。
#カラム.str.contains(…) とは、指定のカラムの中から、中の文字列を含むインデックスを返す関数である。


pikachu

 

Out[5] :

 

  pokedex_number name japanese_name type1 type2 hp attack defense sp_attack sp_defense speed is_legendary generation
24 25 Pikachu Pikachuピカチュウ electric NaN 35 55 40 50 50 90 0 1

 

 

In[6] :

#ピカチュウのとくこう("sp_attack")を表示。
pika_sp_attack = int(pikachu["sp_attack"])

ピカチュウのとくこうの種族値は %s です" % pika_sp_attack

 

Out[6] :

'ピカチュウのとくこうの種族値は 50 です'

 

In[7] :

#csvの中から、ニャースを検索する。

nyasu = dt[dt["japanese_name"].str.contains("ニャース")]

#テーブルの中の、和名のカラムから、"ニャース"という文字列を含むインデックスを検索する。
nyasu

 

Out[7] :

 

  pokedex_number name japanese_name type1 type2 hp attack defense sp_attack sp_defense speed is_legendary generation
51 52 Meowth Nyarthニャース normal dark 40 35 35 50 40 90 0 1

 

 

In[8] :

nyasu_hp , nyasu_sp_defense = int(nyasu["hp"]), int(nyasu["sp_defense"])
"ニャースのHPの種族値は %s , とくぼうの種族値は %s です。" % (nyasu_hp,nyasu_sp_defense)

 

Out[8] :

'ニャースのHPの種族値は 40 , とくぼうの種族値は 40 です。'

 

In[9] :

#さてここで、レベル50,全個体値0, 全努力値0、性格補正 1 のピカチュウが
#レベル50,全個体値0, 全努力値0, 性格補正 1 のニャースに、10万ボルト(威力 : 90, でんき)を打った場合の
#ダメージの実数値を求めたいと思う。
#ポケモンの種族値からステータス実数値を求める式は
#HP の場合 : (種族値×2+個体値+努力値÷4)×レベル÷100+レベル+10
#それ以外の能力 : {(種族値×2+個体値+努力値÷4)×レベル÷100+5}×せいかく補正
#で求められる。

 

In[10] :

#ピカチュウのとくこうの実数値を求める。
level = 50 #レベル は 50
individual_val = 0 #個体値 は 0
effort_val = 0 #努力値 は 0
nature = 1 #性格補正 は 1

SA = int(pika_sp_attack * 2) #種族値に2をかけて切り捨て
SA = int(SA + individual_val + int(effort_val / 4)) #個体値と努力値を4で割った結果を足し
SA = int(SA * level / 100) + 5 #レベルでかけてから100で割った値に5を足し
SA = int(SA * nature) #性格の補正値を掛ける

"ピカチュウ( LV50, 全個体値 0 , 全努力値 0, 性格補正 1 )のとくこうの実数値は %s です" % SA

 

Out[10] :

'ピカチュウ( LV50, 全個体値 0 , 全努力値 0, 性格補正 1 )のとくこうの実数値は 55 です'

 

In[11] :

#哀れにも攻撃を受けることになるロケット団のニャースのHPと、とくぼうの実数値を求める。
level = 50
individual_val = 0
effort_val = 0
nature = 1
HP = int(int(int(nyasu_hp * 2) + individual_val + int(effort_val / 4) ) * level / 100 ) + level + 10
SD = int(int(int(nyasu_sp_defense * 2) + individual_val + int(effort_val / 4) ) * level / 100 + 5 ) * nature
"ニャース( LV50, 個体値 0 , 努力値 0 )のHPの実数値は %s 、とくぼうの実数値は、%s です。" % (HP,SD)

 

Out[11] :

'ニャース( LV50, 個体値 0 , 努力値 0 )のHPの実数値は 100 、とくぼうの実数値は、45 です。'

 

In[12] :

#ダメージの計算は、
#ダメージ = int(int(int(攻撃者のレベル×2/5+2)×威力×A/D)/50+2)×各種ダメージ補正
#で求められる

level = 50
damage = int(level * 2 / 5 + 2)
skill_power = 90
damage = int( damage * skill_power * SA / SD )
damage = damage / 50 + 2
damage = int(damage * 1.5)
"ピカチュウの10万ボルトによる、ニャースへのダメージは %s です。(残りHP %s )" % (damage, HP - damage)

 

Out[12] :

'ピカチュウの10万ボルトによる、ニャースへのダメージは 75 です。(残りHP 25 )'

 

In[13] :

#なお、実際のポケモンのダメージには、ダメージの計算に 0.85 ~ 1 までの乱数がかかっており、
#実際には、上のダメージ計算結果を下回ることの方が多い。


"ピカチュウの10万ボルトによる、ニャースへの取りうるダメージの範囲は %s ~ %s です。" % (int(damage * 0.85),damage)

 

Out[13] :

'ピカチュウの10万ボルトによる、ニャースへの取りうるダメージの範囲は 63 ~ 75 です。'

 

以上が、計算の答えだ。 いかにも一撃でやられてそうに見えるが、もしピカチュウがニャースと同等の力を持っていた場合 ピカチュウの10万ボルトをもってさえも確定一発に持ち込めないのである。 ということは、実際には個体値、努力値、あるいはレベル差があるに違いない。

 

ちなみに、上記の種族値 -> 実数値 , 実数値 -> 実際のダメージ量 の計算の手順は、 関数化すると非常に扱いやすくなる。

 

In[14] :

#なお、上記の、計算であるが pythonをはじめとするプログラミング言語は、手順を関数化することにより
#コーディングにかかる手間と記述量を圧倒的に減らすことが出来る。
#その記法は
#def {関数名}(引数1,引数2,…,引数n,):
#____計算1
#____計算2
#____(…)
#____計算n
#____return <計算結果>
#だ。
#ただし、上記のアンダーバーは、 半 角 ス ペ ー ス に置き換えてもらいたい。
#というのも、pythonでは、関数の内容や、スコープを半角スペース4つ記載するのがルールとなっており、
#これを無視すると、基本的に実行すらできないからだ。
#これをあえてアンダーバーにしてある理由は、もはや説明するまでもないだろう。

 

In[15] :

#種族値、レベル、個体値、努力値、性格から、ステータス実数値を求める与式を関数化(HP以外)
def from_base_to_stats(base_stats, level, individual_val, effort_val ,nature = 1):
    result = int(base_stats * 2)
    result = int(result + individual_val + int(effort_val / 4))
    result = int(result * level / 100) + 5
    result = int(result * nature)
    return result

 

In[16] :

#種族値、レベル、個体値、努力値から、HPの実数値を求める与式を関数化
def from_base_hp_to_stats_hp(base_stats, level, individual_val, effort_val):
    result = int(base_stats * 2)
    result = int(result + individual_val + int(effort_val / 4))
    result = int(result * level / 100)
    result = result + level + 10
    return result

 

In[17] :

#攻撃ポケモンのこうげき(とくこう)、攻撃ポケモンのレベル、攻撃されるポケモンのぼうぎょ(とくぼう)、わざの威力、その他の補正から
#ダメージを求める式を関数化

def require_damage(A, D, attacker_level, skill_power, other_effect = 1):
    result = int(attacker_level * 2 / 5 + 2)
    result = int(result * skill_power * A / D)
    result = result / 50 + 2
    result = int(result * other_effect)
    min_value = int(result * 0.85)
    return result,min_value

 

上記のように、手順を関数化しておくと {関数の名前}(引数1,引数2, ... 引数n) で簡単に答えが出せるようになる。

 

では、もう少し実践的な1問を追加してみよう。 (問2) レベル50(全個体値31,こうげき努力値252,いじっぱり)のガブリアスが、 レベル50(全個体値31,HPの努力値0,ぼうぎょ努力値MAX(252),きまぐれのスイクンに1ガブリアスのげきりん(威力:100,タイプ:ドラゴン)を使った場合、 与えられると予想されるダメージと、スイクンの残りHPを計算せよ。

 

In[18] :

#それでは、
#レベル50,個体値6V(31),こうげきの努力値MAX(252),いじっぱり(性格補正 : 1.1)の、ガブリアスのげきりん(威力:120,タイプ:ドラゴン)
#を
#レベル50,個体値6V(31),HPの努力値0,ぼうぎょ努力値MAX(252),きまぐれ(性格補正 : 1 ) の スイクン
#が受ける場合のダメージと、残りHPを求めるにはどうすればいいか?

 

In[19] :

#csvのデータテーブルから、ガブリアスを検索したはいいが、
#どう見ても、通常のガブリアスの能力値ではなく、メガガブリアスの能力値となっている。
#このように、kaggleで配布されたデータだからといって、必ずしも間違いがないとは限らない。
#見てもらえばわかると思うが、一部ポケモンの高さや重さにも欠損値が含まれている。(実はこれが、プロジェクトの狙いかもしれないが)。


dt[dt["japanese_name"].str.contains("ガブリアス")]

 

Out[19] :

 

  pokedex_number name japanese_name type1 type2 hp attack defense sp_attack sp_defense speed is_legendary generation
444 445 Garchomp Gaburiasガブリアス dragon ground 108 170 115 120 95 92 0 4

 

 

In[20] :

#実際のガブリアスのこうげき種族値は、130であることが知られている。

gabu_A = from_base_to_stats(130,50,31,252,1.1)
"ガブリアスのこうげきの実数値は %s です。" % gabu_A

 

Out[20] :

'ガブリアスのこうげきの実数値は 200 です。'

 

In[21] :

suikun = dt[dt["japanese_name"].str.contains("スイクン")]
suikun

 

Out[21] :

 

  pokedex_number name japanese_name type1 type2 hp attack defense sp_attack sp_defense speed is_legendary generation
244 245 Suicune Suicuneスイクン water NaN 100 75 115 90 115 85 1 2

 

 

In[22] :

#スイクンのHPと、ぼうぎょの実数値を求める。

suikun_H,suikun_D = int(suikun["hp"]),int(suikun["defense"])
suikun_H,suikun_D = from_base_hp_to_stats_hp(suikun_H,50,31,0), \
    from_base_to_stats(suikun_D,50,31,252)
"スイクンのHP,ぼうぎょ の 実数値は、それぞれ %s %s です。" % (suikun_H,suikun_D)

 

Out[22] :

'スイクンのHP,ぼうぎょ の 実数値は、それぞれ 175 167 です。'

 

In[23] :

#ステータスとレベル、わざの威力から
#ダメージの実数値を求める。
#ちなみに、げきりんはタイプ一致なので、ピカチュウの10万ボルトと同じく1.5の補正がかかる。


result = require_damage(gabu_A,suikun_D,50,120,other_effect = 1.5)
"スイクンに対する、ガブリアスのげきりんが取りうるダメージの範囲は、%s ~ %s です。(残りHP:%s ~ %s)" % \
    (result[1],result[0],suikun_H - result[1],suikun_H - result[0])

 

Out[23] :

'スイクンに対する、ガブリアスのげきりんが取りうるダメージの範囲は、82 ~ 97 です。(残りHP:93 ~ 78)'

 

以上の通りである。 スイクンはガブリアスの4世代当時最高峰のステータスから放たれる、げきりんを少なくとも1発は耐えることができ、 更に大抵の場合、れいとうビームを持っていたので当時のトップメタとして、挙げられるポケモンの一体だった。

 

(問3) レベル100,6V,せっかちのミュウと、 レベル100,6V,性格補正なし,素早さの努力値無振のミュウツーが戦闘した場合、必ずミュウが先手をとりたい場合、 振るべき努力値の最小値を求めよ。

 

素早さのステータスは、ダメージの計算とは少し勝手が違い、 その実ステータスが1でも高い方が必ず先手を取る(乱数要素が一切ない)。 ポケモンでの素早さは重要なステータスだというが、環境ポケモンが固定されている以上、 同じポケモンのミラー戦になることは非常に多かったのだ。 素早さが1でも多く上回るということは、下回ってしまった方が先にやられる可能性が高いということを意味している。

 

In[24] :

#では、6V性格せっかちのミュウと
#6V,性格補正なし,素早さ努力値無振のミュウツーが戦闘した場合
#ミュウがミュウツーよりも先手を取れるようにするには、どれだけの努力値を振ればよいか?

 

In[25] :

#csvの中から、ミュウを探す
dt[dt["japanese_name"].str.contains("ミュウ")]

#なんと、和名がミュウのテーブルを調べたいのに、
#同時にミュウツーのデータまでついてきてしまった。


# というのも、この{pd.DataFrame}.str.contains という関数は、中の文字列を 含むもの 全てを検索してしまう。


# 検索文字列が "ミュウツー" であれば、その結果は絶対にミュウツーのもののみだと言い切れるが、
# 検索文字列が "ミュウ" であれば、その結果は必ずしもミュウのものだけとは限らない。

 

Out[25] :

 

  pokedex_number name japanese_name type1 type2 hp attack defense sp_attack sp_defense speed is_legendary generation
149 150 Mewtwo Mewtwoミュウツー psychic NaN 106 150 70 194 120 140 1 1
150 151 Mew Mewミュウ psychic NaN 100 100 100 100 100 100 1 1

 

 

In[26] :

#しかし、上記結果から、ミュウの図鑑番号(pokedex_number)が151であるという重要な手がかりは得られた。

mew = dt[dt["pokedex_number"] == 151]
mew_speed_base = int(mew["speed"])
"ミュウのすばやさの種族値は %s です。" % mew_speed_base

 

Out[26] :

'ミュウのすばやさの種族値は 100 です。'

In[27] :

#ミュウツーの素早さであるが、例によってメガシンカした時の素早さが入ってしまっている。
#本来のミュウツーの素早さの種族値はは130であるので、それで計算した


mewtwo_speed_base = 130

"ミュウツーのすばやさの種族値は %s です。" % mewtwo_speed_base

 

Out[27] :

'ミュウツーのすばやさの種族値は 130 です。'

 

In[28] :

#その前に、能力値の計算に、努力値がどこまで影響を及ぼしてるのかのケースを見てみる。
#ミュウツーの努力値無振と、努力値全振を比較した場合どうか?


mewtwo_speed_6V0E = from_base_to_stats(mewtwo_speed_base,100,31,0,1)
mewtwo_speed_6V252E = from_base_to_stats(mewtwo_speed_base,100,31,252,1)

"ミュウツー(6V性格補正なし)の、すばやさの実数値は %s(努力値無振り) %s(努力値MAX) です。" % (mewtwo_speed_6V0E ,mewtwo_speed_6V252E)

 

Out[28] :

'ミュウツー(6V性格補正なし)の、すばやさの実数値は 296(努力値無振り) 359(努力値MAX) です。'

 

In[29] :

#同様の計算をミュウで行った場合はどうか?

mew_speed_6V0E = from_base_to_stats(mew_speed_base,100,31,0,1.1)
mew_speed_6V252E = from_base_to_stats(mew_speed_base,100,31,252,1.1)

"ミュウ(6V性格補正あり)の、すばやさの実数値は %s(努力値無振り) %s(努力値MAX) です。" % (mew_speed_6V0E ,mew_speed_6V252E)

 

Out[29] :

'ミュウ(6V性格補正あり)の、すばやさの実数値は 259(努力値無振り) 328(努力値MAX) です。'

 

In[30] :

i = 0
val = from_base_to_stats(mewtwo_speed_base,100,31,0,1)


while (i < 253):
    result = from_base_to_stats(mew_speed_base,100,31,i,1.1)
    if (val < result):
        break
    else:
        i = i + 1
        continue

"ミュウツーに対して先手を取りたい場合、ミュウに振るべき努力値は %s です" % i

 

Out[30] :

'ミュウツーに対して先手を取りたい場合、ミュウに振るべき努力値は 136 です'

 

ということだ。昔のポケモン対戦ではミュウとミュウツーが戦ったら、その殆どがミュウツーが先手をとっていたことだろう。 しかし、もしミュウに努力値を振った最速のケースを考えてみた場合、 才能も努力も積んだミュウが、才能はあるが努力をしていないミュウツーを上回るといった事態も十分ありえるという結果になった。 もっとも言い方を変えれば、 才能のあるものが更に努力をした場合、 その才能を下回るものがどれだけ努力を積んだところで絶対に及ばないという 皮肉な結果も同時に意味することとなるが。

 

以上、簡潔なポケモンのCSVの操作を自分なりに行ってみたものだ。

 

本記事は事業所の利用者の理解を深めるために所内の仮想環境内で書いたものだ。 正直言って、Amebaのブログにこれを転載するつもりなど、これっぽっちもなかった。

 

それでも、俺がこの記事をAmebaに乗せようと思ったのは、 編集者はポケモンを第1-5世代までやっていて、ある程度対人戦もこなしていたが、 正直、ここまで厳密な数字を自分の手で求めることを、意識したことがない。 つまるところ、上記の自分の計算が合っている保証など、どこにもないのだ。

 

仮に、上記の計算に間違いが無かったとしても、参考資料を見る内に世代によって、 一部計算の順序や倍率が微妙に異なっているなど、まま有りうる。

 

もし、計算ミスや世代間のギャップを指摘してくれそうな"誰か"の目に留まればいいが、 それが無いと、間違った計算が、あたかも本当のことのように知れ渡ってしまうのだ。

 

計算が間違っている?もっといい計算方法がある?

 

至急、もっといい計算式くれや

はじめまして、利用スタッフのひげです

 

ブログを書いてみようかなと思い立ったのが去年の12月で、気づけばこんな時期になっていましたチーン

 

今回は、PlayStation祭というイベントに行ってきたのでその話をひとつ

 

これまで5.6回ほど行われていて(最初の頃は名前も違いました)

今回は西梅田にあるハービスホールで開催されました

 

発売予定のものから発売して間もないものまで、様々なゲームを試遊できるので購入する際の参考にしたり、新しい発見が出来る貴重なイベントです

 

毎年秋ごろに開催される、僕の一年の楽しみのひとつです爆  笑

 

この写真はまだ開場して間もない時間に撮影したものです

試遊エリアが2つあり、こちらは主力タイトルが多く置かれていました

 

こちらはもう一つのスペースで、主にVRタイトルが配置されていました

 

こちらはステージイベント用のスペースです

この日も色んなステージイベントで盛り上がっていましたニコニコ

 

モンスターハンターワールド:アイスボーンから

イヴェルカーナの大型ヘッドフィギュアや

 

同タイトルより、参加者から可愛いと話題の受付嬢さん

(良いアングルで撮れませんでしたゲロー

 

話題の大型タイトル「DEATH STRANDING」の展示

 

お馴染みトロとピポサルまで、来場者を楽しませる要素が盛りだくさんでした

 

僕自身は例年より試遊できたタイトル数は少ないですが、非常に楽しく遊ぶことができたので満足しています照れ

 

特に「PROJECT RESISTANCE」というカプコンの新規タイトルは非常に面白かったので、製品版も買いたいですねぇ

試遊特典のTシャツも貰いましたニコニコ

 

そのTシャツと来場特典のノベルティがこちら

物販スペースも充実していてこのときは買わなかったんですけど、今思えば買うべきだったかなぁと若干後悔していますキョロキョロ

 

 

今年も楽しかったなぁ

来年はPS5関連の展示もあるかもしれないし、気が早いですけど今から開催が待ち遠しいですね~グラサン