{ "cells": [ { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "view-in-github" }, "source": [ "\"Open" ] }, { "cell_type": "markdown", "metadata": { "id": "OCa-xyVjiadQ" }, "source": [ "# ライブラリ/パッケージ/モジュールとデータの可視化(Matplotlib)\n", "\n", "この章の目的:\n", "\n", "* [ライブラリ]という概念の獲得\n", "* Pythonを用いた各種グラフの描画\n", "\n", "\n", "Pythonでは(他のプログラミング言語と同様)特定の作業がパッケージ化されたプログラム群が用意されていてこれらをライブラリ/パッケージ/モジュールなどと呼ぶ。\n", "正確な定義としてはライブラリ/パッケージ/モジュールの順に、より上位の集合を指しますが、\n", "慣例的には同じ意味で使われることが多いです。\n", "この授業でも呼び方が混在しているかと思います。\n", "\n", "Pythonのライブラリ/パッケージの多くはGitHub上で開発・公開されていて、簡単にインストールしたりインポートして使うことができる。 \n", "\n", "データ分析, AI・機械学習, エクセル等の操作, Webスクレイピングなど、非常に多岐にわたるライブラリが存在する。\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ライブラリのインポート(import)\n", "\n", "幾つかのライブラリ/モジュールは標準で組み込まれているため、特段インストール作業をすることなく、以下のように簡単にインポートして使うことができる。\n", "\n", "たとえば`math`という名前のモジュールをインポートする際には\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "id": "hGBW_qTNkvRq" }, "outputs": [], "source": [ "import math" ] }, { "cell_type": "markdown", "metadata": { "id": "It2WtKEtkwNT" }, "source": [ "とする。カンタン。\n", "\n", "この`math`モジュールはPythonの標準ライブラリの一つで、数学関数を提供するモジュールである。\n", " \n", "上のコードを実行し`math`モジュールを一度`import`してやれば、以降のコードで、\n", "- 円周率$\\pi$: `pi`\n", "- 自然対数: `log`\n", "- 常用対数: `log10`\n", "- 指数関数: `exp`\n", "- 三角関数: `sin`,`cos`\n", "\n", "などを使うことができる。\n", "\n", "モジュール内に用意されている定数や関数を利用するときは通常、```モジュール名.定数```や```モジュール名.関数(引数)```といった形で使う。" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "id": "9BbEO3a8k0VH" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3.141592653589793\n", "2.718281828459045\n", "0.6931471805599453\n", "0.3010299956639812\n", "7.38905609893065\n", "1.2246467991473532e-16\n" ] } ], "source": [ "print(math.pi) #円周率pi\n", "print(math.e) #ネイピア数e\n", "print(math.log(2.0)) #自然対数 \n", "print(math.log10(2.0)) #常用(底が10)対数\n", "print(math.exp(2.0)) #指数関数\n", "print(math.sin(math.pi)) # sin(pi)\n" ] }, { "cell_type": "markdown", "metadata": { "id": "cGKN5FijmR8v" }, "source": [ "注:$sin(\\pi)$は本来0だが、1章で述べたように実数が有限の精度なので、$\\pi$の計算機上での数値が厳密には円周率と異なるため、$sin(\\pi)$の値も僅かに0からズレることになる。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ライブラリのインストール\n", "\n", "Google Colaboratory環境では、Pythonの標準ライブラリ以外のライブラリを、`pip`コマンドを用いてインストールすることができる.\n", "\n", "コードセルで以下のようにすれば良い:\n", "```python\n", "!pip install package_name\n", "```\n", "\n", "ただし、Google Colaboratory環境では、よく使われるライブラリは標準でインストールされているので、 \n", "特殊なライブラリや特定のバージョンを使用したいといった状況でなければ、別途インストールする必要はない。 \n", "また、ユーザーが別途インストールしたライブラリは、そのセッションが終了すると消えてしまうので、 \n", "時間を空けて作業を再開する場合などは、毎回インストールする必要がある。\n" ] }, { "cell_type": "markdown", "metadata": { "id": "69y4fMDWSB11" }, "source": [ "## Numpy" ] }, { "cell_type": "markdown", "metadata": { "id": "FIsrOtnTR4qZ" }, "source": [ "データ分析などで非常によく使われるNumpyというライブラリがある。 \n", "Numpyはおもにベクトルや行列の演算などを高速に行うことが可能という利点があり、データ分析においては必須のライブラリと言っても過言ではない。\n", "\n", "\n", "`ndarray`と呼ばれるNumpy独自の配列を用いることで、ベクトルや行列の演算を高速に行うことができたり、グラフの描画に配列を使う際にも便利な機能が用意されている。\n", "簡単な作業ならリストでも代用できるので必須ではないが、コードを大幅に簡略化することもできるため、今後ノートブックでも適宜Numpyが用いられる。\n", "\n", "Numpyの詳細については[こちらのノートブック](https://colab.research.google.com/github/SotaYoshida/Lecture_DataScience/blob/main/notebooks/Python_misc_numpy.ipynb)を参照。\n", "\n", "Numpyはmathとも多くの互換性があるため、上で行った操作はNumpyでも代用できる:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "id": "_RjUF-6603dn" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.6931471805599453\n", "0.3010299956639812\n", "7.38905609893065\n", "3.141592653589793\n", "1.2246467991473532e-16\n" ] } ], "source": [ "import numpy as np #numpyをnpという名前で使う\n", "print(np.log(2.0))\n", "print(np.log10(2.0))\n", "print(np.exp(2.0))\n", "print(np.pi)\n", "print(np.sin(np.pi))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "また、以下でやるように、数値を要素に持つリストを使って演算やグラフを描画するときは、データのリストなどを`ndarray`型に変換して使う事も多い。\n", "\n", "リストを`ndarray`型に変換するには、numpyの`array`関数を用います。" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ 1.23000e+00 9.34122e+03 -3.23300e+01] \n" ] } ], "source": [ "import numpy as np\n", "list_a = [1.23, 9341.22, -32.33] \n", "ndarray_a = np.array(list_a)\n", "print(ndarray_a, type(ndarray_a))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "グラフの描画で便利な理由は以下のような人単位のデータを持つ入れ子のリストがあったときに、成分の転置を取ってx軸方向のデータ, y軸方向のデータに整形が容易となる。" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[180, 70], [175, 65], [160, 58], [170, 60]]\n", "[[180 175 160 170]\n", " [ 70 65 58 60]]\n", "x [180 175 160 170]\n", "y [70 65 58 60]\n" ] } ], "source": [ "#身長体重のデータ\n", "data = [ [180, 70], [175, 65], [160, 58], [170, 60] ]\n", "print(data)\n", "# データをndarrayに変換\n", "data = np.array(data)\n", "\n", "# 転置を行うメソッドを適用することで、0列目が身長、1列目が体重と整形できる\n", "data = data.T\n", "x = data[0]\n", "y = data[1]\n", "\n", "print(data)\n", "print(\"x\", x)\n", "print(\"y\", y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "なお、numpyの`ndarray`を`print`すると上のようにデータを区切るカンマを省略したり、データの数が多いときは省略して表示してくれる。\n", "これは、`print`関数が`ndarray`型のデータを表示する際に、専用の表示方法を用いているためである。" ] }, { "cell_type": "markdown", "metadata": { "id": "XD-dP853zCmO" }, "source": [ "## Matplotlibを用いた作図\n" ] }, { "cell_type": "markdown", "metadata": { "id": "SGAFbBQJnplQ" }, "source": [ "\n", "次に、`Matplotlib`と呼ばれるライブラリを使って、各種のグラフを作成する方法を見ていこう.\n", "\n", "Matplotlibは様々なグラフが描ける一方で少々テクニカルな部分が多いので**細かい部分は分からなくても心配は不要**である. \n", "「こういうおまじないを唱える(書くと)こうなる」というざっくりとした理解でまずはOK.少しずつ、グラフの詳細を調整する方法をマスターしていけば良い.\n", "\n", "Pythonのライブラリの細かな使い方を調べる時、真っ先に思いつくのが**ググる**ことになるかと思う。\n", "最近だと日本語で書かれた情報に気軽にアクセスできるのは良いことだが、不正確な記述が含まれていてかえって理解を遠ざける危険性もある。\n", "なにか困ったときに一番頼りになるのは大元のライブラリの公式ドキュメント以外ありえない。なぜなら開発者が作っているから。\n", "ページ内にあれこれと広告が貼り付けてあるようなブログ記事を見て不正確な情報に振り回されるくらいなら、公式ドキュメントを参照しよう.\n", "\n", "はじめは授業資料にあるコードなどを少しずつ流用し、自分の目的のためにどうすれば良いか類推しながら改良していって、 \n", "自分の描きたいグラフに近づけていくのが良い。(私もよく過去の自分が作成したコードを流用して作図しています。)\n", "\n", "慣れてくるといろんな図を作ったり、細かいところにこだわったりしたくなります。 そんなときは以下のチートシートが役に立ちます. \n", "https://github.com/matplotlib/cheatsheets \n", "\n", "あるいは、最近だとChat GPTに「二次元配列をプロットするPythonコードを書いてください」\n", "「さっきのコードをさらに拡張して、棒グラフにハッチをつけてください」などと聞いてみても良いかも. \n", "もちろん、コードを貼り付けるだけで個々の部品に関する理解を放棄していると、いつまでたっても有効なプログラムを書けるようにはならないので、\n", "その辺りのバランスを取りながら学習を進めよう。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Matplotlibのインポートと使い方\n", "\n", "まず、以下のコードを実行して、`matplotlib`内の`pyplot`モジュールをインポートしよう. \n", "`matplot.pyplot`だと名前が長いので```plt```という名前で使えるように```import XXX as YY``` などとする." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "oevjB6tvA97n" }, "outputs": [], "source": [ "import matplotlib.pyplot as plt \n", "from matplotlib import pyplot as plt #でも同じ" ] }, { "cell_type": "markdown", "metadata": { "id": "-KUcnUBIg2W2" }, "source": [ "いくつかのライブラリにはディレクトリのように\n", "```\n", "LibraryA \n", "├ module1.py \n", "│ └─ sub_module1_1.py \n", "│ └─ sub_module1_2.py \n", "├ module2.py \n", "︙ \n", "```\n", "という階層構造があり、子,孫,...といった下位のものはピリオドで指定する. \n", "上の```matplotlib.pyplot```はその一例になっている." ] }, { "cell_type": "markdown", "metadata": { "id": "Ycksu5i7BsWp" }, "source": [ "次に、注意事項!!\n", "\n", "Matplotlibは**標準だと日本語が文字化けして豆腐のように表示されてしまう**ので、日本語フォントを使うためのライブラリ、\n", "`japanize-matplotlib`ををGoogleのサーバにインストールする. \n", "\n", "この作業はGoogle Colaboratory環境で作業を再開するたびに行う必要があるので、日本語を含むグラフを描画したければコードに含めておくこと. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "Qwb9pJyCAcfC", "outputId": "29634cc6-f768-4511-fd59-4ebc352de8dd" }, "outputs": [], "source": [ "!pip install japanize-matplotlib #!から始めることでLinuxコマンドを使うことができる。\n", "import japanize_matplotlib \n", "import numpy as np " ] }, { "cell_type": "markdown", "metadata": { "id": "QPB383yO9SFq" }, "source": [ "なお、ライブラリ名にハイフンが含まれる場合は、`import`する際にハイフンをアンダースコアに置き換える必要がある. \n", "(ハイフンはPythonの変数名などに使えないため)\n", "\n", "\n", "Pythonでグラフを作る際の基本は、**データ(描画したいもの)が入った配列を作ること**になる.\n", "\n", "以下では、リスト-likeなデータ(例えば`range`や`ndarray`など)を使って、グラフを描画する方法を見ていく.\n", "毎回リスト-likeと書くのは面倒なので、以降はリスト-likeなデータを単にリストと呼ぶことにするが、\n", "必ずしも描画につかうデータはリストに限らず、リストと互換性のあるndarrayなど、一般の配列も使えることに注意.\n", "\n", "プログラムでグラフを描くのは、人間が手で絵やグラフを描く工程とは少し異なる.\n", "線や棒、いろんな形のシンボルを描いたりして、どんどんグラフを構成する要素を足しながら、 \n", "それらの色や線種、透過度や太さ,背景色などを変えつつ徐々に望むようなグラフにしていく作業になる.\n", "場合によっては、どの要素を前面に配置するかといったレイヤーの意識も必要になる.\n", "\n", "以下では代表的なグラフを例に、Matplotlibを使った作画を試してみよう." ] }, { "cell_type": "markdown", "metadata": { "id": "88mhl_c22S4-" }, "source": [ "### 棒グラフ" ] }, { "cell_type": "markdown", "metadata": { "id": "j98rkXdk03-h" }, "source": [ "Aさんの共通テスト?の得点をリストとして定義してみよう\n", "順番(各科目の名前(ラベル))は、国語,英語,数IA,数IIB,化学,物理,世界史として... " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "WWBmSr7L0_6s" }, "outputs": [], "source": [ "data = [152, 170, 82, 85, 79, 92, 88]\n", "label = [\"国語\",\"英語(R+L)\", \"数IA\", \"数IIB\", \"化学\", \"物理\", \"世界史\"]\n", "total = sum(data)\n", "print(\"合計得点は\", total, \"点で、得点率は\"+str(\"%6.1f\" % (100* total/900))+\"%です\")" ] }, { "cell_type": "markdown", "metadata": { "id": "4gaNmr3CefUn" }, "source": [ "Aさん優秀。これを棒グラフにでもしてみよう。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "S0rBmtTL3YsB" }, "outputs": [], "source": [ "plt.figure( figsize=(10,2) ) \n", "plt.bar(label,data,align='center',width=0.5,color=\"red\") \n", "plt.xlabel(\"各科目\"); plt.ylabel(\"Aさんの得点\") \n", "plt.show() \n", "plt.close()" ] }, { "cell_type": "markdown", "metadata": { "id": "4SV4bCn9nxqV" }, "source": [ "\n", "* 1行目: ```plt.figure(figsize=(10,2)) ``` \n", "\n", " > ここでは、`matplotlib.pyplot`内の`figure`という関数を読んでいる。 \n", " 上で`matplotlib`モジュール内の`pyplot`というモジュールを`plt`という名前でインポートしたので`plt.figure`というのは`pyplot`内の`figure`という関数を使うことを意味する。その役割は、図を描くキャンバスを用意しているようなイメージ。 \n", " ここでは```figsize=(10,2)```という引数を指定した(指定しなければ自動で図のサイズが決まる). \n", " figsize=(横,縦)で大きさが指定できるので、figsizeを変えて試してみよう \n", "\n", "* 2行目: ```plt.bar(label,data,align='center',width=0.5,color=\"red\")``` \n", "\n", " >`pyplot`内の`bar`という関数(棒グラフを描く関数)を呼んでいる。1つめの引数はx軸上の値で、2つめはy軸に対応する値 \n", " x軸用のリストが数値以外のときは整数値を割り当ててプロットしてくれる(7個データがあれば、x=0,1,...,6に割り当てられる)。\n", "\n", "* 3行目: ```plt.xlabel(\"各科目\"); plt.ylabel(\"Aさんの得点\") ``` \n", " \n", " > ここではx軸とy軸のデータの種類(ラベル/labelと呼ぶ)を指定している。 \n", "\n", "* 4行目: ```plt.show()``` \n", "\n", " > それまでに指定した条件で絵を描いて表示する\n", "\n", "* 5行目: ```plt.close() ```\n", "\n", " > キャンバスを閉じる。とくに一つのプログラムで複数絵を描くときはこれを書く必要がある。\n", " (closeしないと、どこまでがどのグラフのための指示かわからず意図しない絵になることがあります)" ] }, { "cell_type": "markdown", "metadata": { "id": "QJU3ua-h6JS0" }, "source": [ "\n", "ライブラリを使用する際には、見慣れない関数がたくさん出てくるので、その全てについて今の段階でオプションの指定の仕方を覚える必要はない.\n", "とりあえず、既に指定されているオプションのいくつかを変更してみよう。\n", "\n", "その際、`color`や`width`, `align`などから「widthは数値だから増減するとどうなるかな?」「`align=\"center\"`がいけるならleftとかrightもあるか?」「こう変えるとこうなるんじゃ...?」と類推しながらやってみよう。\n", "\n", "\n", "**練習** \n", "\n", ">いくつかの得点や、棒グラフの色・幅を変えてプロットしてみよう。 \n", "例えば`color`の場合、一般的な色の名前を指定するか、カラーコード(真っ黒=\"#000000\"など)やRGBの値(0~1の値を3つ並べたもの,例->(0.2,0.5,1))を指定することで色を変更できる。 \n", "※日本語が上手く表示されない場合、上の`japanize_matplotlib`をインストールする行の実行を忘れている." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": { "id": "EJpfw2KwCPdV" }, "source": [ "\n", "\n", "---\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "id": "6iN_Tl4millJ" }, "source": [ "### plot: 線の描画\n", "\n", "説明変数$x$と目的変数$y$があって、とくに **$x$に対する$y$の振る舞い** に興味がある場合には```plot```を使う。\n", "\n", "たとえば、日付($x$)に対するコロナウイルス感染者($y$)の推移をプロットしてみよう。\n", "日付は、WHOのSituation Reportが初めて発表された2019年1月20日を起点(ゼロ)とする経過日数として表すことにして...\n", "\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 539 }, "id": "N0LR-xQii7b3", "outputId": "0254ee47-8280-4fec-fb06-81463204cafc" }, "outputs": [], "source": [ "# 感染者数(y)と重傷者数(y_sev) #2019年2月13日までのデータ\n", "y= [282,314,None,581,846,1320,2014,2798,4593,6065,7818,9826,11953,14557,17391,20630,24554,28276,31481,34886, 37558.40554,43103,45171] \n", "y_sev=[51,51,None,96,177,237,324,461,976,1239,1370,1527,1795,2110,2296,2788,3219,3859,4821,6101,6188,6484,7333,8304]\n", "\n", "plt.figure(figsize=(12,4)) \n", "plt.xlabel(\"2019年1月20日以降の経過日数\")\n", "plt.plot(range(len(y)),y, label=\"世界全体での新型コロナウイルス感染者数\",color=\"red\")\n", "#x軸が0から始まる整数値(0,1,...)で良い場合は、x軸のデータを指定しなくてもplotしてくれる\n", "plt.plot(y_sev, label=\"重傷者数\",color=\"blue\") \n", "plt.legend() ##凡例(線や点等の説明)を描画する\n", "plt.show()\n", "plt.close() \n", "\n", "plt.figure(figsize=(12,4)) \n", "plt.xlabel(\"1月20日以降の経過日数\")\n", "plt.yscale(\"log\") ## y軸を対数スケールに変換\n", "plt.plot(range(len(y)), y, marker=\"x\",label=\"世界全体での新型コロナウイルス感染者数\",color=\"red\") ## markerの値を指定すれば、線だけじゃなく点を描くことも出来る。\n", "plt.plot(y_sev, label=\"重傷者数\",marker=\"o\",color=\"blue\")\n", "plt.legend()\n", "plt.show()\n", "plt.close() " ] }, { "cell_type": "markdown", "metadata": { "id": "5Hbzfjw0pqhu" }, "source": [ "下のグラフはy軸の対数スケールを取っている.\n", "データが無い日の値を0とするのは誤りである. ここではNone(値なし)にしている. こうした欠損値の取り扱いもデータ分析では重要な作業の一つである.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**凡例の表示**\n", "\n", "複数のデータをプロットするときは、それぞれのデータに対して凡例を付けることができる.\n", "barやplot、以下でやるscatterなどそれぞれのグラフを構成するオブジェクトに対して、`label`という引数を指定することで凡例を付けることができる.\n", "\n", "つけた凡例は`plt.legend()`で表示することができる." ] }, { "cell_type": "markdown", "metadata": { "id": "AP4eGtKElufB" }, "source": [ "当然だが、x軸とy軸方向でデータの数が合っていないとエラーとなる. \n", "「一緒にしているつもりなのにエラーが出る...」と言う場合は、`len`関数を使うなどして、リストの長さ/要素の数をチェックしてみよう。\n", "カンマがピリオドになっていて数がずれる、といったことがよくある。 \n", "\n", "例: \n", "(意図したリスト) `[2,3,5]` ←長さ3のリスト \n", "(間違えて作ったリスト) `[2.3, 5]` ←カンマがピリオドになっていて、長さ2のリストになっている. \n", "\n", "上記のようなミスを防ぐには、値を入力してカンマを打ったあと半角スペースを開けるようにすると分かりやすい, `[2,3,5]`→`[2, 3, 5]`.\n", "\n", "カンマの後ろにスペースを入れることは文法上の必要条件ではないが、コードの可読性を上げるために推奨される書き方である. \n", "英文などでも基本となるため、癖づけておくと良い." ] }, { "cell_type": "markdown", "metadata": { "id": "EiGt8E4VoWEI" }, "source": [ "**練習**: \n", "「matplotlib marker」でWeb検索してみて、どんな形状のmarkerが使えるか調べてみよう. \n", "また、その中から\"x\"(バツ印)以外の好きなシンボルを選んで上のグラフの2番目などをプロットしてみよう。\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Oy34IWVsCLZv" }, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": { "id": "H15gDDnkmz5p" }, "source": [ "### scatter: 散布図の描画\n", "\n", "$x$と$y$、2つの量があって、どちらにも興味がある、 \n", "あるいは両者の間の相関に興味がある場合、散布図を描くと、見通しやすくなることが多いです。\n", "\n", "以下では、2017年の宇都宮の平均気温とアイスクリームの消費量の相関を見てみましょう。 \n", "気温・アイスクリームの消費量ともに、1月から12月に順番にリストに入れていくことにします。\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 304 }, "id": "ZpNvApJ-nVKv", "outputId": "377c58ab-36c5-4194-e701-9be33e08a4a0" }, "outputs": [], "source": [ "x= [3.1, 4.3, 6.6, 13.2, 19.1, 20.9, 26.4, 25.1, 21.9, 15.7, 9.6, 3.8]\n", "y= [568, 572, 804, 833, 930, 965, 1213, 1120, 835, 540, 451, 502]\n", "\n", "plt.figure(figsize=(4,4)) \n", "plt.title(\"宇都宮市\") ## 図にはタイトルをつけることができます\n", "plt.xlabel(\"平均気温 (℃)\") #軸ラベルの指定\n", "plt.ylabel(\"世帯あたりのアイスクリーム・シャーベットの消費金額 (円)\")\n", "plt.scatter(x,y)\n", "plt.show()\n", "plt.close() " ] }, { "cell_type": "markdown", "metadata": { "id": "B-535rGQpYsg" }, "source": [ "相関係数などの情報を含んだもう少しかっこいい図を作ってみましょう。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 348 }, "id": "bOGBYPK7pgJZ", "outputId": "05eba7a2-d3a0-4e48-afa4-416483f3b606" }, "outputs": [], "source": [ "#データをリスト形式で与える\n", "x= [3.1, 4.3, 6.6, 13.2, 19.1, 20.9, 26.4, 25.1, 21.9, 15.7, 9.6, 3.8]\n", "y= [568, 572, 804, 833, 930, 965, 1213, 1120, 835, 540, 451, 502]\n", "\n", "# Numpyを使ってx,yの配列間の相関行列を計算. 詳細は相関分析の際に.\n", "r = np.corrcoef(x,y) \n", "corrcoef = r[0,1] #相関行列rの(0,1)成分を取り出す\n", " \n", "#季節ごとに適当に色を塗るために、月を入力すると色を返す関数を定義\n", "def seasoncolor(month):\n", " if month <= 2 or month ==12:\n", " return \"blue\"\n", " elif 3 <= month <=5:\n", " return \"green\"\n", " elif 6 <= month <=8:\n", " return \"red\"\n", " elif 9<= month <=11:\n", " return \"orange\"\n", " else:\n", " print(\"month\",month, \" is not supported\")\n", "\n", "# 図の描画部分\n", "fig = plt.figure(figsize=(5,5))\n", "ax = fig.add_subplot(111) #fig.add_subplot(1,1,1)と等価. 1行1列の1番目のグラフをaxとして取得\n", "ax.set_facecolor(\"#D3DEF1\")\n", "ax.set_title(\"宇都宮市\")\n", "ax.set_xlabel(\"平均気温 (℃)\")\n", "ax.set_ylabel(\"世帯あたりのアイスクリーム・シャーベットの消費金額 (円)\")\n", "ax.grid(True,axis=\"both\",color=\"w\", linestyle=\"dotted\", linewidth=0.8)\n", "for i in range(len(x)):\n", " tcol=seasoncolor(i+1)\n", " ax.scatter(x[i],y[i],marker=\"o\",s=10,color=tcol,zorder=20000,alpha=0.7)\n", " ax.text(x[i],y[i],str(i+1)+\"月\",color=\"k\",fontsize=8)\n", "ax.text(0.1,0.9, \"r=\"+str(\"%5.2f\" % corrcoef), transform=ax.transAxes,fontsize=12)\n", "plt.show()\n", "plt.close()" ] }, { "cell_type": "markdown", "metadata": { "id": "WTDhlmHY7r2p" }, "source": [ "### $\\clubsuit$ ```ax (matplotlib.axes) ```" ] }, { "cell_type": "markdown", "metadata": { "id": "qQHLqX9ntOur" }, "source": [ "上では、axという見慣れないものが導入されている。\n", "\n", "キャンバスの上に小さな作業領域```axes```を指定するための```add_subplot```や```add_axes```といった関数がある。\n", "```ax```はこれらの関数で生成される作業領域に慣例的に用いる変数名で、とくに作業領域が一つの場合によく用いる。\n", "\n", "```axes```を使うと、キャンバス上に複数のグラフを描くことができるので、たとえばキャンバスを四分割して、似たようなグラフを4つ同時に描いたりするのに便利. \n", "```axes```を使いこなすのは少々テクニカルな点も多いので、よくわからない部分はとりあえず飛ばし読みで構わない。 \n", "\n", "使いこなせれば、論文に使う図表を作るなど、大変重宝するはずだ。細かい図の書き方は必要に応じて勉強していこう.\n", "\n", "`axes`に関しては、日本語で書かれた以下の記事もおすすめ:\n", "https://qiita.com/skotaro/items/08dc0b8c5704c94eafb9" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 319 }, "id": "5btbwRL4YCJq", "outputId": "252c4ae4-b687-4c45-ff80-62e92db6ede9" }, "outputs": [], "source": [ "data = [152, 170, 82, 85, 79, 92, 88]\n", "label = [\"国語\",\"英語\", \"数IA\", \"数IIB\", \"化学\", \"物理\", \"世界史\"]\n", "\n", "fig = plt.figure(figsize=(10,5))\n", "axTL = fig.add_subplot(2,2,1) #TL: Top Leftのつもり\n", "axTR = fig.add_subplot(2,2,2) #TR: Top Rightのつもり \n", "axBL = fig.add_subplot(2,2,3) #BL: Bottom Leftのつもり\n", "axBR = fig.add_subplot(2,2,4) #BR: Bottom rightのつもり\n", "axTL.plot(y_sev)\n", "axTR.scatter(x,y)\n", "axBL.bar(label,data,align='center',width=0.5,color=\"red\") \n", "axBR.text(0.5,0.4,\"右下だよ\")\n", "plt.show()\n", "plt.close()" ] }, { "cell_type": "markdown", "metadata": { "id": "161cPa6nrVJG" }, "source": [ "### Google Drive上のファイル操作&Google Colab.上で作ったグラフの保存\n", "\n", "プログラムを実行して絵を描けるようになったら、次にそれを保存して、レポートに貼り付けたり、誰かに送ったりする必要が出てくる。 \n", "Google Colab.では、同じGoogleのサービスであるGoogle drive上にファイルを保存したり保存したファイルを他人と共有することができる。\n", "\n", "皆さんのアカウントのGoogle Driveにあるファイルに、Google Colab.からアクセスするためにはマウントという作業が必要になる。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Cr7sAeT37OSo" }, "outputs": [], "source": [ "from google.colab import drive\n", "drive.mount('/content/drive') # ←のマウントする際の名前は好きに決められる。drive.mount('gdrive')とかでもOK" ] }, { "cell_type": "markdown", "metadata": { "id": "fKJKV7iQj_kt" }, "source": [ "上のコードを実行し(複数アカウントを所持している方はアカウントの選択をして)ポップアップ等の指示に従い操作を行う。\n", "成功すると、Mounted at ほにゃららというメッセージが出る。\n", "\n", "上のコード2行目は「google driveをdriveという名前でマウントする」という操作を表していて、\n", "マウントできていれば、以下のコードを実行すると、皆さんのアカウントのマイドライブ直下のファイル一覧が表示される。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "6CyxsW43aDh0" }, "outputs": [], "source": [ "!ls ./drive/MyDrive" ] }, { "cell_type": "markdown", "metadata": { "id": "Mem3umDTc21A" }, "source": [ "Google Colab.からは、!マークをつけることでLinuxコマンドが使える. \n", "上の`ls`というコマンドは(List Segmentsの略で)ファイルやディレクトリの情報を表示するコマンド。\n", "\n", "半角のスラッシュ```/```はディレクトリ階層を意味していて、Windowsで言うところの¥に相当する。 \n", "コンピュータでディレクトリやパスを指定するときは通常このような**パス**と呼ばれるものを指定して扱う(※パスについてはファイル操作のノートに詳しい記述がある).\n" ] }, { "cell_type": "markdown", "metadata": { "id": "tWWrxd0N_z4k" }, "source": [ "次に、GoogleDriveに、図を保存する用のフォルダを作っておこう. \n", "\n", "`mkdir`(make directoryの略)コマンドで、マイドライブ直下に```Colab_pic```というディレクトリを作ることにします." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Ou5uXl-U_zV9" }, "outputs": [], "source": [ "!mkdir './drive/MyDrive/Colab_pic' " ] }, { "cell_type": "markdown", "metadata": { "id": "t3G421FJjMMG" }, "source": [ "上のコードを実行後にエラーが出ていなければGoogle Driveを開くと`Colab_pic`というディレクトリが作成されているはず。" ] }, { "cell_type": "markdown", "metadata": { "id": "dlHHaqPA_8we" }, "source": [ "一度フォルダを作ってしまうと、2回目以降は上のコードを実行しても「既にフォルダありますよ!!」というメッセージがでるため、ノートを開く度といったように複数回実行する必要はない。\n" ] }, { "cell_type": "markdown", "metadata": { "id": "enGmvm6gAVoE" }, "source": [ "これで準備ができた。試しに以下のコードを実行して図を保存してみよう。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "PHW-hUepA0dD" }, "outputs": [], "source": [ "# 先程の図\n", "fig = plt.figure(figsize=(6,6))\n", "ax = fig.add_subplot(1,1,1) \n", "ax.set_facecolor(\"#D3DEF1\")\n", "ax.set_title(\"宇都宮市\")\n", "ax.set_xlabel(\"平均気温 (℃)\")\n", "ax.set_ylabel(\"世帯あたりのアイスクリム・シャーベットの消費金額 (円)\")\n", "ax.grid(True,axis=\"both\",color=\"w\", linestyle=\"dotted\", linewidth=0.8)\n", "for i in range(len(x)):\n", " tcol=seasoncolor(i+1)\n", " ax.scatter(x[i],y[i],marker=\"o\",s=10,color=tcol,zorder=20000,alpha=0.7)\n", " ax.text(x[i],y[i],str(i+1)+\"月\",color=\"k\",fontsize=8)\n", "ax.text(0.1,0.9, \"r=\"+str(\"%5.2f\" % corrcoef), transform=ax.transAxes,fontsize=12)\n", "###plt.show()の代わりに、savefigというメソッドを使い、グラフを保存する\n", "plt.savefig(\"./drive/My Drive/Colab_pic/scatter_Utsunomiya_ice.pdf\") \n", "plt.close()" ] }, { "cell_type": "markdown", "metadata": { "id": "LdmUlNpQjwEL" }, "source": [ "上のコードを実行後にGoogle Drive上の指定したフォルダを確認してみよう.\n", "\n", "少しラグがあるかもしれないが、少し待つと、フォルダ内に`scatter_Utsunomiya_ice.pdf`というpdfファイルが出来る。\n", "\n", "Matplotlibはファイル名を変えるだけで、指定した拡張子で描画してくれるので色々試してみよう(.jpg,.pdf,.eps,.pngなど)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**注意**\n", "\n", "`savefig()`関数の前に`show()`を実行するとその時点でキャンバスがクリアされるので、`savefig()`で保存したファイルが空になってしまうので注意. \n", "`show()`は`savefig()`の後に実行すること." ] }, { "cell_type": "markdown", "metadata": { "id": "uICk2RZ5uFwD" }, "source": [ "**余談** \n", "\n", "プレゼンのスライドに載せる画像は可能な限り、ラスタ形式ではなくベクタ形式(pdfなど)がおすすめです.\n", "(あるいは、高解像度でjpegやpngを作ってスライドを作り、誰かにスライドを渡すときは軽量化する)\n", "線がガビガビの図だらけスライドを使ったスライドは「あぁ配慮が足りないんだな」と思われて損をしてしまうかもしれない。\n", "例えば、プレプリントサーバーであるarXivには毎日多くの論文がアップロードされているが、\n", "論文の体を為していない文章や、いわゆる「トンデモ論文」といって、科学的なプロセスを踏んでいない論文も稀にある。\n", "そうした論文に共通する特徴の一つとして、図がガビガビになっていることが挙げられる。\n", "論文でラスタ画像を使う場合は「トンデモ論文だと思われて誰にも読まれない危険性」を理解して使いましょう。" ] }, { "cell_type": "markdown", "metadata": { "id": "WHPD2iPs0JlX" }, "source": [ "**練習** \n", "\n", "これまでの(棒グラフ,円グラフ,1次元図,散布図)のグラフを描画したコード部分で、 \n", "データを自由に足したりオプションを変えながら、`plt.show()`を`plt.savefig(\"your_filepath.filetype\")`に書き換えてファイルに出力してみましょう。\n", "少々くどいが念の為言っておくと、`your_filepath`や`filetype`は実際のファイルパスや拡張子にそれぞれ変えること。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "IOyDWeNzUzou" }, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": { "id": "FS7A2eX6daYK" }, "source": [ "### gifアニメーションの作成" ] }, { "cell_type": "markdown", "metadata": { "id": "u5BC24cadfQH" }, "source": [ "「なんだこの程度のグラフならExcelでも簡単にできるじゃん...」と思った皆さんのために、もう少し凝ったことをやってみましょう。 \n", "\n", "以下のリンクに、x軸をGDP,y軸を24歳から35歳の平均就学年数の女性/男性比(%)としたグラフを画像ファイル(png)にしたものを公開した。 \n", "もともとのデータはFACTFULNESSから来ているのでライセンスフリー.\n", "そのデータをもとに、Pythonで幾つかの国のデータを抜き出して、グラフを作成してある。\n", "\n", "https://drive.google.com/file/d/1W57Ci8cbFbjGkB5muFURQJqib2E9NSz3/view?usp=sharing\n", "\n", "この画像ファイルをパラパラ漫画のようにしてgifアニメーションにしてみよう.\n", "そのために必要なファイルにGoogle Colab.からアクセスできるよう、以下のいずれかの手順で作業しよう(授業では楽ちんな2を使う予定)" ] }, { "cell_type": "markdown", "metadata": { "id": "qH7GKz_yfRhO" }, "source": [ "#### 方法1: Google Driveを開いて直接操作する方法\n", "\n", "1. まず上記リンクからダウンロード\n", "2. Zip形式なので、それを解凍する\n", "3. 解凍したフォルダを自身のGoogle Driveの好きな場所にアップロードする\n", " ※以下のグラフを作成するコードをそのまま使いたければマイドライブ直下にアップロード\n", " \n", "つまり、アップロードされた画像ファイルが、マイドライブ→`GDPvsWomenInSchool`という階層にある状態になる。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 方法2: LinuxコマンドとPythonコードで行う方法\n", "\n", "zipを解凍するソフトウェアが無い方はこちらがおすすめ:\n", "\n", "1. Google Driveをマウントしておく\n", "2. Pythonコードを使って、共有リンクからzipファイルを保存する\n", "3. zipファイルをunzipコマンドで展開し、展開したフォルダをGoogle Driveのフォルダに移動する" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 手順 1. Driveのマウントがまだ未実行であればコメントアウトを外して実行\n", "#from google.colab import drive\n", "#drive.mount('/content/drive') " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 手順 2. Pythonコード\n", "import urllib.request\n", "import sys\n", "url = \"https://drive.google.com/uc?export=download&id=1ifBG3v1_Z2VixHcxpQUP7WLEUi5y7OfZ\"\n", "filename = \"GDPvsWomenInSchool.zip\"\n", "urllib.request.urlretrieve(url,filename)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 手順3. Linuxコマンド (Google Colab.上では、!から始めることでLinuxコマンドを使うことができる)\n", "\n", "#zipファイルを解凍する\n", "!unzip GDPvsWomenInSchool.zip \n", "\n", "#解答したファイルをマイドライブ直下へ(マウントした状態でないと無効)\n", "!mv GDPvsWomenInSchool drive/MyDrive/" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### pngファイルを読み込んでgifアニメーションを作成する" ] }, { "cell_type": "markdown", "metadata": { "id": "C79BzM_hhBT3" }, "source": [ "先程のグラフがgoogle driveに保存されているかどうかは、以下のコマンドで確認できる(アップロードして直後はファイルが見つからないことがあります)." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "uFfxkEk-hGsO" }, "outputs": [], "source": [ "!ls ./drive/MyDrive/GDPvsWomenInSchool/*.png" ] }, { "cell_type": "markdown", "metadata": { "id": "8k2AXhuTfqJF" }, "source": [ "GDPvsWomenInSchool/の部分は、もし独自のフォルダなどを用意した場合は、対応するフォルダ名に適宜変更してください.\n", "\n", "ファイルが確認できたら、年代ごとに別々になったたくさんのグラフを、1つのパラパラ漫画にまとめてみよう. \n", "\n", "pngファイルをまとめてgifにするコードは以下の通り(処理にしばし時間がかかります):" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Uqdk5CaBdgJi" }, "outputs": [], "source": [ "from PIL import Image\n", "import glob\n", "\n", "# まとめたいpngをワイルドカード*で指定\n", "files = sorted(glob.glob('./drive/My Drive/GDPvsWomenInSchool/GDPvsWomen*.png')) \n", "images = list(map(lambda file: Image.open(file), files))\n", "\n", "# 出力名と保存場所を指定\n", "oupf = './drive/My Drive/Colab_pic/GDPvsWomen.gif' \n", "\n", "# 画像をGIFアニメーションとして保存\n", "images[0].save(oupf, save_all=True, append_images=images[1:], duration=400, loop=0)" ] }, { "cell_type": "markdown", "metadata": { "id": "eiu1iJjEokt6" }, "source": [ "エラーが出なければ、変数```oupf```で指定した場所に、gifファイルが生成されている。\n", "\n", "作成例: [ファイルへのリンクはこちら](https://github.com/SotaYoshida/Lecture_DataScience/blob/main/notebooks/pic_for_notebook/GDPvsWomen.gif)\n", "\n", "\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "id": "7rVhvZASjDPK" }, "source": [ "### $\\clubsuit$おまけ\n", "\n", "その他のグラフ" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "9nJ-tBtxjFJ2" }, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "from mpl_toolkits.mplot3d import axes3d\n", "from mpl_toolkits.mplot3d.axes3d import get_test_data\n", "import os\n", "\n", "X, Y, Z = get_test_data()\n", "fig = plt.figure(figsize=(10,5))\n", "ax = fig.gca(projection='3d')\n", "ax.set_xlabel(\"x\")\n", "ax.set_ylabel(\"y\")\n", "ax.set_zlabel(\"target function\")\n", "ax.view_init(elev=45)#上から見た角度を調整できる\n", "ax.plot_surface(X, Y, Z,cmap=plt.cm.viridis)\n", "plt.show()\n", "plt.close()" ] }, { "cell_type": "markdown", "metadata": { "id": "c34ap-zmHd5s" }, "source": [ "二次元ヒストグラム" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "n00yqPx9Hf1E" }, "outputs": [], "source": [ "import matplotlib.cm as cm\n", "import numpy as np\n", "\n", "mu1 = [ 3.0, 2.0]\n", "cov1 = [ [1.0, 0.7],[0.7,1.0]]\n", "numS = 50000\n", "\n", "sample1 = np.random.multivariate_normal(mu1,cov1,numS)\n", "x1, y1 = sample1.T\n", "\n", "fig = plt.figure(figsize=(6,5))\n", "ax1 = fig.add_subplot(111)\n", "H1 = ax1.hist2d(x1,y1, bins=40, cmap=cm.jet)\n", "ax1.scatter(mu1[0],mu1[1],color=\"k\",marker=\"x\")\n", "ax1.set_title('sample1')\n", "ax1.set_xlabel('x'); ax1.set_ylabel('y')\n", "fig.colorbar(H1[3],ax=ax1)\n", "plt.show()\n", "plt.close()\n" ] }, { "cell_type": "markdown", "metadata": { "id": "n2vmy-cwzHVi" }, "source": [ "日本地図" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "eFGbkhOQzH27" }, "outputs": [], "source": [ "!pip install japanmap\n", "from japanmap import pref_names,pref_code,groups,picture\n", "import matplotlib.pyplot as plt\n", "from pylab import rcParams" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "KkH_HRkYMmUw" }, "outputs": [], "source": [ "plt.figure(figsize=(6,6))\n", "plt.imshow(picture({'栃木県': 'red', '群馬県': 'blue'}))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 色に関する注意\n", "\n", "色は、データの可視化において非常に重要な要素である.\n", "一方で、色は人間の感覚に依存するため、その選択には注意が必要である.\n", "\n", "例えば、男性の20人に1人程度の割合で何らかの色覚異常があるとされている。\n", "\n", "Pythonでは、カラーマップやパレットといった、色の選択を補助するための機能が用意されているが、\n", "カラーマップの中にも、colorblind friendlyなものが用意されており、学術論文などでも、色覚異常の読者に対する配慮が求められることがある。\n", "\n", "**自分が見ている世界と、他人が見ている世界は異なる**という事実を意識するのは、色に限らずコミュニケーションなどにおいても重要である。" ] } ], "metadata": { "colab": { "collapsed_sections": [], "include_colab_link": true, "name": "Python_chapter4_Matplotlib.ipynb", "provenance": [] }, "kernelspec": { "display_name": "Python 3.9.13 64-bit", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.9" }, "vscode": { "interpreter": { "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49" } } }, "nbformat": 4, "nbformat_minor": 0 }