{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "colab": { "provenance": [], "toc_visible": true }, "kernelspec": { "name": "python3", "display_name": "Python 3" }, "language_info": { "name": "python" } }, "cells": [ { "cell_type": "markdown", "source": [ "# パッケージとモジュール・ファイル操作\n", "ここまでの講義を通して、Pythonの基本的な構文について学びました。\n", "\n", "今回は、データ構造化処理では決して欠かせないファイルの読み書きを学びます。\n", "\n", "また、Pythonプログラミングにおいて最も重要な、外部のモジュールやパッケージを利用したプログラムについても見ていきます。\n", "\n", "今回学ぶ内容は、次のとおりです。\n", "\n", "* モジュール・パッケージの利用\n", "* 基本的なファイルの読み書き\n", "* ファイルパス\n", "\n", "はじめに、本講義で使用するファイルを皆さんの環境にダウンロードするため、次のコードを実行してください。" ], "metadata": { "id": "Fn0oKgscebci" } }, { "cell_type": "code", "source": [ "!wget https://github.com/tendo-sms/python_beginner_2023/raw/main/files_3/files.zip .\n", "!unzip files.zip\n", "!mv files/* ." ], "metadata": { "id": "L4q4Uc_8RV1v" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "# モジュール・パッケージの利用\n", "ここでは、外部のモジュールやパッケージを利用する方法についてご紹介します。" ], "metadata": { "id": "_5uzGBYmfjhO" } }, { "cell_type": "markdown", "source": [ "## モジュールとパッケージのおさらい\n", "「Pythonの基礎2」の講義にて、モジュールおよびパッケージという用語をご説明しました。ここでおさらいしておきましょう。\n", "\n", "| 用語 | 意味 |\n", "|---|---|\n", "| モジュール | ソースコードを記述した拡張子「.py」のファイル |\n", "| パッケージ| ある機能を提供するために必要な、複数のモジュールをまとめたもの |" ], "metadata": { "id": "PQK8xbImUFSt" } }, { "cell_type": "markdown", "source": [ "## モジュールやパッケージを利用する意味【超・重要!!】\n", "実は、**「外部のモジュールやパッケージの利用」こそPythonプログラミングの真髄**、と言っても過言ではありません。\n", "\n", "モジュールやパッケージを利用するということは、「誰かが作った外部のソースコードを使わせてもらう」というイメージです。\n", "\n", "Pythonは他のプログラミング言語と比べて、外部のモジュールやパッケージが非常に充実しています。\n", "\n", "皆さんが苦労することは、世界中の誰もが同じように苦労します。そして、苦労した誰かが必ず、便利なソースコードを作っているものなのです。それらのソースコードを簡単に取り込める方法こそが、これから紹介するimportなのです。\n", "\n", "苦労して作った数百行のプログラムが、誰かが公開しているソースコードを使ったらたった2~3行で実現できた、なんて例も珍しくありません。「車輪の再発明」に陥らないよう、しっかりと今回の話を理解してくださいね。\n", "\n", "次の例を見てみてください。Gatanのdm3ファイルに記録されたスペクトルデータをグラフ化するプログラムです。\n", "\n", "あらかじめHyperSpyというパッケージをインストールしてしまえば、ソースコード自体はたったの3行でかけてしまうのです。(外部のモジュールを取り込むimportの行を除けば、メインの処理は実質2行!)" ], "metadata": { "id": "YaaKUblPUHUx" } }, { "cell_type": "code", "source": [ "# 事前にHyperSpyパッケージのインストール\n", "!pip install hyperspy" ], "metadata": { "id": "mf52FyeH_1_s" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "# Gatanのdm3ファイルに記録されたスペクトルデータをグラフ化\n", "import hyperspy.api as hs\n", "\n", "spectrum = hs.load(\"spectrum_sample.dm3\")\n", "spectrum.plot()" ], "metadata": { "id": "4ncLCOO6_48B" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "ちなみに、スペクトルのグラフ化にはExcelを使う方が多いかもしれません。\n", "\n", "しかし、dm3フォーマットを読み解いて、加工して、Excelで読み込んで、マウスでポチポチとグラフを作成するのと比べてどうでしょうか。Pythonの底力を感じませんか!?\n", "\n", "プログラミングというと、複雑なアルゴリズムを設計して、たくさんのソースコードを書かなければいけない、というイメージが強いかもしれません。\n", "\n", "しかし、Pythonであれば、まるでブロックを組み立てるようにプログラムを作成できてしまいます。皆さんの中でパラダイムシフトを起こして、プログラミングに対するハードルをぐぐっと下げてくださいね。" ], "metadata": { "id": "5SZqgVk7JEs2" } }, { "cell_type": "markdown", "source": [ "## モジュールのインポート(import)\n", "ここでは、外部の「モジュール」を読み込んで使ってみます。\n", "\n", "モジュールの読み込みには、次のとおり「**import文**」を使います。\n", "\n", "~~~\n", "import モジュール名(拡張子.pyはつけない)\n", "~~~\n", "「モジュール名」には、モジュールのファイル名「モジュール名.py」から拡張子.pyを取り除いた名称を記述します。\n", "\n", "モジュールをimportすると、次のようにモジュール内の関数、変数などを呼び出せるようになります。\n", "\n", "~~~\n", "モジュール名.関数名(引数)\n", "モジュール名.変数名\n", "~~~" ], "metadata": { "id": "b1As6G7J_Cji" } }, { "cell_type": "markdown", "source": [ "### Pythonで用意されたモジュールをインポートする\n", "import文の簡単な例として、まずはPythonであらかじめ用意された標準モジュールである、「mathモジュール」をインポートして使ってみましょう。\n", "\n", "mathモジュールは、数学的な機能を提供するモジュールです。\n", "\n", "まずは、関数を呼び出してみます。\n", "\n", "次のソースコードは、平方根を求めるためにmathモジュールのsqrt関数を呼び出しています。「モジュール名.関数名(引数)」の形になっていますね。" ], "metadata": { "id": "fLRwV2O6RsKy" } }, { "cell_type": "code", "source": [ "import math\n", "\n", "print(math.sqrt(2))" ], "metadata": { "id": "4lUB-UJTMgyj" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "次に、変数を呼び出してみましょう。\n", "\n", "次のソースコードは、円周率の値が格納された変数piを呼び出しています。「モジュール名.変数名」の形になっていますね。" ], "metadata": { "id": "viwuqQGGMjRQ" } }, { "cell_type": "code", "source": [ "print(math.pi)" ], "metadata": { "id": "r0cBB_gJQL63" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "### 別名を付けてインポートする\n", "次のようなインポートの仕方もあります。\n", "\n", "~~~\n", "import モジュール名 as 別名\n", "~~~\n", "\n", "このようにインポートすると、モジュール名ではなく、別名で関数や変数などを使用できるようになります。\n", "\n", "(逆に、モジュール名は使用できなくなります。)\n", "\n", "~~~\n", "別名.関数名(引数)\n", "別名.変数名\n", "~~~\n", "\n", "ここまで見てきたmathの例で、別名を使ってインポートしてみましょう。「mt」という別名を付けています。" ], "metadata": { "id": "Ma28AXNpz1Jn" } }, { "cell_type": "code", "source": [ "import math as mt\n", "\n", "print(mt.sqrt(2))\n", "print(mt.pi)" ], "metadata": { "id": "yc-Nc8pa0fKC" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "mathはモジュール名が短いのであえてasを使う必要はありませんが、モジュール名が長い場合などはasを使って、記述しやすい別名を付けるのもよいでしょう。" ], "metadata": { "id": "ffO2zZsJ0qKj" } }, { "cell_type": "markdown", "source": [ "### 自作のモジュールをインポートする\n", "Pythonであらかじめ用意されたモジュールや、他の誰かが公開している外部モジュールだけではなく、自分でモジュールを作ってインポートすることもできます。\n", "\n", "最初に今回の講義で必要なファイルをダウンロードしたので、カレントディレクトリにsample_module.pyというファイルがダウンロードされています。ファイルの中身は、次のような内容です。\n", "\n", "~~~\n", "def print_function():\n", " print(\"関数print_functionが呼び出されました。\")\n", "\n", "var1 = \"変数1\"\n", "var2 = \"変数2\"\n", "~~~\n", "\n", "モジュールをインポートする方法は、自作のモジュールであっても同じです。次のソースコードでは、モジュールsample_module.pyをインポートして、print_function関数の呼び出し、およびvar1変数とvar2変数の値の参照を行っています。" ], "metadata": { "id": "NgSiYvuRRncM" } }, { "cell_type": "code", "source": [ "import sample_module\n", "\n", "sample_module.print_function()\n", "\n", "print(sample_module.var1)\n", "print(sample_module.var2)" ], "metadata": { "id": "15nfVGIGSLq1" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "【ワンポイント】\n", "\n", "モジュールを自作するというのは、どのようなケースでしょうか。\n", "\n", "例えば構造化プログラムは、そのほとんどが「データの入力」「データの構造化」「データの出力」という3つの大きな機能からなります。\n", "\n", "これらの機能をソースコードの上から下へ、ダラダラと書いていくのはやめましょう。\n", "\n", "この3つの機能それぞれを関数にするというやり方も悪くありませんが、これらは一つ一つが大きな機能なので、各機能ごとに自作のモジュールを作る方がよいでしょう\n", "\n", "例:\n", "* データの入力:input_data.py\n", "* データの構造化:structure_data.py\n", "* データの出力:output_data.py\n", "\n", "そして各機能をさらに細かい機能に分割し、分割した機能ごとに関数を作ると、大きなプログラムでも非常に理解しやすい構造になります。例えば、次のようなイメージです。\n", "\n", "* input_data.pyの中に、次のような関数を定義\n", " * jdxファイルの読み込み関数、CSVファイルの読み込み関数 etc.\n", "* structure_data.pyの中に、次のような関数を定義\n", " * データのフォーマットを変換する関数、データを整形する関数 etc.\n", "* output_data.pyの中に、次のような関数を定義\n", " * メタデータをファイルに出力する関数、グラフを出力する関数 etc.\n" ], "metadata": { "id": "AJLvPwsdW3Iu" } }, { "cell_type": "markdown", "source": [ "### 特定の関数や変数だけをインポートする\n", "モジュール全部ではなく、モジュールの中の特定の関数や変数だけをインポートすることもできます。\n", "\n", "from ~ import文を使って、次のように定義します。\n", "\n", "~~~\n", "from モジュール名 import 関数名or変数名 [, 関数名or変数名, ・・・]\n", "~~~\n", "importする関数や変数が複数あるときは、カンマ区切りで複数指定することができます。\n", "\n", "また、この方法でimportした関数や変数は、前に「モジュール名.」を付けずに呼び出します。\n", "\n", "~~~\n", "関数名(引数)\n", "変数名\n", "~~~\n", "\n", "先ほどのsample_module.pyから、関数print_functionと、変数var2だけをインポートして使ってみましょう。" ], "metadata": { "id": "4iixOG7TSY3k" } }, { "cell_type": "code", "source": [ "from sample_module import print_function, var2\n", "\n", "print_function()\n", "\n", "print(var2)\n", "print(var1)" ], "metadata": { "id": "1o2WIKUoSh4j" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "関数名や変数名の前に「sample_module.」を付けないことに注意してください。\n", "\n", "var1はインポートしていないので、参照しようとするとエラーになっています。\n", "\n", "【注意事項】\n", "\n", "Google Colaboratoryでは、同一ページ内の全てのソースコードはつながっています。前の例題で「import sample_module」を行っているため、「sample_module.var1」と指定すると参照できてしまいます。動作が紛らわしいですが、ご注意ください。" ], "metadata": { "id": "kAxG8YkDVwZL" } }, { "cell_type": "markdown", "source": [ "## パッケージのインストール" ], "metadata": { "id": "5aHaiLku_IJG" } }, { "cell_type": "markdown", "source": [ "### pipコマンドとcondaコマンド\n", "今度は、ある機能を実現するためのモジュールをまとめた、外部の「パッケージ」を使ってみましょう。\n", "\n", "パッケージを利用するには、Pythonプログラムを実行する前に、あらかじめ端末(Windowsのコマンドプロンプト、Linuxのログインシェルなど)で次のコマンドを実行してパッケージをインストールしておく必要があります。\n", "\n", "- 公式版Pythonでは、「**pipコマンド**」でインストールします。\n", "- Anacondaでは、「**condaコマンド**」でインストールします。\n", "\n", "Python実行環境をOSにインストールすると、Windowsのコマンドプロンプトや、Linuxのログインシェルなどから、pipコマンドおよびcondaコマンドを使用できるようになります。\n", "\n", "パッケージをインストールするには、次のように実行します。なお「パッケージ名」は大文字小文字を区別しません。\n", "\n", "~~~\n", "pip install パッケージ名\n", "~~~\n", "または\n", "~~~\n", "conda install パッケージ名\n", "~~~\n", "\n", "今回の講座では、以降はpipコマンドを例にご説明します。\n", "\n", "【注意事項】\n", "\n", "pipコマンドでしかインストールできないパッケージや、condaコマンドでしかインストールできないパッケージも存在します。\n", "\n", "ご自身の環境で使いたいパッケージがない場合、上記のコマンドを使わずに自力でインストールできることもありますが、非常にハードルが高いです。代替可能な別のパッケージを探すか、Python実行環境の変更も検討しましょう。\n", "\n", "なおAnacondaはcondaコマンドだけでなく、pipコマンドも使用できます。そのためAnacondaを使っておけば安心と思いがちです。しかしpipコマンドとcondaコマンドを混在して使用するとやっかいなトラブルが発生することもあるので、あまりお勧めできません。" ], "metadata": { "id": "XfX-BuQdUPco" } }, { "cell_type": "markdown", "source": [ "### pipコマンドの使用例\n", "例として、PythonでGUIのプログラムを作成するときによく使用される、PyQt5のパッケージをインストールしてみます。\n", "\n", "(本当は、本講座で詳しくご紹介する「pandas」や「Matplotlib」といったパッケージをインストールする例としたかったのですが・・・Google Colaboratoryでは、最初からこれらのパッケージがインストールされていました!)" ], "metadata": { "id": "XGPcVd4-URTD" } }, { "cell_type": "code", "source": [ "!pip install pyqt5" ], "metadata": { "id": "K47xuaKBayS9" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "なお先頭の「!」は、Google Colaboratory独自の構文です。Pythonプログラムを実行するのではなく、Linuxのログインシェルのイメージで、bashのコマンドや、catコマンドやdiffコマンドといったLinuxコマンドを実行できます。\n", "\n", "pipコマンドの便利なところは、パッケージ間の依存関係を自動で管理してくれるところです。\n", "\n", "パッケージには、「このパッケージを動かすには、前提として別のパッケージが必要」といった依存関係があります。\n", "\n", "pipコマンドでパッケージをインストールすると、前提となるパッケージが入っていない場合は自動で一緒にインストールしてくれます。" ], "metadata": { "id": "vtVirmVegI5M" } }, { "cell_type": "markdown", "source": [ "# 基本的なファイルの読み書き\n", "「Pythonの基礎1」の講義で、どんなデータ構造化プログラムでも次のような流れが基本となることをご説明しました。\n", "\n", "1. データの入力: ファイルの読み取り方\n", "1. データの構造化: データ成形・加工の方法\n", "1. データの出力: 可視化の方法や保存\n", "\n", "ここでは、データの入力や出力に関わる重要な機能として、ファイルの読み書きの方法をご紹介します。" ], "metadata": { "id": "b8RpY1idcBk-" } }, { "cell_type": "markdown", "source": [ "## 標準的なファイルの読み書き\n", "まずは外部のモジュールやパッケージを使わずに、Python標準の関数を使ってデータを読み込んでみます。\n", "\n", "次のプログラムを例にして説明します。読み込むファイルopen_sample.jdxは、JCAMP-DXのjdxファイルから、説明のために先頭のヘッダ部分のみを抜粋したものです。" ], "metadata": { "id": "QAS9Bl1Oeg-N" } }, { "cell_type": "code", "source": [ "# ファイルのオープン\n", "fobj = open(\"open_sample.jdx\")\n", "\n", "# ファイルの読み込み\n", "txtdata = fobj.read()\n", "print(txtdata)\n", "\n", "# ファイルのクローズ\n", "fobj.close()" ], "metadata": { "id": "7UisyAcUsESv" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "このソースコードを構成するそれぞれの機能について、ひとつひとつ解説します。\n" ], "metadata": { "id": "jQqFgDT3zf0H" } }, { "cell_type": "markdown", "source": [ "### ファイルのオープン(open関数)\n", "Pythonでファイルの読み書きを行うには、まずファイルのオープンを行う必要があります。\n", "\n", "例えば、WindowsでテキストファイルやExcelファイルの読み書きをするとき、まずはエクスプローラでファイルをダブルクリックするなどして、ファイルを開きますよね。そんなイメージです。\n", "\n", "標準的なファイルのオープンでは、「**open関数**」を使用します。\n", "\n", "~~~\n", "open(ファイルパス)\n", "~~~\n", "\n", "open関数の戻り値として、「**ファイルオブジェクト**」というものが返ってきます(変数fobjに格納)。ファイルオブジェクトは、ファイルをPythonプログラム上で読み書きできる形にしたデータです。\n", "\n", "このファイルオブジェクトを使って、データの読み書きを行います。" ], "metadata": { "id": "OhcI_hwXkpuC" } }, { "cell_type": "markdown", "source": [ "### ファイルの読み込み(readメソッド)\n", "ファイルからデータを読み込む方法はいくつかありますが、今回の例では最もシンプルな「**readメソッド**」を使用しています。\n", "\n", "~~~\n", "ファイルオブジェクト.read()\n", "~~~\n", "\n", "readメソッドの戻り値として、ファイルの中身が文字列として返ってきます(変数txtdataに格納)。\n", "\n", "print関数でtxtdataの中身を画面に出力すると、ファイルの中身が格納されていることが分かります。" ], "metadata": { "id": "KLtFMUhZFO40" } }, { "cell_type": "markdown", "source": [ "### ファイルのクローズ(closeメソッド)\n", "最後に、「**closeメソッド**」でファイルを閉じます。\n", "\n", "~~~\n", "ファイルオブジェクト.close()\n", "~~~\n", "\n", "WindowsでテキストファイルやExcelファイルの読み書きを終えたあと、閉じるボタンをクリックするなどしてファイルを閉じますよね。そんなイメージです。\n", "\n", "closeメソッドでファイルを閉じた後は、もうreadメソッドで読み込んだりすることはできません。\n", "\n", "ちなみにクローズを忘れてしまうと、例えば、長期間動き続けるようなプログラムにおいてファイルをオープンできる数の上限に達してしまい、プログラムがエラーになるなどの問題が発生します。\n", "\n", "逆に言うと、上記のようなケース以外では、クローズを忘れても実際のところあまり実害はありません。\n", "\n", "とはいえ、ファイルを使い終わったらクローズするのは、Pythonを含めた多くのプログラミング言語において、お作法のようなものです。必ずクローズする癖を付けましょう・・・\n", "\n", "・・・と、言いたいところですが、いくら気を付けていても、ファイルのクローズは結構忘れてしまいがちです(ほぼ実害がないだけに)。クローズを入れていても、if制御文やfor制御文のbreakなどでスキップされてしまったなどのミスも起こりえます。\n", "\n", "そこでPythonでは、ファイルのクローズを自動的に行うための便利な仕組みを提供しています。それが次でご紹介する「with文」です。" ], "metadata": { "id": "ZbO6BqKfHBhw" } }, { "cell_type": "markdown", "source": [ "### with文\n", "「**with文**」を用いて次のように記述することで、インデントで示されたwith構文の影響範囲を抜けるときに、Pythonが自動でファイルをクローズしてくれます。\n", "\n", "~~~\n", "with open(ファイルパス) as ファイルオブジェクト:\n", " ファイルオブジェクトを使ってファイルを読み書きする処理\n", " ファイルオブジェクトを使ってファイルを読み書きする処理\n", " :\n", "\n", "後続の処理 (ここではもうファイルオブジェクトを使えない)\n", "~~~\n", "\n", "前述のプログラムをwith文を使って書き換えると、次のようになります。" ], "metadata": { "id": "sJgipiMsHEWN" } }, { "cell_type": "code", "source": [ "with open(\"open_sample.jdx\") as fobj:\n", " txtdata = fobj.read()\n", " print(txtdata)" ], "metadata": { "id": "vZIgXR2BHlP7" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "これで、最後にcloseメソッドを呼ぶ必要がなくなりました。\n", "\n", "with文は非常に便利ですが、稀に、with文を使わずにcloseメソッドを使った方が書きやすいケースもあります。\n", "\n", "ですが現在のところは、「**open関数を使うときは必ずwith文を使う**」と覚えておいていただければ問題ありません。" ], "metadata": { "id": "fPEfWAOZH73J" } }, { "cell_type": "markdown", "source": [ "### 1行ずつファイルを読み込む\n", "前述のreadメソッドでは、ファイルの中身すべてを1つの文字列として読み込みました。\n", "\n", "ですが構造化プログラムでは、機器が出力したファイルを1行ずつ読み込みながら処理する、といったことの方が多いです。\n", "\n", "次のプログラムを見てください。" ], "metadata": { "id": "8_ulXD4KLKCy" } }, { "cell_type": "code", "source": [ "with open(\"open_sample.jdx\") as fobj:\n", " for txtdata in fobj:\n", " print(\"読み込んだ行:\" + txtdata)" ], "metadata": { "id": "BYZ-TB6dNjYF" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "read関数を使わず、ファイルオブジェクト(fobj)をfor文の「繰り返し要素」に指定しています。\n", "\n", "これで、ファイルの中身を1行ずつ読み込めた!・・・ようにも見えますが、ちょっと違和感がありますね。各行の間に、不自然な空行が入っています。\n", "\n", "実はテキストファイルには、実際には以下のような形でデータが書き込まれています。テキストエディタ等では目に見えないだけで、【改行】というデータが入っているのです。\n", "\n", "~~~\n", "##TITLE= deg【改行】\n", "##JCAMP-DX= 5.01【改行】\n", " :\n", "##TIME= 10:29:30【改行】\n", "##SPECTROMETER/DATA SYSTEM= DELTA2_NMR【改行】\n", "\n", "~~~\n", "注:##の後ろに半角空白が入って見えますが、実際のデータには入っていません。Google Colaboratoryの仕様で、どうしても半角空白が勝手に入って見えてしまうのです・・・\n", "\n", "\n", "for文にファイルオブジェクトを指定して1行ずつ読み込むと、【改行】も含めた文字列が1行のデータとして読み込まれます(【改行】も含めて変数txtdataに格納される)。\n", "\n", "加えて、print関数が画面に出力する際に最後に【改行】を挿入しています。そのため、print関数は次のように画面に文字列を出力しています。\n", "\n", "~~~\n", "##TITLE= deg【元からファイルに入っていた改行】【print関数が挿入する改行】\n", "##JCAMP-DX= 5.01【元からファイルに入っていた改行】【print関数が挿入する改行】\n", " :\n", "##TIME= 10:29:30【元からファイルに入っていた改行】【print関数が挿入する改行】\n", "##SPECTROMETER/DATA SYSTEM= DELTA2_NMR【元からファイルに入っていた改行】【print関数が挿入する改行】\n", "~~~\n", "\n", "改行がそれぞれ2つずつあるため、不自然な空行が入ってしまうのです。\n", "\n", "これを防ぐには、読み込んだ1行の文字列にrstripメソッドを使いましょう。\n", "\n", "【おさらい】\n", "\n", "* 文字列.rstrip(削除文字列) と記述すると、文字列の末尾からだけ、連続した削除文字列をすべて削除します。削除文字列を指定しないと、連続した半角空白・タブ・改行を削除します。" ], "metadata": { "id": "tysYQ1R0QGvM" } }, { "cell_type": "code", "source": [ "with open(\"open_sample.jdx\") as fobj:\n", " for txtdata in fobj:\n", " print(\"読み込んだ行:\" + txtdata.rstrip())" ], "metadata": { "id": "Vkd4SF9pRiPF" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "rstripメソッドにより、txtdataに格納された文字列の右端にあった【元からファイルに入っていた改行】が削除されました。\n", "\n", "それにより、print関数は次のように画面に文字列を出力しました。\n", "\n", "~~~\n", "##TITLE= deg【print関数が入れた改行】\n", "##JCAMP-DX= 5.01【print関数が入れた改行】\n", " :\n", "##TIME= 10:29:30【print関数が入れた改行】\n", "##SPECTROMETER/DATA SYSTEM= DELTA2_NMR【print関数が入れた改行】\n", "~~~\n", "\n", "これで、期待どおりの出力になりましたね。\n", "\n", "【ワンポイント】\n", "\n", "ちなみに、この【改行】として記録されているデータは、OSによって異なるデータが採用されています。Windowsは「CRLF」、LinuxやmacOSなどでは「LF」というデータになっています。(ちなみに、古いmacOSではまた別の「CR」というデータでした。)\n", "\n", "ただしPythonでファイルを読み込むとき、特別に指定をしなければ、これらの違いを意識する必要はありません。どちらの改行コードも正しく読み取ることができます。" ], "metadata": { "id": "FHI_ry8-R0Yu" } }, { "cell_type": "markdown", "source": [ "### ファイルをオープンするときのモード\n", "open関数には、モードを2番目の引数として渡すことができます。\n", "\n", "~~~\n", "open(ファイルパス, モード)\n", "~~~\n", "\n", "モードには次の種類があり、\"r\"などといった形で文字列として指定します。\n", "\n", "|モード|意味|\n", "|---|---|\n", "|\"r\"|読み込み用で開く(デフォルト値)|\n", "|\"w\"|書き込み用で開く ファイルが存在する場合は上書き|\n", "|\"x\"|書き込み用で開く ファイルが存在する場合はエラー|\n", "|\"a\"|追加書き込み用で開く|\n", "|\"r+\"|読み込みと書き込みの両方ができる 書き込みは追加書きとなる ファイルが存在しない場合はエラー|\n", "|\"w+\"|読み込みと書き込みの両方ができる ファイルが存在する場合は上書き|\n", "|\"b\"|\"rb\"や\"wb\"という形で指定してバイナリモードで読み書き|\n", "\n", "これまでのソースコードの例ではオプションを指定していなかったため、デフォルトの\"r\"となっていました。" ], "metadata": { "id": "ZZ3Bsvm-Hlcu" } }, { "cell_type": "markdown", "source": [ "### ファイルの書き込み(writeメソッド)\n", "今度は、ファイルを書き込み用(w)で開いて書き込みを行ってみましょう。\n", "\n", "ここでは、「**writeメソッド**」を使って書き込みを行います。次のソースコードを見てみましょう。" ], "metadata": { "id": "kA8LBs_sSo9R" } }, { "cell_type": "code", "source": [ "with open(\"output.txt\", \"w\") as fobj:\n", " write_str =\"データを書き込みます\"\n", " fobj.write(write_str)" ], "metadata": { "id": "zASl77JXgCL4" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "output.txtは、プログラム実行開始前には存在していないファイルです。\"w\"モードでファイルをオープンすることは、書き込み用という意味だけでなく、ファイルの新規作成を行うことも兼ねています。\n", "\n", "writeメソッドの引数に文字列を指定すると、オープンしたファイルにその文字列が書き込まれます。Google Colaboratory画面左端のフォルダマークから、ファイルの中身を確認してみましょう。\n", "\n", "\n", "\n", "データが書き込まれていることが確認できました。\n", "\n", "なお、複数行書き込みたい場合の例を次に示します。" ], "metadata": { "id": "jegMbLuVgdTt" } }, { "cell_type": "code", "source": [ "with open(\"output.txt\", \"w\") as fobj:\n", " write_str = \"データを書き込みます1\"\n", " fobj.write(write_str + \"\\n\")\n", " write_str = \"データを書き込みます2\"\n", " fobj.write(write_str + \"\\n\")" ], "metadata": { "id": "Ls8CS-gCk8JB" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "注意していただきたいのは、改行を自分で指定する必要がある点です。改行は、文字列中に「**\\n**」と記述することで挿入できます。\n", "\n", "(Google Colaboratoryですと、通常の説明文では「バックスラッシュ」、コード例では「円記号」として表示されますが、実態は同じ文字です。)" ], "metadata": { "id": "vKyjE7VqltYG" } }, { "cell_type": "markdown", "source": [ "### 文字エンコーディングの指定\n", "カレントディレクトリに、テキストファイル「encoding_sample.jdx」というファイルを置いてあります。このファイルの中身は、次のような内容です。\n", "\n", "これまで使用した入力ファイル「open_sample.jdx」の内容に加え、最後の行にユーザー定義ラベルでオペレーターの名前が記録されています。\n", "\n", "~~~\n", "##TITLE= deg\n", "##JCAMP-DX= 5.01\n", "##DATA TYPE= NMR SPECTRUM\n", "##DATA CLASS= NTUPLES\n", "##NUM DIM= 1\n", "##ORIGIN= DELTA2_NMR\n", "##OWNER= delta\n", "##DATE= 2021/10/02\n", "##TIME= 10:29:30\n", "##SPECTROMETER/DATA SYSTEM= DELTA2_NMR\n", "##$OPERATOR= 髙橋太郎\n", "~~~\n", "\n", "この「encoding_sample.jdx」ファイルを読み込んでみます。" ], "metadata": { "id": "zKwrOqFll-mO" } }, { "cell_type": "code", "source": [ "with open(\"encoding_sample.jdx\") as fobj:\n", " txtdata = fobj.read()\n", " print(txtdata)" ], "metadata": { "id": "NGUixL9Bm4Zo" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "プログラムがエラーになってしまいました。\n", "\n", "実はencoding_sample.txtの内容は、文字エンコーディング(文字コード、文字エンコードなどと呼ぶこともあります。)が「CP932」です。\n", "\n", "Google Colaboratoryは、Linux上でPythonプログラムが動いています。LinuxのPythonでは、デフォルトでは文字エンコーディングが「UTF-8」だとみなしてファイルを読み込もうとします。その結果、UTF-8でない文字が出てきたためにエラーとなりました。\n", "\n", "LinuxでUTF-8以外の文字エンコーディングで記述されたテキストファイルを読み込む場合、open関数の引数に「**encodingオプション**」を指定します。次のソースコードを見てみましょう。" ], "metadata": { "id": "69z56ieMm_Hm" } }, { "cell_type": "code", "source": [ "with open(\"encoding_sample.jdx\", encoding=\"cp932\") as fobj:\n", " txtdata = fobj.read()\n", " print(txtdata)" ], "metadata": { "id": "KtZEgqj8nW5R" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "今度は、正しくファイルを読み込むことができました。\n", "\n", "文字エンコーディングは、初級者がつまづきやすい代表的なポイントです。次のようなことでハマりがちです。\n", "\n", "* Pythonでファイルを読み込もうとしたらエンコーディングエラーになった\n", "* Pythonで出力したCSVファイルをExcelで読み込んだら文字化けした\n", "\n", "1点目は「エンコーディングエラー」を示すメッセージが出てエラーとなることから、比較的、原因が分かりやすいです。\n", "\n", "しかし2点目は、とても分かりにくいです。Excelは、基本的に文字エンコーディングがCP932であるとみなしてファイルを読み込みます。\n", "\n", "UTF-8のファイルも「BOM付きUTF-8」という形式なら正しく読み込めるのですが、Pythonのopen関数のデフォルト値であるUTF-8とは別の形式であるため、明示的に「BOM付きUTF-8」でオープンする必要があります。\n", "\n", "文字エンコーディングの詳細については膨大な説明となってしまいますので割愛しますが、「**ファイルを読み書きするときは文字エンコーディングが何であるかを確認し、open関数のencodingオプションを適切に指定する**」という点だけは、必ず覚えておいてください。\n", "\n", "【ワンポイント】\n", "\n", "ちなみに「CP932」というのは、Microsoft社がShift-JISをベースに独自拡張した文字エンコーディングです。今回の例では、「髙 (はしご高)」がCP932の独自文字であり、標準のShift-JISには含まれない文字です。他にもよく出てくる独自文字には、「﨑 (立ち崎)」などもあります。\n", "\n", "Windowsの世界で「Shift-JIS」という言葉を使っていても、実は「CP932」を指していることがほとんどです。\n", "\n", "encodingオプションには\"shift_jis\"という指定もありますが、これは標準のShift-JISを示します。「髙 (はしご高)」を扱えない文字エンコーディングなので、前述のプログラムはエラーとなります。\n", "\n", "「Windowsで作られたShift-JISのファイル」は、encoding=\"cp932\"とすべきケースがほとんどでしょう。" ], "metadata": { "id": "a6N4h8hjns6X" } }, { "cell_type": "markdown", "source": [ "# パッケージによるCSVファイルの読み書き\n", "ここでは、外部パッケージによるCSVファイルの読み書きについてご紹介します。\n", "\n", "取り上げるのは、「**NumPy**」「**pandas**」という2つのパッケージです。それぞれの特徴を、次にまとめます。\n", "\n", "* NumPy\n", " * 「行列計算(行列和・行列積など)」を得意とするパッケージです。\n", "* pandas\n", " * 「配列データの整形・加工」を得意とするパッケージです。\n", "\n", "データ構造化プログラムでは、機器が出力したデータなど、CSVファイルの入出力を行うことがとても多いです。\n", "\n", "これらのパッケージを使って読み書きをすると、CSVファイルの内容を自動的に多次元のリスト(リストのリスト、入れ子のリスト)として扱うことができるなど、ここまでにご紹介したPython標準関数と比べて非常に便利です。\n", "\n", "ちなみに、NumPyとpandasどちらも配列データを扱うパッケージということから、どのように使い分ければよいのか、初級者は悩みがちです。\n", "\n", "前述の説明のとおり「行列計算をしたいのか」「データの整形・加工を行いたいのか」という観点で、適切なパッケージを選択しましょう。\n", "\n", "特にデータ構造化プログラムでは、機器が出力したCSVデータを整形・加工してメタデータとして登録するなどの目的で、pandasをよく利用します。" ], "metadata": { "id": "a7pn8FcedNOX" } }, { "cell_type": "markdown", "source": [ "## NumPyでのCSVファイル読み書き\n", "NumPyでCSVファイルの読み書きをやってみましょう。" ], "metadata": { "id": "MLB9gjaEdk-O" } }, { "cell_type": "markdown", "source": [ "### モジュールのインポート\n", "NumPyを利用するには、今回の講義でご紹介したimport文を使って、NumPyのモジュールをインポートします。モジュール名は小文字の「numpy」なので、間違えないようにしてください。\n", "\n", "なおNumPyをインポートするときは、asを使って「np」という別名を付けるのが慣例になっています。Pythonプログラマの共通認識のようなものですので、慣例に倣うようにしてください。\n", "\n", "~~~\n", "import numpy as np\n", "~~~" ], "metadata": { "id": "nz7-WQQreWxf" } }, { "cell_type": "markdown", "source": [ "【注意事項】\n", "\n", "Google ColaboratoryやAnacondaでは、NumPyが最初からインストールされているのでimport文だけで利用できます。公式版Pythonでは最初はNumPyがインストールされていませんので、プログラムを作成する前に、あらかじめpipコマンドを使ってインストールしてください。\n", "\n", "~~~\n", "pip install numpy\n", "~~~" ], "metadata": { "id": "KazzlWmFxCAM" } }, { "cell_type": "markdown", "source": [ "### CSVファイルの読み込み\n", "NumPyでCSVファイルを読み込むには、loadtxt関数を使います。\n", "\n", "今回は、次のような内容のCSVファイルを読み込みます。\n", "\n", "
| 1 | 2 | 3 |
| 4 | 5 | 6 |
| 7 | 8 | 9 |