Pythonの使い方ノート
プログラミング言語Pythonについて勉強したノートです。
for, if, range, 配列など入門レベルの内容を整理しました。
下の灰色のボタンで内容の絞り込みができます。
基本
心がけ
コードが相互に影響することを避ける。
機能分野毎にファイルを分ける。
1つの関数に1つの機能。
機能が分かる名前を付ける。
繰り返す処理は関数にする。
サードパーティライブラリを避け、利用しない機能を削除する。
データを複数のコードで使う時はglobal関数、file、DataBaseに格納する。
小数の計算を繰り返すと誤差が大きくなる(0.1*3など)。
PATHを通す
PATHを通すことで、実行ファイルのフルパスを指定せずに実行できる。
ファイルが見つからないエラーが出た時、PATHの設定ができていない場合も多い。
Windowsでは「環境変数」、Macではzshなら「.zshrc」、bashなら「.bash_profile」で設定する。
bashとzshはどちらもUNIX系OSで使われるシェル(シェルはユーザーとOSの橋渡しをするプログラム)。
bashよりzshの方が新しく、zshはbashの機能を拡張している。
シェルの種類は echo $SHELL で確認できる。
pyenvとvenv
pyenvはPythonのバージョンを管理するツール。
複数のバージョンを共存させて簡単に切り替えることができる。
venvは仮想環境を作成するツール。Python3.3以降の標準ライブラリ。
仮想環境を使って、Pythonの実行環境をプロジェクト毎に分けて管理する。
Git
ソースコードのバージョン管理システム。
いつ、誰が、どうファイルを変更したのか記録できる。
コミットで変更を記録し、簡単に過去のバージョンに戻れる。
ブランチ機能でメインコードに影響を与えずに新機能の開発や修正ができる。
Jupyter Notebook/ JupyterLab
Pythonなどのプログラムを書いて・実行して・見せられるツール。
JupyterLabはJupyter Notebookの発展版。
・プログラムコード、実行結果、説明分などを1つのドキュメントにまとめることができる。
・セル単位でコードを書き、実行結果を確認できる。
・グラフや表の簡単に表示できる。
・.ipynb形式でファイル保存して共有できる。
表データ
ヘッダー(横方向): 通常一番上にある項目名
インデックス(縦方向): 通常一番左にある番号など
行・レコード・ロウ(横方向): 1件のデータ
列・カラム(縦方向): データが持つ項目(データが持つ要素の種類)
要素・フィールド : 各マス。エクセルではセル
データフレーム : 2次元の表データ
シリーズ : 1次元のデータ
変数の説明コメント
"""
:param 変数名: 変数の説明
:type 変数名: str/int/datetimeなど
:return: 戻り値
"""
エラー処理
try:
処理
except 例外1:
例外処理
except 例外2 as e: # 変数eが例外オブジェクトを受け取る
例外処理
else:
例外が発生しなかった時の処理
finally:
例外の有無に関わらず行う処理
使ったファイルを自動で閉じる
with open('ファイル名') as 変数名:
data = 変数名.read()
pip
pip install ライブラリ名==バージョン # インストール/ダウングレード
python -m pip install ライブラリ名 #Pythonのバージョンごとに管理したい場合
pip install -U ライブラリ名 # バージョンアップ
pip uninstall ライブラリ名 # アンインストール
pip list # インストール済みライブラリ
pip list --outdated # 現verと最新verの比較
pip show ライブラリ名 # 依存するライブラリを確認
日本語フォント
Windows : "Meiryo"
macOS : "Hiragino Maru Gothic Pro"
Colab Notebook : "IPAexGothic"
演算
算術
8 // 5 # 1: 切捨て除算
8 % 5 # 3: 剰余
3 ** 2 # 9: べき乗
+ と * は文字列にも使える。
置き換え演算子
a *= 2 # a = a * 2
比較
x == y # 値が同じならTrue(型が違えばFalse)
x === y # 値も型も同じならTrue
x != y # 値が等しければFalse
x > y > z # zより大きくxより小さければTrue
x is y # オブジェクトが同じならTrue
x is not y # オブジェクトが同じならFalse
x in リスト名 # リストにxが含まれるならTrue
x not in 辞書名 # 辞書のキーにxが含まれるならFalse
リスト
見やすい書き方
リスト名 = [
'A', # 1行ずつ改行
'B', # 最後のデータにも,を付ける
]
追加と削除
リスト名.append()
リスト名.remove()
多重代入
変数A, 変数B, 変数C = リスト名(or タプル)
インデキシング(indexing)
リスト名[-1] # 一番最後
スライシング(slicing)
リスト名[0:3] # [1番目, 2番目, 3番目]
リスト名[:-2] # [後ろから2番目, 一番最後]
リスト名[start:end:step]
リスト名[::-1] # リストを逆転させる
# スライシングのイメージ図
strings = "abcde"
| a | b | c | d | e |
0 1 2 3 4 5
-5 -4 -3 -2 -1
strings[1:3] # bc
strings[-5:-2] # abc
内包表記
用途: リストを作るためにループを使う。
x = [i*5 for i in range(3)]
y = [i for i in range(10) if i%2==0] # 0~8までの偶数リスト
# dataframeの値を収納した多重リスト
z = [[df.iat[i, j] for j in range(len(columns)] for i in range(len(df))]
[x.upper() for x in strings if len(x) > 2]
# 以下と同じ
result = []
for x in strings:
if len(x) > 2:
result.append(x.upper())
タプル
不変のリスト
t = (35.0, 130.0)
用途: 緯度経度。
辞書
キーと値をセットにしたリスト
dict = {キー1: 値1, キー2: 値2}
# キーの取り出し
dict.keys()
条件式
if not 変数: # 空の場合にTrue(None, False, 0, "", [] など)
elif 条件:
else:
処理速度
組み込み関数 > for > while
forループをmap(), filter(), functools.reduce()で置き換える。
標準ライブラリを使う。関数内部でループする。
ndarrayとufunc、リスト内包表記やジェネレータを利用する。
変数は必要な時だけに使う。
global変数をループの中で使う場合はlocal変数にコピーする。
置き換え演算子を使う。
ループ
リストの中を順番に取り出す
for x in リスト名:
用途: 1つの配列を処理する。
辞書のキーを1つずつ取り出す
for k in 辞書名:
辞書のキーと値をセットで1つずつ取り出す
for k, v in 辞書名.items():
整数でループ
for i in range(10) # 0~9まで繰り返し
for i in range(1, 10) # 1~9まで繰り返し
for i in range(1, 10, 2) # 1~8まで1つ飛ばしで繰り返し
用途: 複数の配列を処理する。
数字を順に増やしつつ、リストの中を取り出す
for i, x in enumerate(リスト):
# i:0, x:リスト[0]
# i:1, x:リスト[1]
やっていることは以下と同じ
i = 0
for x in リスト:
i += 1
複数のリスト等から値を順に取り出し、1セットずつ返す
for x, y in zip(range(2), range(1, 6, 2)): # 要素数に差があるときは、少ない方の数まで繰り返し。
繰り返しを条件で決める
while 条件式:
ファイル処理
処理速度
書き込み: HDF5 >> SQL > CSV >> Excel
10 : 90 : 120 : 1000
読み込み: HDF5 >> CSV > SQL >> Exce
1 : 10 : 20 : 500
CSVの文字コード
UTF-8
PNGとJPEGをWEBPに変換する
from PIL import Image
import os
import glob
import shutil
# 入力フォルダと出力フォルダの指定
input_dir = "./input_sample/"
output_dir = "./output_sample/"
backup_dir = "./backup_sample/"
# 出力フォルダが存在しないときは作成
if not os.path.exists(output_dir):
os.makedirs(output_dir)
# 他の方法(from pathlib import Path が必要)
# output_path = Path(output_dir)
# output_path.mkdir(parents=True, exist_ok=True)
# 入力フォルダ内のファイルを取得する
f_list = os.listdir(input_dir)
for f_name in f_list:
# ファイルのパスを取得する
input_path = os.path.join(input_dir, f_name)
# ファイルの拡張子を確認し、PNGかJPEGなら変換する
if os.path.isfile(input_path) and f_name.lower().endswith((".png", ".jpg", ".jpeg")):
# 出力ファイルのパスを作成する。新しいファイル名は旧ファイル名をそのまま使う。
output_path = os.path.join(output_dir, os.path.splitext(f_name)[0] + '.webp')
# [1]はファイル名の拡張子(.pngなど)、[0]は拡張子なしのファイル名
# (ファイル名の一部を取り出す場合は os.path.splitext(os.path.basename(path))[0][5:])など)
# 画像を開いてWebP形式で保存する
with Image.open(input_path) as image:
# リサイズして変換する
width, height = image.size
new_width = 600
new_height = int(height * (new_width / width))
image.resize((new_width, new_height)).save(output_path, 'webp')
print(f'完了: {f_name} -> {os.path.basename(output_path)}')
# 変換終了後に変換前のファイルをバックアップ用のフォルダに移動(例は.png拡張子のファイルみ)
move_list = glob.glob(input_dir + "*.png")
for f in move_list:
shutil.move(f, backup_dir)
# 他の方法(from pathlib import Path が必要)
# f_path = Path(input_dir + "*.png")
# f_path.replace(f"backup_dir{f_path.name}")
関数
def 関数名(引数, 引数, キーワード引数=値)
処理
return 戻り値
# キーワード引数: 指定されなかったときの初期値
ラムダ関数
値を返すような処理を一文で定義して書く関数
ラムダ関数の例
list = [1, 2, 3, 4]
list2(list, lambda x: x * 2)
# [2, 4, 6, 8]
組み込み関数
format
文字列
"{}は{}年生まれ".format(who, when)
"{:>5}".format("aaa") # aaa(右寄せ5文字幅)
"{:b>5}".format("aaa") # bbaaa(空白を文字で埋める)
整数
"{:03d}".format(10) # 010(3桁で空白は0埋め)
"{:.0f}".format(0.6) # 1(小数点以下偶数への丸め: 0.5まで切り捨て)
"{:3d}".format(10) # 10(最小3桁の幅)
"{:<5,.2f}".format(123) # 左寄せ 最小幅5桁 コンマ付き 小数点以下2桁
小数
"{:.2f}".format(0.1) # 0.10
format(0.1, ".2f") # 0.10
"{:.1e}".format(0.01) # 1.0e-02
"{:5.3f}".format(0.01) # 0.010(最小5桁の幅)
"{:.2g}".format(1.23) # 1.2(有効数字2桁)
"{:.3g}".format(1.2) # 1.2(有効数字3桁 0非表示)
"{:#.3g}".format(1.2) # 1.20(有効数字3桁 0表示)
符号・コンマ
"{:+}".format(1) # +1
"{:+.2f}".format(0.1) # +0.10
"{:,}".format(1000) # 1,000
"{:.2%}".format(0.12345) # 12.35%(%に変換して小数点以下2桁)
split, strip
## .split()
bunsyou = "A is B"
words = bunsyou.split()
# ['A', 'is', 'B']
## .split(区切り文字)
text = "きゅうり,かっぱ,皿"
words = text.split(",") コンマで分割
# ['きゅうり', 'かっぱ', '皿']
## .split(区切り文字, 分割回数) 分割回数を省略すると全部対象になる
content = "【タイトル】文章。"
parts = content.split("】", 1) 最初の 】で1回だけ分割
# ['【タイトル】', '文章']
## .splitlines() 文字列を改行毎に分割してリストにする
text = "きゅうり\nかっぱ\n皿"
lines = text.splitlines()
# ['きゅうり', 'かっぱ', '皿']
.strip() 文字列の前後にある空白や改行を削除する。
round
整数
round(1.56) # 2(四捨五入)
小数
round(1.56, 1) # 1.6(小数点以下1位)
標準ライブラリ
re(Regular Expression:正規表現)
import re
matches = re.findall(r"(台風第\d+号.*?(?:上陸|通過).*?。)", content)
以下解説
re.findall 該当する文章を全部拾う
content 検索する文章を入れる
\d 数字(0〜9)
+ 直前のパターンが1回以上繰り返される
\d+ 1桁以上の数字
. 任意の1文字(改行は除く)
* 直前のパターンを0回以上繰り返し
? 最短一致
.*? できるだけ短く任意の文字列にマッチ
.*?。 句点(。)が出てくるまで任意の文章を取る
(...) グループ化。この文章全体が一つのマッチとして処理される
?: キャプチャしないグループ
上陸|通過 "上陸"または"通過"を捕捉する
(?:上陸|通過) "上陸"または"通過"を捕捉するが、取り出しはしない
re.findall(pattern, string) 全マッチをリストで返す
re.search(pattern, string) 最初にマッチした部分を返す
re.sub(pattern, repl, string) 置換
re.match(pattern, string) 文字列の先頭からマッチするか確認
re.compile(pattern) パターンを事前にコンパイルして使い回せる
datetime
from datetime import datetime
from datetime import timedelta
import pytz
datetime.now() # 現在時刻
datetime.now().year # 今年。month, day, hour, minute, second
datetime.now(pytz.utc) # UTC
datetime(2023, 1, 2, 14, 5, 3, 0) # 2023/1/2 14:05:03
datetime.now().strftime("%Y年%m月%d日%H%M%S") # 2023年01月02日14時05分03秒
(%y: 西暦2桁、%I: 12時間表記)
datetime(2023, 1, 2) + timedelta(days=1) # 1日進める: 2023/1/3
datetime(2023, 1, 2, 12) + timedelta(hours=2) # 2時間進める: 2023/1/2 14時
time.pref_counter()
パフォーマンスカウンター。
スリープ中の経過時間も含まれる。
import time
start = time.perf_counter() # 計測開始
処理内容
end = time.perf_counter() # 計測終了
print('{:.2f}'.format(end-start)) # 小数点以下2桁の秒
自動化(macでcronを利用する)
1. cronにフルディスクアクセスの権限を与える
システム設定
→ プライバシーとセキュリティ
→ フルディスクアクセス
→ +(追加)
→ shift + command + g(同時に押す)
→ /usr/sbin/cron(と入力して検索)
→ cronを選択(追加)
2. cronの設定を追加
ターミナルでcronを追加する
$ crontab -e
エディタが立ち上がったらINSERTモードに変更
i
設定内容を書く(一例)
30 09 * * 5 /usr/[ディレクトリ名]/[ファイル名.py]
エディタをノーマルモードに変更
esc
設定を保存して終了
:wq
crontabのオプション
-e cronを設定するエディタを起動
-l 設定済みのcronを表示
-r cronを全て削除
vim(エディタ)の使い方
i 入力モードに変更(カーソルのある部分で挿入)
esc ノーマルモードに戻る
:w 内容を保存
:q 終了
:wq 保存して終了
crontabの書き方
30 09 * * 5 (絶対パス)/sample.py
(金曜日の9時30分にsample.pyを実行)
分 時 日 月 曜日 [実行するファイル名(フルパス)]
* 全てを対象とする
*/1 時の場合は1時間ごと
1-3,5 時の場合は1,2,3,5時
曜日 0:日, 1:月, 2:火・・・5:金, 6:土
crontabでdateを使って今日の年月日の入ったディレクトリを作る
(crontabでは%をエスケープする必要がある)
mkdir [親ディレクトリ]/$(date +"\%Y\%m\%d")
協定世界時で後ろに文字列を付け足す場合
mkdir $(TZ=UTC date +"\%Y\%m\%d")[文字列]
cronログ(標準)
ログの出力先
/var/mail/[ユーザ名]
ログをリアルタイムで監視
cd /var/mail
(ログのあるディレクトリに移動)
tail -f [ログファイル名]
(終了するときは ctrl + c)
crontabで実行中のプログラムを停止
実行中のプログラムを確認
ps aux | grep [プログラム名]
プロセスを停止する
kill [PID]
cronでシェルを実行する
誤って『crontab -r』と入力するとcronの内容が削除されてしまう
これを回避するためにcronで実行したい内容を.shファイルに記入する
cronの内容
30 09 * * * (絶対パス)/[ファイル名].sh
シェルの例
#!/bin/bash
(絶対パス)/python3 (絶対パス)/[実行ファイル].py >> (絶対パス)/[ログファイル].log 2>&1
>> ログを追記
> ログを上書き
2>&1 標準エラー出力を標準出力と同じ場所に出力
権限を確認
ls -l
シェルに実行権限を与える(crontabで実行できるようにするため)
chmod 744 [ファイル名].sh
JSON
数値: 整数、小数、指数(1.23e4)
NaNは使えない
真偽値: true, false(小文字で記述)
値なし: null(PythonではNone)
書き込み
import json
items = [
{"a1-key": "a1-value", "b1-key": 10},
{"a2-key": "a2-value", "b2-key": 11}
]
with open('ファイル名.json', 'w', encoding='utf-8') as fp:
json.dump(items, fp, indent=4) # 4つ空白を入れて整形
CSVを読み込んでJSONに変換
import json, csv
file_in = 'ファイル名.csv'
file_out = 'ファイル名.json'
# csvファイルの読み込み
items = []
with open(file_in, 'r', encoding='utf-8') as fp:
reader = csv.reader(fp)
for i, row in enumerate(reader):
if i < 2: continue # 2行読み飛ばす
# 各列の変数
retu1, retu2, retu3 = row
items.append({
'列1': retu1,
'列2': retu2,
'列3': retu3
})
# JSONに変換
json_s = json.dumps(items, indent=4, ensure_ascii=False)
# 書き出し
with open(file_out, 'w', encoding='utf-8') as fp:
fp.write(json_s)
時系列予測
ベースとなるモデルの作成
例1:平均値
例2:最後の実測値
平均絶対誤差率(MAPE)が小さいほど良いベースモデル
import pandas as pd
import numpy as np
# データの読み込み
df = pd.read_csv('csvファイル')
# 訓練データ
train = df[:-10] # 最後の10個を除く実測値
# テストデータ
test = df[-10:] # 最後の10個の実測値
# 例1:平均値のみを使ったベースモデル
test.loc[:, 'rei_1'] = np.mean(train['data']) # この例では'data'は実測値が格納されている列名
# 例2:最後の実測値を使ったベースモデル
test.loc[:, 'rei_2'] = train.data.iloc[-1] # この例では data は実測値が格納されている列名
def mape(実測値, 予測値):
return np.mean(np.abs((実測値 - 予測値) / 実測値)) * 100