DS JUNKSTAブログ

元文系院卒が独学で取り組むDS(データサイエンス)、その有象無象のアウトプット、DSは本当に楽しい

【Streamlit/Python】キャッシュ機能を利用してデータを読み込む

PythonのWebアプリケーション・フレームワークの一つであるStreamlitを用いて、業務で使用するデータ可視化ツールを作成している。
Streamlitの開発では、ローカルサーバーにつないでブラウザ上でアプリケーションの見え方を確認し、
コードを変更するごとにブラウザを更新することになるが、めちゃくちゃ重いデータセットを扱う際、
キャッシュ機能を利用するととても便利だったので以下をまとめる。


関数に @st.cache

データセットを読み込む関数の前に@st.cacheをつけるだけ!


@st.cache
def import_tb_dataset(dataset):
	df = pd.read_csv(r'{}'.format(dataset))
	return df


ただし、元となるデータに変更があった場合、再読み込みで時間がかかることになるので注意。
キャッシュが利用できなかった場合エラーメッセージなどオプションが設定できるが、詳細は👇の公式ドキュメントを参照のこと。

ブラウザを更新する度にいちいちクソでかビッグデータを読み込むのは本当に煩雑なので、とても助かる。

【RStudio/Mac】基本的な操作まとめ(ディレクトリの確認/ファイルの読み込み/値の抽出)

最近、『Rによるやさしい統計学』を読みながらR言語とRStudioに触れており、基本的なRStudioでのファイル読み込みやデータの抽出などでつまづいたいくつかの点をまとめる。
以下のコマンドはMac OS上でRStudioを操作する場合のものであることをご了承ください。


エラーメッセージの言語を英語にする

consoleで以下を実行

Sys.setenv(LANG="en")


せっかく日本語で表示されるのはいいのだけれど、出力されるエラーメッセージを検索するなら英語の方がGoogle検索しやすいので英語に設定する。
検索するほどRを使えていないのだけれど、今後のためにも。


現在のディレクトリの確認/作業ディレクトリの移動

RStudioではconsole上でディレクトリの移動ができる。
読み込むファイル(後述)のパスの書き方に関わるので、適切なディレクトリに移動する必要がある。

現在のディレクトリの確認
getwd()
# [1] "/Users/<username>"

get working directoryのことだと思われる。
linuxコマンドではpwdに該当する。

作業ディレクトリの移動
setwd("~/Desktop/Rによるやさしい統計学")

""のなかに絶対パスを記述。
linuxコマンドではcdに該当する。


ちなみに dir()関数 を使ったら、現在のディレクトリの中身を確認できる。

dir()
# 実行結果
[1] "shidouhouU8.csv"

csvファイルの読み込み

読み込みたいディレクトリに 移動してから、consoleで以下を実行

df <- read.csv('shidouhouU8.csv')

dfとだけ実行すると結果が表示される

df
# 実行結果
   SID   name sex math stat psych_test stat_test1 stat_test2 method
1    1   大村  男 嫌い 好き         13          6         10      C
2    2   本多  男 嫌い 好き         14         10         13      B
3    3   川崎  男 好き 好き          7          6          8      B
4    4   多村  男 好き 好き         12         10         15      A
5    5   松中  男 嫌い 嫌い         10          5          8      B
6    6 小久保  男 嫌い 嫌い          6          3          6      C
7    7   柴原  男 嫌い 嫌い          8          5          9      A
8    8   井手  男 嫌い 嫌い         15          9         10      D
9    9   田上  男 嫌い 嫌い          4          3          7      D
10  10   松田  男 好き 嫌い         14          3          3      D
11  11   高谷  女 好き 好き          9         11         18      A
12  12   杉内  女 嫌い 好き          6          6         14      A
13  13   和田  女 好き 好き         10         11         18      A
14  14   新垣  女 嫌い 嫌い         12          9         11      C
15  15   大隣  女 嫌い 好き          5          7         12      B
16  16   水田  女 好き 嫌い         12          5          5      D
17  17   斉藤  女 嫌い 嫌い          8          8          7      C
18  18   柳瀬  女 嫌い 嫌い          8          7         12      C
19  19   佐藤  女 嫌い 嫌い         12          7          7      B
20  20   馬原  女 嫌い 嫌い         15          9          7      D

読み込んだデータフレームから特定のカラムを抽出する

mathのカラムの値だけを取り出したい場合は以下を実行

> df$math
# 実行結果
 [1] "嫌い" "嫌い" "好き" "好き" "嫌い" "嫌い" "嫌い" "嫌い" "嫌い" "好き" "好き" "嫌い"
[13] "好き" "嫌い" "嫌い" "好き" "嫌い" "嫌い" "嫌い" "嫌い"

データフレームの変数と指定するカラム名の間に「$」をつける。
そのまま変数に格納することももちろん可能

> math <- df$math

読み込んだデータフレームのカラムごとにフィルターをかける

データフレームの中でsexカラムが「男」の行だけを抽出する場合は以下を実行

> df[df$sex=="男",]
# 実行結果
   SID   name sex math stat psych_test stat_test1 stat_test2 method
1    1   大村  男 嫌い 好き         13          6         10      C
2    2   本多  男 嫌い 好き         14         10         13      B
3    3   川崎  男 好き 好き          7          6          8      B
4    4   多村  男 好き 好き         12         10         15      A
5    5   松中  男 嫌い 嫌い         10          5          8      B
6    6 小久保  男 嫌い 嫌い          6          3          6      C
7    7   柴原  男 嫌い 嫌い          8          5          9      A
8    8   井手  男 嫌い 嫌い         15          9         10      D
9    9   田上  男 嫌い 嫌い          4          3          7      D
10  10   松田  男 好き 嫌い         14          3          3      D


「$」以外は基本的にPythonのpandasと同じような絞り込みが可能のようだが、「,」のつけ忘れに注意。


比較演算子を用いて特定の数値の絞り込みももちろん可能

> df[df$psych_test>10,] #psych_testが10より大きいデータフレームだけ抽出
# 実行結果
   SID name sex math stat psych_test stat_test1 stat_test2 method
1    1 大村  男 嫌い 好き         13          6         10      C
2    2 本多  男 嫌い 好き         14         10         13      B
4    4 多村  男 好き 好き         12         10         15      A
8    8 井手  男 嫌い 嫌い         15          9         10      D
10  10 松田  男 好き 嫌い         14          3          3      D
14  14 新垣  女 嫌い 嫌い         12          9         11      C
16  16 水田  女 好き 嫌い         12          5          5      D
19  19 佐藤  女 嫌い 嫌い         12          7          7      B
20  20 馬原  女 嫌い 嫌い         15          9          7      D


sexが男のstat_test1の列を抽出したい場合は以下の通り

> df[df$sex=="男",]$stat_test1
# 実行結果
 [1]  6 10  6 10  5  3  5  9  3  3

【Jupyter lab】%autoreload 1 と %autoreload 2 の違い

Jupyter labのnotebookでその他のモジュール(.pyファイル)をオートリロードするには、以下を実行すればいい。

%load_ext autoreload
%autoreload 2
import (モジュール名)

しかし、%autoreload 2 だけでなく、%autoreload 1 もあったりしたため、本記事ではその違いを整理する。

%autoreload 1

IPythonの公式ドキュメントには、以下のように書いている。

Reload all modules imported with %aimport every time before executing the Python code typed.
(試訳)Pythonコードを実行するごとに%aimportでインポートされた全てのモジュールをリロードする

つまり、%aimport でインポートしたモジュールが対象に、notebookの実行ごとにオートリロードされることになる。
ただしimport でインポートしたモジュールはオートリロード対象外。

%load_ext autoreload
%autoreload 1
%aimport some_function #←オートリロード対象
import some_function #←オートリロード対象外



%autoreload 2

公式ドキュメントでは以下の通り。

Reload all modules (except those excluded by %aimport) every time before executing the Python code typed.
(試訳)Pythonコードを実行するごとに全てのモジュール(%aimportで除外されたモジュールは除く)をリロードする

いろいろ試してみたところ、importだけなく%aimport でインポートしたモジュールもオートリロード対象。

%load_ext autoreload
%autoreload 2
%aimport some_function #←オートリロード対象
import some_function #←オートリロード対象

ただし、

%aimport -(モジュール名)

のようにモジュールを指定して実行した時点で、オートリロードがストップされる。

%aimport -some_function #←指定したモジュールがオートリロード対象外になる

これを実行した時点から、オートリロード対象外になる。使い道は不明。


これらの他に%autoreload 3もあるのだが、%autoreload 2 との違いがわからなかったので今回は説明を省いた。

参考

IPython公式ドキュメント
autoreload — IPython 8.3.0 documentation

【Python】Jupyter labでnotebook外のモジュールを呼び出す時の設定

Jupyter labでコーディングをしているとnotebookへの記述が溜まってしまうので、
よく使う関数は別の.pyファイル内にまとめておいて、呼び出す必要が出てくる。

本記事では、notebook外のpy.ファイルから関数を呼び出す際の設定方法をまとめる。

結論

notebookで、以下の通りモジュール(関数をまとめた.pyファイル)を呼び出す設定を実行しておく

%load_ext autoreload
%autoreload 2
import (モジュール名)

 

説明

モジュールのオートリロードの設定
%load_ext autoreload
%autoreload 2

この記述で、importしたモジュール(.pyファイル)が変更⇨保存するたびにモジュールをオートリロードしてくれる。
notebookで実行するたび、モジュール内の関数の変更内容が自動で反映されていく。


例えば、作業中のnotebookと同じディレクトリ内にsome_function.pyという関数用のモジュールを準備している場合。

autoreloadとimportの設定
%load_ext autoreload
%autoreload 2
import some_function


some_function.py では↓のようにデフォルト引数を設定したtriangle関数を記述している。

def triangle(height=300, base=100):
    return (height*base)/2


notebookでの実行方法は↓

(モジュール名).(関数名)

で実行。

some_function.triangle()
# 15000.0

このようにモジュールの関数から値が返る。


関数のデフォルト値を変更した場合…

# height=300⇨20, base=200⇨10
def triangle(height=20, base=10):
    return (height*base)/2

notebookで同関数を実行すると、↓のように関数の変更内容が正しく反映されている。

some_function.triangle()
# 100.0

【Python】リスト内包表記(list comprehension)

Pythonでは「リスト内包表記」を用いたら、
リストの中でfor文でイテラブルオブジェクトを回して新しいリストを生成できる。
「リスト内包表記」は英語で list comprehension 。

for文を用いたリスト内包表記の基本形
[処理 for 変数 in イテラブルオブジェクト]

例えば、

squares = []
for x in range(10):
    squares.append(x**2)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

のように複数行で書かれるfor文のコードは、

squares = [x**2 for x in range(10)]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

の1行でかける。

参考にさせていただいたWEB記事

Pythonリスト内包表記の使い方 | note.nkmk.me

【Python】関数におけるparameter(仮引数)とargument(実引数)【基礎の基礎の基礎】

Pythonで具体的なコードを読んだり書いたりしているとあまり気づかないけれど、いざ出たエラー文を実際に英語を訳してみようとすると、あれparameterって?argumentって?と認識があいまいだったので、その知識の整理。

argumentってなんだっけ…?じゃあparameterは…?

*基礎の基礎の基礎の内容の記事です

結論

関数の呼び出し元から渡される値が「実引数:argument
受け取り側の変数が「仮引数:parameter


だから、

TypeError: get_triangle() missing 1 required positional argument: 'height'

を翻訳すると、

TypeError: get_triangle()には本来あるはずのキーワード引数'height'が足りてません

となる。
positional argumentは「キーワード引数」で、呼び出し時に「仮引数=値(実引数)」の形で呼び出すことができる引数のこと。

参照:

仮引数と実引数(京都産業大学/山田 修司先生/アドバンスド プログラミング より)
cc.kyoto-su.ac.jp/~yamada/ap/parameter_argument.html

ベルヌーイの微分方程式における"合成関数の微分"

ベルヌーイの微分方程式とは、1階線形微分方程式の応用系の解法の一つ。

 

マセマ『常微分方程式キャンパスゼミ』のP.44、ベルヌーイの微分方程式を用いた解法の解説より。

ベルヌーイの微分方程式では、

 y' = + P(x)y = Q(x)y^n …①   (n ≠ 0, 1)

 y ≠ 0 として、①の両辺に  (-n+1) \cdot y^{-n}をかけて、

 (-n + 1)y^{-n} \cdot y' + (-n + 1)P(x)y^{-n+1} = (-n + 1)Q(x) という形にする。

ここで、合成関数の微分 (y^{-n+1})' = (-n + 1)y^{-n} \cdot y'とできることを利用して、

 (y^{-n+1})' + (-n + 1)P(x)y^{-n+1} = (-n + 1)Q(x) …①'

という形にし、①'に y^{-n+1} = uとしてuの1階線形微分方程式の解の方程式が使えるような形にしていくが、合成関数の微分でなぜ (y^{-n+1})' = (-n + 1)y^{-n}  \cdot y'となるのか少しつまづいたので、合成関数の微分を振り返りながら整理する。

 

合成微分  (y^{-n+1})' = (-n + 1)y^{-n} \cdot y'

 t = y^{-n+1}とすると、

 (y^{-n+1})' = (t)'となるが、そもそも (y^{-n+1})'はxでの微分を意味するので、

 (t)' = \frac{dt}{dx}を意味する。

ここで、合成関数の微分として考えると、

 \frac{dt}{dx} = \frac{dt}{dy} \cdot \frac{dy}{dx}となるが、

 \frac{dt}{dy} = (y^{-n+1})' = (-n+1)y^{-n} および  \frac{dy}{dx} = y' なので、

 \frac{dt}{dx} = \frac{dt}{dy} \cdot \frac{dy}{dx} = (-n+1)y^{n} \cdot y'となる。

つまり、 (y^{-n+1})' = (-n+1)y^{n} \cdot y'と表せるので、ベルヌーイの微分方程式では  y^{-n+1} = u として u微分方程式を解いていけることになる。

 

参考にさせていただいたサイト:

ベルヌーイ形の微分方程式(高校数学の基本問題)