- 投稿日:2020-08-30T23:51:57+09:00
幅優先探索(BPF)多分理解した(python)
参考記事
この記事は、pythonでBFSを検索すると上位にでてきます。
しかし、私にとって一部わかりにくい部分があり、あと、多分ですが、説明に一部間違いがあったと思われる。
ですので、私の理解したことを記事にしようと思いました。書き換えた
BPF完全に理解した状況で、それを記録しました。
from collections import deque n, m = map(int, input().split()) graph = [[] for _ in range(n+1)] for i in range(m): a, b = map(int, input().split()) graph[a].append(b) graph[b].append(a) #print(graph) ⇦① dist = [-1] * (n+1) dist[0] = 0 dist[1] = 0 #print(dist) ←② d = deque() d.append(1) #print(d)⇦③ while d: #print(d)⇦④ v = d.popleft() #末尾から要素を1つ「返して」、「dからはその要素を消す」しょっぱなのv=1⇦⑤ #print(v) for i in graph[v]: #しょっぱなは、graph[1]=[2]なので、v=1,i=2です。⇦⑥ #print(i)⇦⑦ #print(graph[i])← if dist[i] != -1: #print(i)⇦⑧ continue else: #!!! #print(i)⇦⑨ #print(v) #しょっぱなのv=1,i=2 ⇦⑩ dist[i] = dist[v] + 1 #elseなのか? #◯!!! d.append(i) #i=2で探索開始 #◯!!! ans = dist[1:] #print(*ans, sep="\n")関門
一番きついのは、 #!!!で記載した部分の、[else]の部分が参考文献では記述されていないことです。
多分elseだと思います。。。これがない書き方もあるんでしょうが、if elseになれた目では理解が難しかったです。
①、②から、
graph
[[], [2], [1, 3, 4], [2, 4], [3, 2]]
dist
[0, 0, -1, -1, -1]⑤のpopleft()は「dから消す」と思い込んでましたが、「dから消して、vに入れる」です。
⑥ですが
⑤でv=1になったので、graph[1]=[2]がiに代入されます。つまり、i=2です。if~elseですが、 ifが実行される時は⇨、elseが実行されるときは←として、iとgraph内の配列を記述すると
2 ←
[1, 3, 4]
1 ⇨
[2]
3 ←
[2, 4]
4 ←
[3, 2]
2 ⇨
[1, 3, 4]
4 ⇨
[3, 2]
3 ⇨
[2, 4]
2 ⇨
[1, 3, 4]◯!!!ですが、しょっぱなの場合は
dist[2]=dist[1]+1 つまり、[1]から1つだけ離れているので、距離が記録されます。
d.append(i)で次のvの探索が進みます。参考文献の間違いだと思われる箇所
4行目は、distの初期化(#5)でdist[0]とdist[1]以外は-1と定義したので、「dist[i]==1であれば未>訪問(このwhile文の中でまだ頂点iについて調べていない)」ということです。逆にdist[i]!=1なら訪問済であることを表します。
これは、
「dist[i]==-1であれば未>訪問(このwhile文の中でまだ頂点iについて調べていない)」ということです。逆にdist[i]!=-1なら訪問済であることを表します。
だと思われます。。。。
- 投稿日:2020-08-30T23:48:32+09:00
機械学習についてのメモ(随時更新)
学習の流れ
実施内容の決定
データ入手
データ前処理
手法選択
ハイパーパラメータ選択
モデルの学習
モデルの評価 → 3、4、5へ
訓練データとテストデータを分ける理由
・システムリリース時
持っている全データを訓練データとしても良い・精度評価を行いたい場合
訓練データとテストデータを分け、訓練データのみから学習したモデルでテストデータを評価する教師あり学習の目的が、未知のデータに対する予測であるため
適合不足と過学習
適合不足
・訓練データに対しても予測精度が低い状態
過学習
・訓練データによくフィットしているが、テストデータ(未知のデータ)に対する予測精度が低い状態
適合不足と過学習のバランスをいかにうまく取るかが機械学習の一番難しいところ
- 投稿日:2020-08-30T23:31:35+09:00
[Python] クラスをiterableにする方法
Contents
Pythonのクラスで反復処理1を行う方法の解説記事です。
各コードはGitHub repositoryからダウンロードできます。iter_minimum.pyiteration = IterClass(["a", "ab", "abc", "bb", "cc"]) print([v for v in iteration if "a" in v]) # ['a', 'ab', 'abc']
実行環境 OS Windows Subsystem for Linux Python version 3.8.5 解決方法
クラスの特殊メソッド
__iter__
2とyield from
構文3によって実装できます。リストまたはタプルを記憶し、繰り返し処理として呼び出された際に先頭から順番に返します。iter_minimum.pyclass IterClass(object): """ Iterable class. Args: values (list[object] or tuple(object)): list of values Raises: TypeError: @values is not a list/tuple """ def __init__(self, values): if not isinstance(values, (list, tuple)): raise TypeError("@values must be a list or tuple.") self._values = values def __iter__(self): yield from self._values応用(
__bool__
との組み合わせ)上記の例では新しいクラスを作成する意義が感じられないので、応用バージョンを作りました。
最小単位の作成
反復処理で返される最小単位
Unit
クラスをまず作成します。iter_advanced.pyclass Unit(object): """ The smallest unit. """ def __init__(self, value): if not isinstance(value, (float, int)): raise TypeError( f"@value must be integer or float value, but {value} was applied.") self._value = value self._enabled = True def __bool__(self): return self._enabled @property def value(self): """ float: value of the unit """ return self._value def enable(self): """ Enable the unit. """ self._enabled = True def disable(self): """ Disable the unit. """ self._enabled = False
Unit
は固有の値と状態(__bool__
: True/False)を持っています。Unitの作成:
iter_advanced.pyunit1 = Unit(value=1.0) # Unitの状態がTrueであれば値を表示 if unit1: print(unit1.value) # 1.0状態をFalseに:
iter_advanced.py# Disable unit1.disable() # Unitの状態がTrueであれば値を表示 if unit1: print(unit1.value) # 出力なし状態をTrueに:
iter_advanced.py# Disable unit1.enable() # Unitの状態がTrueであれば値を表示 if unit1: print(unit1.value) # 1.0反復処理を行うクラス
複数の
Unit
の情報を保管して反復処理を行うクラスSeries
を作成します。iter_advanced.pyclass Series(object): """ A series of units. """ def __init__(self): self._units = [] def __iter__(self): yield from self._units def add(self, unit): """ Append a unit. Args: unit (Unit]): the smallest unit Raises: TypeError: unit is not an instance of Unit """ if not isinstance(unit, Unit): raise TypeError("@unit must be a instance of Unit") self._units.append(unit) def _validate_index(self, num): """ Validate the index number. Args: num (int): index number of a unit Raises: TypeError: @num is not an integer IndexError: @num is not a valid index number """ if not isinstance(num, int): raise TypeError( f"@num must be integer, but {num} was applied.") try: self._units[num] except IndexError: raise IndexError(f"@num must be under {len(self._units)}") def enable(self, num): """ Enable a unit. Args: num (int): index of the unit to be enabled Raises: TypeError: @num is not an integer IndexError: @num is not a valid index number """ self._validate_index(num) self._units[num].enable() def disable(self, num): """ Disable a unit. Args: num (int): index of the unit to be disabled Raises: TypeError: @num is not an integer IndexError: @num is not a valid index number """ self._validate_index(num) self._units[num].disable()状態がTrueとなっているUnitの値を返す関数
Series
クラスの挙動を確認するため、状態がTrueとなっているUnitの値を返す関数を作ります。iter_advanced.pydef show_enabled(series): """ Show the values of enabled units. """ if not isinstance(series, Series): raise TypeError("@unit must be a instance of Series") print([unit.value for unit in series if unit])挙動確認
Unit
をSeries
に登録し、すべてのUnit
の値を表示します。iter_advanced.py# Create a series of units series = Series() [series.add(Unit(i)) for i in range(6)] show_enabled(series) # [0, 1, 2, 3, 4, 5]5, 6番目のUnit(値は4, 5)の状態をFalseにすると...
iter_advanced.py# Disable two units series.disable(4) series.disable(5) show_enabled(series) # [0, 1, 2, 3]5番目のUnit(値は4)の状態をTrueに戻すと...
iter_advanced.py# Enable one disabled unit series.enable(4) show_enabled(series) # [0, 1, 2, 3, 4]あとがき
閲覧ありがとうございます。お疲れ様でした。
この記事を作成したきっかけ:
自作のPython package CovsirPhy: COVID-19 analysis with phase-dependent SIRsでこの仕組みを使いました。
- PhaseUnit: ODEモデルのパラメータ値が一定となる期間
- PhaseSeries: 感染者数のシナリオ
- 投稿日:2020-08-30T23:18:35+09:00
asyncpg でよくやる操作まとめ
psycopg2 でよくやる操作まとめ - Qiita がじわじわ LGTM が増える人気記事になっているが、最近は aiohttp Server との組み合わせで asyncpg の方をよく使うようになってきたので、こちらについてもまとめる。
しかし公式ドキュメントが普通にわかりやすいし分量も多くないので全部読んでしまうのが早いかもしれない。
asyncpg — asyncpg Documentationasyncpg 概要
- Python から PostgreSQL にアクセスするためのモジュール
- asyncio を活用した非同期処理を行うため Python 3.5 以上でないと動かない
- DB-API の仕様 には準拠していない (そもそも DB-API に規定されている仕様は同期的な API のため)
基本的な使い方
インストールする
$ pip install asyncpg接続する
import asyncpg dsn = "postgresql://username:password@hostname:5432/database" conn = await asyncpg.connect(dsn) # ... await conn.close()
conn
はasyncpg.connection.Connection
オブジェクトとして得られる。コネクションプールを作成する
import asyncpg dsn = "postgresql://username:password@hostname:5432/database" async with asyncpg.create_pool(dsn) as pool: await pool.execute("...")
pool
はasyncpg.pool.Pool
オブジェクトとして得られる。プールからコネクションを取り出して使うこともできるが、プールに対して直接
execute
などのメソッドを実行することもできる。クエリを実行する
asyncpg では cursor を作成せずとも直接
conn.execute
でクエリを実行できる。await conn.execute("INSERT INTO users(name) VALUES($1)", "foo")クエリ内に
$数字
と書いておいてクエリ実行時の第2引数以降に値を指定すると、クエリに値を埋め込むことができる。datetime
型など、一部の型のオブジェクトはそのまま指定してもよしなに内部で変換してクエリに埋め込んでくれる。どの型をどう変換するかは公式ドキュメントに載っており、自分でカスタマイズすることもできる。データを取得する
execute
の代わりにfetch
などのメソッドでクエリを実行すると結果を取得できる。await conn.fetch("SELECT * FROM users") #=> [<Record id='1' name='foo'>, <Record id='2' name='bar'>]結果は
asyncpg.Record
オブジェクトのリストとして得られる。最初の1行目だけを取得する
fetchrow
や、1行1列目の値だけを取得するfetchval
も便利。await conn.fetchrow("SELECT * FROM users WHERE id = $1", "1") #=> <Record id='1' name='foo'>await conn.fetchval("SELECT COUNT(1) FROM users") #=> 2Record オブジェクトについて
Record オブジェクトは tuple のようにも dict のようにも振る舞うので、わざわざ他のデータ型に変換する必要はあまりなさそう。
record = await conn.fetchrow("SELECT * FROM users WHERE id = $1", "1") record[1] #=> インデックスでアクセス record["name"] #=> キーでアクセストランザクションを使う
トランザクションを使う場合は非同期コンテキストマネージャ (async with) ブロック内に処理を書く。
async with connection.transaction(): await conn.execute("...")async with を使ってトランザクションを開始した場合は、ブロックが終了した時点で自動で commit されるため、明示的に commit する必要はない。
タイムアウトを設定する
コネクション作成時に
command_timeout
に秒数を指定すると、クエリのデフォルトタイムアウトを設定できる。conn = await asyncpg.connect(dsn, command_timeout=60)クエリ実行のたびに個別にタイムアウトを設定する場合は
execute
やfetch
の引数でtimeout
を指定する。await conn.execute("...", timeout=60)便利 Tips
取得したデータを Pandas DataFrame にする
fetch の結果は Record オブジェクトのリストなので、それをそのまま
pd.DataFrame()
に入れてしまってもなんとかなる。import pandas as pd records = await conn.fetch("SELECT * FROM users") if len(records) > 0: df = pd.DataFrame(records, columns=list(records[0].keys())) else: # 結果が0件だった場合の処理Pandas DataFrame の内容を INSERT する
asyncpg は COPY 系のメソッドが豊富で、タプルのリストを COPY コマンドで Bulk Insert してくれる
copy_records_to_table
を使えば Pandas DataFrame も簡単に INSERT できる。await conn.copy_records_to_table("users", records=df.itertuples(), columns=df.columns)CSV ファイルを INSERT する
同様に
copy_to_table
を使えば CSV フォーマットなどのファイルから簡単に INSERT できる。await conn.copy_to_table("users", source="users.csv", format="csv")指定したオプションは多くが COPY クエリにそのまま流れるので、COPY クエリの仕様を理解すれば使いこなせる。(format, null, header など)
PostgreSQL: Documentation: COPY
- 投稿日:2020-08-30T23:18:34+09:00
Raspberry pi - Arduino Uno間のシリアル通信(Python)
Raspberry pi - Arduino間などでデータのやり取りをしたい、と思って調べてたらでできたものをオブジェクト指向化したやつ。
Arduino側のソース
ソース
Serial.inoint n = 12; void setup(){ Serial.begin(9600); } void loop(){ Serial.println(n); Serial.write("Hello World!\n"); delay(1000); }
- 一応シリアルモニタで通信ができているか確認する。
- シリアルモニタで通信を確認出来たらシリアルモニタを閉じる。(閉じないと
SerialException
エラーが出るみたい。)- 通信速度は
9600
に設定- 数値の送信は
Serial.print
関数、文字列の送信はSerial.write
関数で行う。(一つにできないのかな?)Raspberry pi側(Python)のソース
pyserialのpipインストール
Pythonでシリアル通信をするために必要なライブラリをインストールする。
pip install pyserialソース
Arduino_USB.pyimport serial import time class Arduino_USB: data = "" # コンストラクタ def __init__(self, dev, bps): # 通信の設定 デバイス名 通信速度 self.ser = serial.Serial(dev, bps) time.sleep(2) # シリアル通信開始 def startUSB(self): # スタートコマンド。バイト文字"a"の送信 self.ser.write(b"a") # シリアル通信切断 def closeUSB(self): self.ser.close() # Arduinoからデータを読み出す def getUSB(self): self.data = self.ser.readline() return str(self.data, encoding = "utf-8") a = Arduino_USB("/dev/ttyACM0", 9600) a.startUSB() print(a.getUSB()) print(a.getUSB()) a.closeUSB()
- Raspberry piにArduinoをUSB接続する。
- デバイス名は基本
/dev/ttyACM0
で認識されている。- Arduino側と同じ通信速度に設定する。
time.sleep
でArduino側のラグを待機。- 多分改行コードまでを読み込んでる?
実行結果
12 こんにちはまとめ
- データの読み込みのタイミングの同期と読み込む範囲がよくわからん。(1Byteとか書いてあったけど日本語が読み込めるのはなぜ?)
- 数値の送信は
Serial.print
関数、文字列の送信はSerial.write
関数で行うところを一つにできないのかな?- いろいろと謎だけど個人的な遊びなので動けばヨシ!
- 投稿日:2020-08-30T22:50:15+09:00
GPyを使ってガウス過程回帰
ガウス過程回帰とは
すでにサンプリング済みの入力
x
に対応する出力y
をもとにして、
新しい入力x'
に対する出力の予測値y'
の期待値と分散を返す回帰モデルを作成する。
限られたサンプル点から関数の真の形を予測するときに用いられる。
https://jp.mathworks.com/help/stats/gaussian-process-regression-models.htmlガウス過程回帰モデルを扱うには、GPyというpythonのライブラリを使う。
https://gpy.readthedocs.io/en/deploy/#真の関数の描画
入力を2次元とし、真の関数はそれらを余弦関数に通した値の足し合わせであるとする。
temp.pyimport numpy as np # 関数の定義 def func(x): fx = np.sum(np.cos(2 * np.pi * x)) return fx xa = np.linspace(-1, 1, 101) ya = np.linspace(-1, 1, 101) Xa, Ya = np.meshgrid(xa, ya) Za = np.zeros([101, 101]) for i in range(len(Xa)): for j in range(len(Ya)): x = np.array([Xa[i,j], Ya[i,j]]) Za[i,j] = func(x) # 描画 import matplotlib.pyplot as plt fig1 = plt.figure(figsize=(8,8)) ax1 = fig1.add_subplot(111) ax1.contour(Xa, Ya, Za, cmap="jet", levels=10, alpha=1) plt.xlim(-1,1) plt.ylim(-1,1)サンプリング
無駄なくサンプリングする方法として、Sobol sequenceやLatin hypercube sampling等がある。
それらは使わず、ここでは単純にランダムにサンプル点を決定する。
サンプル点数は最初は少なめに、20点とする。temp.pyimport random random.seed(1) # ランダムにサンプリング n_sample = 20 Xa_rand = [random.random()* 2 - 1 for i in range(n_sample)] Ya_rand = [random.random()* 2 - 1 for i in range(n_sample)] xlist = np.stack([Xa_rand, Ya_rand], axis=1) Za_rand = [] for x in xlist: Za_rand = np.append(Za_rand, func(x)) # 描画 ax1.scatter(Xa_rand, Ya_rand)先程の図にサンプル点をプロットする。
下半分はまだマシだが、上半分はサンプルが少なく、スカスカである。
ガウス過程回帰
ガウス過程回帰モデルを構築する。
GPy.kern
でカーネル関数を選択する。ここでは2次元のRBFカーネルとした。
GPy.models.GPRegression
で回帰モデルを構築し、model.optimize
でモデルのパラメータをチューニングさせる。temp.pyimport GPy # 学習用データ Input = np.stack([Xa_rand, Ya_rand], axis=1) Output = Za_rand[:,None] # ガウス過程回帰モデルを構築 kernel = GPy.kern.RBF(2) model = GPy.models.GPRegression(Input, Output, kernel) model.optimize(messages=True, max_iters=1e5) # 描画 model.plot(levels=10) plt.gcf().set_size_inches(8, 8, forward=True) plt.xlim(-1,1) plt.ylim(-1,1) plt.xlabel("x1") plt.ylabel("x2")応答曲面をプロットする。
20点とかなり点数が少なかったが、意外と大まかな山谷は再現できている。
上半分の誤差は大きい。
信頼区間の描画
2次元の入力のうち、どちらか一方を0に固定して、応答曲面の断面を見てみる。
temp.py# x2=0断面 model.plot(fixed_inputs=[(1, 0)]) plt.xlim(-1,1) plt.ylim(-4,4) plt.xlabel("x1") # x1=0断面 model.plot(fixed_inputs=[(0, 0)]) plt.xlim(-1,1) plt.ylim(-4,4) plt.xlabel("x2")水色の帯は2.5~97.5%の信頼区間を示す。
信頼区間の幅が大きいほど、回帰結果にばらつきが大きい。
x2の大きいところはやはり自信がないらしい。サンプリング数を増やした場合
サンプリングの数が多くなるほど、信頼区間の幅は狭くなり、回帰結果のばらつきが小さくなる。
まとめ
GPyを用いて、ガウス過程回帰モデルを構築した。
- 投稿日:2020-08-30T22:47:06+09:00
将棋棋士の高見七段と増田六段をCNNで分類してみた【CNN初心者向け】
この記事でやったこと
- CNNによる画像分類をkerasで実装
- 将棋界でも似ていると言われている高見七段と増田六段を分類できるかを試した
- 高見七段と増田六段は学習後の分類結果が悪かった。
- 代わりに「高見七段とガルリ・ガスパロフ」や「高見七段と藤井二冠」を分類してみたところうまく分類できていた。
- やはり高見七段と増田六段は似ている...?
はじめに
突然ですが、将棋界で似ていると言われている高見七段と増田六段をご存知でしょうか。(上が高見七段、下が増田六段)
切れ長い目や黒縁のメガネなど似ている要素が多い気がします。実際自分も将棋好きですが、数年前はまじで混乱していました。kerasでCNNを学んだのでこの二人を分類できるかを試してみました。
実装概要
使用しているライブラリ一覧は下記になります。実装はgoogle colabで行っています。
import cv2 import os import numpy as np import matplotlib.pyplot as plt import seaborn as sns import random import pandas as pd from PIL import Image from sklearn.model_selection import train_test_split from google.colab.patches import cv2_imshow顔部分の切り抜き
まずはgoogle画像検索で画像をダウンロードしました。だいたい各人40枚程度集まりました。
枚数としては少ないですが、なかなか写真の数も多くなくこれが限界でした...次にダウンロードした画像から顔部分のみを切り抜きます。
顔検出に必要な学習済みモデルは下記からダウンロードできます。
https://github.com/opencv/opencv/tree/master/data/haarcascades
使い方については下記サイトを参考にしました。
- 【Python】OpenCVを使った顔認識が予想以上に簡単だった件:https://chusotsu-program.com/opencv-frontalface/
#githubよりダウンロード HAAR_FILE = "haarcascade_frontalface_alt2.xml" cascade = cv2.CascadeClassifier(HAAR_FILE) m_list = os.listdir("masuda_orig") #増田六段の顔切り取り for m_num,m in enumerate(m_list): image = cv2.imread("masuda_orig/" + m) face_list = cascade.detectMultiScale(image, minSize=(10, 10)) for i, (x, y, w, h) in enumerate(face_list): trim = image[y: y+h, x:x+w] trim = cv2.resize(trim,(size_im,size_im)) cv2.imwrite(DATA_DIR + 'masuda_tmp/masuda'+str(m_num) +"_" + str(i+1) + '.jpg', trim)"masuda_orig"フォルダにあるファイルから顔画像のみを切りとったデータを"masuda_tmp"フォルダに保存していきます。
これを高見七段にも同様に実施しました。一例として上の高見七段の画像は下記のように切り取られます。
学習データとテストデータの作成
次に顔部分が切り抜かれた画像から、手動で誤って切り抜かれた画像や、一緒に写っている別人の画像を除きます。
この部分は手動で行いました。そうして得られた画像を学習データとテストデータに分類します。
m_list = os.listdir("masuda") t_list = os.listdir("takami") X = [] y = [] for m in m_list: image = Image.open("masuda/" + m) image = image.convert("RGB") image = np.asarray(image) X.append(image) y.append([1]) for t in t_list: image = Image.open("takami/" + t) image = image.convert("RGB") image = np.asarray(image) X.append(image) y.append([0]) X=np.asarray(X) y=np.asarray(y) X_train,X_test, y_train,y_test = train_test_split(X,y,shuffle=True,test_size=0.3) print(X_train.shape,X_test.shape, y_train.shape, y_test.shape) X_train = X_train.astype("float") / 255 X_test = X_test.astype("float") / 255学習モデルの作成と学習の実行
学習モデルはCNNを通したあとに全結合層を通して二値分類しています。
モデルは下記サイトを参考にしました。
- 画像認識で「綾鷹を選ばせる」AIを作る: https://qiita.com/tomo_20180402/items/e8c55bdca648f4877188
- 脳死で覚えるkeras入門: https://qiita.com/wataoka/items/5c6766d3e1c674d61425
from keras.models import Sequential from keras.layers import Conv2D from keras.layers import MaxPool2D from keras.optimizers import Adam from keras.layers import Dense, Dropout, Activation, Flatten model = Sequential() model.add(Conv2D(32,(3,3),activation="relu",input_shape=(size_im,size_im,3))) model.add(MaxPool2D((2,2))) model.add(Dropout(0.1)) model.add(Conv2D(64,(3,3),activation="relu")) model.add(MaxPool2D((2,2))) model.add(Dropout(0.1)) model.add(Conv2D(128,(3,3),activation="relu")) model.add(MaxPool2D((2,2))) model.add(Dropout(0.1)) model.add(Conv2D(128,(3,3),activation="relu")) model.add(MaxPool2D((2,2))) model.add(Dropout(0.1)) model.add(Flatten()) model.add(Dense(512,activation="relu")) model.add(Dense(1,activation="sigmoid")) model.summary()学習後の分類結果
学習後のモデルの精度、テスト結果の正誤を確認して、分類を誤ったデータがどれだったかを確認していきます。
#モデルの学習 optim=Adam() model.compile(loss="binary_crossentropy", optimizer=optim, metrics="acc") model.fit(X_train,y_train, epochs=20, batch_size=2, validation_data=(X_test,y_test))#モデルの分類を誤った画像を表示する df = pd.DataFrame() df["pred"] = model.predict(X_test).flatten() df["test"] = y_test.flatten() df["pred"] = df["pred"].apply(lambda x: 0 if x < 0.5 else 1) df["acc"] = df["pred"] == df["test"] fig, ax = plt.subplots(1,len(mistake_list),figsize=(20,5)) mistake_list = df[df["acc"] == 0].index for i,test_i in enumerate(mistake_list): ax[i].imshow(X_test[i,...])高見七段と増田六段
エポック数を20として実行した結果、正解率は
学習データ90%、テストデータ67%となる結果が得られました。学習データが40枚、テストデータが19枚となります。
学習データに対して過学習している傾向がわかります。また、二値分類の結果で67%は低い結果と言えそうです。
分類を誤った画像は下記になります。左の2つとかは確かに分類が難しそう。高見七段と藤井二冠
高見七段と増田六段が似ているため、学習結果が低かった可能性を検証するべく、
「高見七段と藤井二冠」の分類も行ってみました。学習データが40枚、テストデータが19枚となります。その結果は学習データ92%、テストデータ88%。
増田六段との分類と比べると若干精度が高くなっています。
やはり、高見七段と増田六段は分類が難しい...?
分類が間違った画像は下記。特に目立った特徴はないですね...
高見七段とガルリがスロフ
試しにもっと違う人ということで、元チェス世界王者のガルリガスパロフとの分類も試してみました。
学習データとテストデータは48枚、21枚です。その結果は学習データ100%、テストデータ100%。
100%はちょっと怪しいですが、増田六段や藤井二冠との分類と比較すると高い精度になっています。まとめ
分類結果は下記となりました。
- 「高見七段と増田六段」:67%
- 「高見七段と藤井二冠」:88%
- 「高見七段とガルリガスパロフ」:100%機械からみてもこの二人は似ているということになりそうです。
ただし今回は学習データが40枚ほどと非常に少なかったのが課題です。そのため、得られた結果も正確性が低く、どれをテストデータにするかで変動してしまいます。
もっとデータを多くすれば精度はより高くなりそうです。使用したコード
実際に使用したコードはこちら。
https://colab.research.google.com/drive/14Dg2-uQWSf4NT2OnxTWGVSEDST3O68d8?usp=sharing
- 投稿日:2020-08-30T22:47:06+09:00
将棋棋士の高見七段と増田六段をCNNで分類してみた
この記事でやったこと
- CNNによる画像分類をkerasで実装
- 将棋界でも似ていると言われている高見七段と増田六段を分類できるかを試した
- 高見七段と増田六段は学習後の分類結果が悪かった。
- 代わりに「高見七段とガルリ・ガスパロフ」や「高見七段と藤井二冠」を分類してみたところうまく分類できていた。
- やはり高見七段と増田六段は似ている...?
はじめに
突然ですが、将棋界で似ていると言われている高見七段と増田六段をご存知でしょうか。(上が高見七段、下が増田六段)
切れ長い目や黒縁のメガネなど似ている要素が多い気がします。実際自分も将棋好きですが、数年前はまじで混乱していました。kerasでCNNを学んだのでこの二人を分類できるかを試してみました。
実装概要
使用しているライブラリ一覧は下記になります。実装はgoogle colabで行っています。
import cv2 import os import numpy as np import matplotlib.pyplot as plt import seaborn as sns import random import pandas as pd from PIL import Image from sklearn.model_selection import train_test_split from google.colab.patches import cv2_imshow顔部分の切り抜き
まずはgoogle画像検索で画像をダウンロードしました。だいたい各人40枚程度集まりました。
枚数としては少ないですが、なかなか写真の数も多くなくこれが限界でした...次にダウンロードした画像から顔部分のみを切り抜きます。
顔検出に必要な学習済みモデルは下記からダウンロードできます。
https://github.com/opencv/opencv/tree/master/data/haarcascades
使い方については下記サイトを参考にしました。
- 【Python】OpenCVを使った顔認識が予想以上に簡単だった件:https://chusotsu-program.com/opencv-frontalface/
#githubよりダウンロード HAAR_FILE = "haarcascade_frontalface_alt2.xml" cascade = cv2.CascadeClassifier(HAAR_FILE) m_list = os.listdir("masuda_orig") #増田六段の顔切り取り for m_num,m in enumerate(m_list): image = cv2.imread("masuda_orig/" + m) face_list = cascade.detectMultiScale(image, minSize=(10, 10)) for i, (x, y, w, h) in enumerate(face_list): trim = image[y: y+h, x:x+w] trim = cv2.resize(trim,(size_im,size_im)) cv2.imwrite(DATA_DIR + 'masuda_tmp/masuda'+str(m_num) +"_" + str(i+1) + '.jpg', trim)"masuda_orig"フォルダにあるファイルから顔画像のみを切りとったデータを"masuda_tmp"フォルダに保存していきます。
これを高見七段にも同様に実施しました。一例として上の高見七段の画像は下記のように切り取られます。
学習データとテストデータの作成
次に顔部分が切り抜かれた画像から、手動で誤って切り抜かれた画像や、一緒に写っている別人の画像を除きます。
この部分は手動で行いました。そうして得られた画像を学習データとテストデータに分類します。
m_list = os.listdir("masuda") t_list = os.listdir("takami") X = [] y = [] for m in m_list: image = Image.open("masuda/" + m) image = image.convert("RGB") image = np.asarray(image) X.append(image) y.append([1]) for t in t_list: image = Image.open("takami/" + t) image = image.convert("RGB") image = np.asarray(image) X.append(image) y.append([0]) X=np.asarray(X) y=np.asarray(y) X_train,X_test, y_train,y_test = train_test_split(X,y,shuffle=True,test_size=0.3) print(X_train.shape,X_test.shape, y_train.shape, y_test.shape) X_train = X_train.astype("float") / 255 X_test = X_test.astype("float") / 255学習モデルの作成と学習の実行
学習モデルはCNNを通したあとに全結合層を通して二値分類しています。
モデルは下記サイトを参考にしました。
- 画像認識で「綾鷹を選ばせる」AIを作る: https://qiita.com/tomo_20180402/items/e8c55bdca648f4877188
- 脳死で覚えるkeras入門: https://qiita.com/wataoka/items/5c6766d3e1c674d61425
from keras.models import Sequential from keras.layers import Conv2D from keras.layers import MaxPool2D from keras.optimizers import Adam from keras.layers import Dense, Dropout, Activation, Flatten model = Sequential() model.add(Conv2D(32,(3,3),activation="relu",input_shape=(size_im,size_im,3))) model.add(MaxPool2D((2,2))) model.add(Dropout(0.1)) model.add(Conv2D(64,(3,3),activation="relu")) model.add(MaxPool2D((2,2))) model.add(Dropout(0.1)) model.add(Conv2D(128,(3,3),activation="relu")) model.add(MaxPool2D((2,2))) model.add(Dropout(0.1)) model.add(Conv2D(128,(3,3),activation="relu")) model.add(MaxPool2D((2,2))) model.add(Dropout(0.1)) model.add(Flatten()) model.add(Dense(512,activation="relu")) model.add(Dense(1,activation="sigmoid")) model.summary()学習後の分類結果
学習後のモデルの精度、テスト結果の正誤を確認して、分類を誤ったデータがどれだったかを確認していきます。
#モデルの学習 optim=Adam() model.compile(loss="binary_crossentropy", optimizer=optim, metrics="acc") model.fit(X_train,y_train, epochs=20, batch_size=2, validation_data=(X_test,y_test))#モデルの分類を誤った画像を表示する df = pd.DataFrame() df["pred"] = model.predict(X_test).flatten() df["test"] = y_test.flatten() df["pred"] = df["pred"].apply(lambda x: 0 if x < 0.5 else 1) df["acc"] = df["pred"] == df["test"] fig, ax = plt.subplots(1,len(mistake_list),figsize=(20,5)) mistake_list = df[df["acc"] == 0].index for i,test_i in enumerate(mistake_list): ax[i].imshow(X_test[i,...])高見七段と増田六段
エポック数を20として実行した結果、正解率は
学習データ90%、テストデータ67%となる結果が得られました。学習データが40枚、テストデータが19枚となります。
学習データに対して過学習している傾向がわかります。また、二値分類の結果で67%は低い結果と言えそうです。
分類を誤った画像は下記になります。左の2つとかは確かに分類が難しそう。高見七段と藤井二冠
高見七段と増田六段が似ているため、学習結果が低かった可能性を検証するべく、
「高見七段と藤井二冠」の分類も行ってみました。学習データが40枚、テストデータが19枚となります。その結果は学習データ92%、テストデータ88%。
増田六段との分類と比べると若干精度が高くなっています。
やはり、高見七段と増田六段は分類が難しい...?
分類が間違った画像は下記。特に目立った特徴はないですね...
高見七段とガルリがスロフ
試しにもっと違う人ということで、元チェス世界王者のガルリガスパロフとの分類も試してみました。
学習データとテストデータは48枚、21枚です。その結果は学習データ100%、テストデータ100%。
100%はちょっと怪しいですが、増田六段や藤井二冠との分類と比較すると高い精度になっています。まとめ
分類結果は下記となりました。
- 「高見七段と増田六段」:67%
- 「高見七段と藤井二冠」:88%
- 「高見七段とガルリガスパロフ」:100%機械からみてもこの二人は似ているということになりそうです。
ただし今回は学習データが40枚ほどと非常に少なかったのが課題です。そのため、得られた結果も正確性が低く、どれをテストデータにするかで変動してしまいます。
もっとデータを多くすれば精度はより高くなりそうです。使用したコード
実際に使用したコードはこちら。
https://colab.research.google.com/drive/14Dg2-uQWSf4NT2OnxTWGVSEDST3O68d8?usp=sharing
- 投稿日:2020-08-30T22:46:26+09:00
jupyter lab上で画像のラベリングをする
やりたいこと
AIに使いたい画像のラベリングをしたいが、GUIを作るのが面倒。。。と思い、
JupyterLab上でできないかーと考え、色々調べてたらできたのでそのときのメモ。
matplotで表示した画像を更新しながらinputでラベリングを入力するようなイメージ。実行環境
- Python 3.8.5
- jupyter lab 2.2.2
- mac(windows10でも実行済み)
コード
sample_imgフォルダにある画像をラベリングする。
準備
import cv2 import matplotlib.pyplot as plt import os import glob import IPython # 画像のpathを取得 img_list = glob.glob(os.path.join(r"sample_img","*.jpg")) img_list >>> ['sample_img/img3.jpg', 'sample_img/img2.jpg', 'sample_img/img1.jpg']画像を表示するコード
name_list = [] for img_path in img_list: # 画像の読み込み img = cv2.imread(img_path) # 画像を表示するフレーム fig = plt.figure(figsize=(5,5)) ax = fig.add_subplot(1,1,1) ax.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB)) plt.title(img_path) plt.pause(.01) # ラベル付けするためのテキストボックス comment = input() if comment == "break": break else: # 表示している画像をクリア IPython.display.clear_output() # Inputに入力した文字列をリストに追加 name_list.append(comment)確認
# name_listの確認 name_list >>> ['dog', 'cat', 'cat']最後に
Input部分をラジオボタンみたいにできたらいいな〜.
- 投稿日:2020-08-30T22:40:01+09:00
[Python] UnionFind ABC177D
ABC177D
UnionFindにはいろいろな実装があるが, 本問ではparents配列にノード数を保持する実装だと非常に簡単に解ける. 以下のようにしてノード数を保持する.
・自身が子のとき, 親ノード番号を格納する。自身が根のとき, ノード数を負の数で格納する。
・負の数のときは自身が根であり, その絶対値がその木のノード数を表す。サンプルコードimport sys #UnionFindTreeクラスの定義 class UnionFind(): #クラスコンストラクタ #selfはインスタンス自身 def __init__(self, n): #親ノードを-1に初期化する self.parents = [-1] * n #根を探す def find(self, x): if self.parents[x] < 0: return x else: self.parents[x] = self.find(self.parents[x]) return self.parents[x] #xとyの木を併合 def union(self, x, y): #根探し x = self.find(x) y = self.find(y) if x == y: return if self.parents[x] > self.parents[y]: x, y = y, x self.parents[x] += self.parents[y] self.parents[y] = x N, M = map(int, input().split()) info = [tuple(map(int, s.split())) for s in sys.stdin.readlines()] #UnionFindインスタンスの生成 uf = UnionFind(N) for a, b in info: #インデックスを調整し、a,bの木を結合 a -= 1; b -= 1 uf.union(a, b) ans = min(uf.parents) print(-ans)
- 投稿日:2020-08-30T22:37:53+09:00
OpenCVでスプライトを回転させる
はじめに
OpenCVで透過画像を扱う ~スプライトを舞わせる~の続きです。
スプライトを回転させるって、ワクワクしますな。ナムコのSYSTEM II基板を思い出します。アサルトとか、オーダインとか。プレイステーション本体を購入する前にナムコミュージアム VOL.4を買ったものだ。
OpenCVで画像を回転させる
OpenCVでは
cv2.rotate()
で画像を90度単位で回転できることができるが、任意の角度で回転させる関数はない。より高度な関数があるので、それを使う。アフィン変換のお勉強
回転や拡大縮小などの一次変換と平行移動を合わせた写像をアフィン変換という。要は
\begin{pmatrix} x' \\ y' \end{pmatrix} = \begin{pmatrix} a & b \\ d & e \end{pmatrix} \begin{pmatrix} x \\ y \end{pmatrix} + \begin{pmatrix} c \\ f \end{pmatrix}という変換だ。この形ではプログラムしづらいので
\begin{pmatrix} x' \\ y' \\ 1 \end{pmatrix} = \begin{pmatrix} a & b & c \\ d & e & f \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} x \\ y \\ 1 \end{pmatrix}と表現することにする。一番下がなんとなく気持ち悪いが、
1=1
という恒等式があるだけだ。この
\begin{pmatrix} a & b & c \\ d & e & f \\ \end{pmatrix}を定義して、
cv2.warpAffine()
という関数にかける。
ただし、画像を回転させるにあたり、わざわざ\begin{pmatrix} cosθ & -sinθ & 0 \\ sinθ & cosθ & 0 \\ \end{pmatrix}という計算をする必要はない。
cv2.getRotationMatrix2D()
で得られる行列を使えばよいのだ。回転行列を作る cv.getRotationMatrix2D(center, angle, scale)
- center 回転中心。
- angle 回転角度。反時計回りで、単位はラジアンではなく度。
- scale 拡大縮小の倍率。
平行移動分は自分で追加しよう。
画像をアフィン変換する cv.warpAffine(src, M, dsize)
- src 元画像。
- M 2*3の変換行列。
- dsize 出力画像サイズを
(width, height)
のタプルで指定する。- これら以外にも必須でない引数があるが省略。
実践
変換行列は具体的にどうなっているのかなっと。
ソース1import cv2 angle = 30 # degrees M = cv2.getRotationMatrix2D((0,0), angle, 1) print (M) print (M.shape) print (M.dtype)結果1[[ 0.8660254 0.5 0. ] [-0.5 0.8660254 0. ]] (2, 3) float64cos30度 = √3 /2 = 0.8660254…だからまさしく回転行列の…って、あれ? マイナスの位置が違ってるぞ?
どうやら数学で使うxy平面とは違い下が正になっている座標系に対応した計算になっているらしい。で、これを使って実際に画像変換してみる。
ソース2import cv2 filename = "hoge.png" img = cv2.imread(filename) h, w = img.shape[:2] center = (140,60) angle = 0 while True: angle = (angle + 10) % 360 M = cv2.getRotationMatrix2D(center, angle, 1) img_rot = cv2.warpAffine(img, M, (w, h)) cv2.imshow(filename, img_rot) key = cv2.waitKey(100) & 0xFF if key == ord("q"): break cv2.destroyAllWindows()結果はこう。
元画像 結果 回転角度と回転中心については理解したが、「ちょっと待てよ、そもそも画像がはみ出してるじゃん」と言いたくなってくる。
はみ出しを避ける
はみ出しを避けるにはどうするか。どうするかって、計算するんだよ計算。
2辺の長さが w と h である長方形(赤)が角度aだけ傾いたとき、外接する四角形(青)のサイズは
rot_w = w*cos(a) + h*sin(a) rot_h = w*sin(a) + h*cos(a)となる。0度~90度 を外れるとサインやコサインがマイナスになるので、正確には各項は絶対値とする必要がある。
ああ、ようやく理解したぞ。後述の先人たちの記事にこの式が出てきて回転行列とも違うし何だろうと思っていたのよね。で、この青の長方形に収まるように位置を平行移動すればいいわけだ。(0,0)を回転中心としているので、画像中心を基準で考える、と。
ソース3import cv2 import numpy as np filename = "hoge.png" img = cv2.imread(filename) h, w = img.shape[:2] angle = 0 while True: angle = (angle + 10) % 360 a = np.radians(angle) w_rot = int(np.round(w*abs(np.cos(a)) + h*abs(np.sin(a)))) h_rot = int(np.round(w*abs(np.sin(a)) + h*abs(np.cos(a)))) M = cv2.getRotationMatrix2D((w/2,h/2), angle, 1) M[0][2] += -w/2 + w_rot/2 M[1][2] += -h/2 + h_rot/2 img_rot = cv2.warpAffine(img, M, (w_rot,h_rot)) cv2.imshow(filename, img_rot) key = cv2.waitKey(100) & 0xFF if key == ord("q"): break cv2.destroyAllWindows()結果はこう。画像がウィンドウからはみ出ることはなくなったが、ウィンドウの位置(画像の左上の位置)が共通という制約の上でのアニメーションなので暴れているのは仕方がない。
回転中心を指定する
中心を指定して回転させ、さらに作られた画像が暴れないようにするには、あらかじめ回転後の画像が描かれるキャンバスのサイズを決めておけばよい。
キャンパスのサイズを求めるのは難しくない。回転中心と四隅の距離のうち、最大のやつが半径に相当する。キャンバスのサイズはその2倍。
調子が良かったのだろう、以下のソースでは半径を求める式をわずか1行で表現している。
今となっては自分でもどういう計算をしているのか読み解くのが大変なほどだ。あまり技巧に走るのもよくないな。ソース4import cv2 import numpy as np filename = "hoge.png" img = cv2.imread(filename) h, w = img.shape[:2] xc, yc = 140, 60 # 回転中心 angle = 0 # 回転中心と四隅の距離の最大値を求める pts = np.array([(0,0), (w,0), (w,h), (0,h)]) ctr = np.array([(xc,yc)]) r = np.sqrt(max(np.sum((pts-ctr)**2, axis=1))) winH, winW = int(2*r), int(2*r) while True: angle = (angle + 10) % 360 M = cv2.getRotationMatrix2D((xc,yc), angle, 1) M[0][2] += r - xc M[1][2] += r - yc imgRot = cv2.warpAffine(img, M, (winW,winH)) cv2.imshow("", imgRot) key = cv2.waitKey(100) & 0xFF if key == ord("q"): break cv2.destroyAllWindows()回転画像を背景画像に合成する
以上の処理を、前回のスプライト関数に追加する。
int()
でなくmath.ceil()
で切り上げしたほうが良かったかもしれない。ソース5import cv2 import numpy as np def putSprite_mask2(back, front4, pos, angle=0, center=(0,0)): x, y = pos xc, yc = center fh, fw = front4.shape[:2] bh, bw = back.shape[:2] # 回転中心と四隅の距離の最大値を求める pts = np.array([(0,0), (fw,0), (fw,fh), (0,fh)]) ctr = np.array([(xc,yc)]) r = int(np.sqrt(max(np.sum((pts-ctr)**2, axis=1)))) # 回転する M = cv2.getRotationMatrix2D((xc,yc), angle, 1) M[0][2] += r - xc M[1][2] += r - yc imgRot = cv2.warpAffine(front4, M, (2*r,2*r)) # 回転画像を含む外接四角形 # 外接四角形の全体が背景画像外なら何もしない x0, y0 = x+xc-r, y+yc-r if not ((-2*r < x0 < bw) and (-2*r < y0 < bh)) : return back # 外接四角形のうち、背景画像内のみを取得する x1, y1 = max(x0, 0), max(y0, 0) x2, y2 = min(x0+2*r, bw), min(y0+2*r, bh) imgRot = imgRot[y1-y0:y2-y0, x1-x0:x2-x0] # マスク手法で外接四角形と背景を合成する front_roi = imgRot[:, :, :3] mask1 = imgRot[:, :, 3] mask_roi = 255 - cv2.merge((mask1, mask1, mask1)) roi = back[y1:y2, x1:x2] tmp = cv2.bitwise_and(roi, mask_roi) tmp = cv2.bitwise_or(tmp, front_roi) back[y1:y2, x1:x2] = tmp return back if __name__ == "__main__": filename_back = "space.jpg" filename_front = "uchuhikoushi.png" img_back = cv2.imread(filename_back) img_front = cv2.imread(filename_front, -1) pos = [(0, 50), (300,200), (400,400), (500,-50), (-100,1000)] # 画像を置く左上座標 xc, yc = 140, 60 # 前景画像の回転中心 angle = 0 while True: back = img_back.copy() for x,y in pos: img = putSprite_mask2(back, img_front, (x,y), angle, (xc,yc)) # 正しく描写されていることを確認する(必須ではない) cv2.circle(img, (x,y), 5, (0,255,0), -1) # 前景画像の左上にマーク cv2.circle(img, (x+xc,y+yc), 5, (0,0,255), -1) # 回転中心にマーク cv2.putText(img, f"angle={angle}", (10,440), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2) cv2.imshow("putSprite_mask2", img) key = cv2.waitKey(0) & 0xFF if key == ord("q"): break angle = (angle + 30) % 360 cv2.destroyAllWindows()終わりに
本当はこの先、外接四角形を最小限にして左上座標を細かく制御することで同等の挙動を実現させたかったのだが、図形と行列と、つまりは高校数学レベルの問題がうまく解けずにそこまではできなかった。
えー? 今、高校で行列やらないの?!参考記事
指定した角度づつ元画像の中心を軸に回転させた画像を作成して保存する。
opencvの画像回転で、はみ出した部分が切り取られないようにする方法
完全に理解するアフィン変換
- 投稿日:2020-08-30T22:10:34+09:00
犬ですが何か?Django--カスタムユーザーモデルを作成する
ユーザー登録/ログイン/ログアウトの機能追加
こんにちワン!柴犬のぽん太です。今日は暑いのでガリガリ君ソーダ味を食べました。めちゃくちゃ美味しかったです!ガリガリ君って埼玉県の会社が作っているんですね。ぽん太はまだ埼玉県に行ったことがないです。
さて、今日はユーザー管理機能に挑戦します。
accountアプリの作成
まずはaccountアプリケーションを作ります。
terminal(venv_dog) Ponta@shiba_app # python manage.py startapp accountsアプリケーションを作成したらそれをshiba_app/settings.pyのINSTALLED_APPSに登録します。ぽん太のINSTALLED_APPSは次のようになっています。
shiba_app/settings.pyINSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'top.apps.TopConfig', 'wan.apps.WanConfig', 'accounts.apps.AccountsConfig', ]最後の
'accounts.apps.AccountsConfig',
を追加しました。shiba_app/setting.pyのその他の設定
ユーザー登録・認証機能のためにsettings.pyに幾つかの設定が必要です。
まず、メールアドレス認証のためのメール送受信ですが、これは開発環境ではコンソールへ表示させます。このための設定が、
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
です。つぎに、Djangoがカスタムユーザーモデルを参照するように、
AUTH_USER_MODEL = 'accounts.CustomUser'
を設定します。さらに、後述のdjango-allauthのために、
SITE_ID = 1
を設定します。また、認証バックエンドの設定が、
AUTHENTICATION_BACKENDS = (
'allauth.account.auth_backends.AuthenticationBackend',
'django.contrib.auth.backends.ModelBackend',
)
です。
allauth.account.auth_backends.AuthenticationBackend
が一般ユーザーがメールアドレス認証でログインするための認証バックエンド、
django.contrib.auth.backends.ModelBackend
が管理サイトにスーパーユーザーがユーザー名でログインするための認証バックエンドです。ACCOUNT_AUTHENTICATION_METHOD = 'email'
で、メールアドレス認証に設定し、
ACCOUNT_USERNAME_REQUIRED = True
でユーザー名を必須としています。ACCOUNT_EMAIL_VERIFICATION = 'mandatory'
ACCOUNT_EMAIL_REQUIRED = True
でメールアドレス確認を必須としています。LOGIN_REDIRECT_URL = 'wan:index'
でログイン後、wanアプリケーションのindexにリダイレクトし、
ACCOUNT_LOGOUT_REDIRECT_URL = 'top:index'
でログアウト後、topアプリケーションのindexにリダイレクトします。ACCOUNT_LOGOUT_ON_GET = True
は、ログアウトボタンのクリック後ただちにログアウトさせる設定です。shiba_app/settings.pyEMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' AUTH_USER_MODEL = 'accounts.CustomUser' SITE_ID = 1 AUTHENTICATION_BACKENDS = ( 'allauth.account.auth_backends.AuthenticationBackend', 'django.contrib.auth.backends.ModelBackend', ) ACCOUNT_AUTHENTICATION_METHOD = 'email' ACCOUNT_USERNAME_REQUIRED = True ACCOUNT_EMAIL_VERIFICATION = 'mandatory' ACCOUNT_EMAIL_REQUIRED = True LOGIN_REDIRECT_URL = 'wan:index' ACCOUNT_LOGOUT_REDIRECT_URL = 'top:index' ACCOUNT_LOGOUT_ON_GET = Trueカスタムユーザーモデルの定義
Djangoにはデフォルトのユーザーモデルが定義されていますが、それをオーバーライドしてカスタムユーザーモデルを定義します。
account/models.pyrom django.contrib.auth.models import AbstractUser from django.db import models class CustomUser(AbstractUser): class Meta: verbose_name_plural = 'CustomUser' dogname = models.CharField(verbose_name='Dog Name', blank=False, max_length=40) introduction = models.TextField(verbose_name='Introduction', blank=True)dogname, introductionを追加してみました。
あとで機会があればこれらの入力画面を作ってみたいと思います。マイグレーション
terminal(venv_dog) Ponta@shiba_app # python manage.py makemigrations Migrations for 'accounts': accounts/migrations/0001_initial.py - Create model CustomUser Process finished with exit code 0 (venv_dog) Ponta@shiba_app # python manage.py migrate Operations to perform: Apply all migrations: accounts, admin, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0001_initial... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying auth.0012_alter_user_first_name_max_length... OK Applying accounts.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying sessions.0001_initial... OK (venv_dog) Ponta@shiba_app #django-allauthのインストール
ユーザー認証機能の作り込みは面倒なので、django-allauthというパッケージを利用します。
terminal(venv_dog) Ponta@shiba_app # pip install django-allauthDjango-allauthをアプリケーションで使えるようにするためにsettings.pyのINSTALLED_APPSに
'django.contrib.sites',
'allauth',
'allauth.account',
'allauth.socialaccount',
の4行を追加します。shiba_app/settings.pyINSTALLED_APPS = [ (中略) 'django.contrib.sites', 'allauth', 'allauth.account', 'allauth.socialaccount', ]もう一度マイグレーション
django-allauthが使うテーブルを作成するため、もう一度migrateコマンドを実行します。
terminal(venv_dog) Ponta@shiba_app # python manage.py migrate Operations to perform: Apply all migrations: account, accounts, admin, auth, contenttypes, sessions, sites, socialaccount Running migrations: Applying account.0001_initial... OK Applying account.0002_email_max_length... OK Applying sites.0001_initial... OK Applying sites.0002_alter_domain_unique... OK Applying socialaccount.0001_initial... OK Applying socialaccount.0002_token_max_lengths... OK Applying socialaccount.0003_extra_data_default_dict... OKスーパーユーザーの作成
Djangoの管理画面にアクセスするため、スーパーユーザーを作成します。
terminal(venv_dog) Ponta@shiba_app # python manage.py createsuperuser Username: ponta Email address: ponta@example.com Password: Password (again): This password is too short. It must contain at least 8 characters. This password is too common. Bypass password validation and create user anyway? [y/N]: N Password: Password (again): Superuser created successfully.おっと、パスワードが短すぎたり、簡単すぎるとpassword validationに引っかかるようです。
2回目には真面目に設定しました。管理画面にログイン
スーパーユーザーを作成しましたので、一旦Djangoの管理画面にログインしてみます。
http://127.0.0.1:8000/admin/
にアクセスします。すると、
http://127.0.0.1:8000/admin/login/?next=/admin/
にリダイレクトされ、ログイン画面が現れました。ここにスーパーユーザーを作成したときのUsernameとPasswordを入力し、ログインします。すると、下記のように無事ログインできて、管理画面が現れました。
ルーティング設定 shiba_app/urls.py
いま、shiba_appには3つのアプリケーション(top, wan, accounts)が作成されています。
shiba_app/urls.pyfrom django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('', include('top.urls')), path('wan/', include('wan.urls')), path('accounts/', include('allauth.urls')), ]ルーティング設定 top/urls.py
top/urls.pyfrom django.urls import path from . import views app_name = 'top' urlpatterns = [ path('', views.IndexView.as_view(), name='index'), ]ルーティング設定 wan/urls.py
wan/urls.pyfrom django.urls import path from . import views app_name = 'wan' urlpatterns = [ path('', views.IndexView.as_view(), name='index'), ]画面設定top/views.py
top/views.pyfrom django.views import generic class IndexView(generic.TemplateView): template_name = "top/index.html"画面設定wan/views.py
wan/views.pyfrom django.views import generic class IndexView(generic.TemplateView): template_name = "wan/index.html"画面テンプレートtop/index.html
ログイン中はユーザー名とログアウトボタンが表示され、ログアウト中はユーザー登録ボタンとログインボタンが表示される仕組みです。
top/templates/top/index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>柴犬ぽん太の「犬ですが何か?」</title> </head> <body> <h1>柴犬ぽん太の「犬ですが何か?」</h1> <p>柴犬のぽん太です。よろしくワン!</p> {% if user.is_authenticated %} <p>ユーザー名: {{ user }}</p> <p><a href="{% url 'account_logout' %}">ログアウト</a> </p> {% else %} <p><a href="{% url 'account_signup' %}">ユーザー登録</a></p> <p><a href="{% url 'account_login' %}">ログイン</a></p> {% endif %} </body> </html>画面テンプレートwan/index.html
ログイン中はユーザー名とログアウトボタンが表示され、ログアウト中はユーザー登録ボタンとログインボタンが表示される仕組みです。(えっ!さっきと同じ??)
wan/templates/wan/index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>柴犬ぽん太の「犬ですが何か?」</title> </head> <body> <h1>柴犬ぽん太の「犬ですが何か?」</h1> {% if user.is_authenticated %} <p>ユーザー名: {{ user }}</p> <p><a href="{% url 'account_logout' %}">ログアウト</a> </p> {% else %} <p><a href="{% url 'account_signup' %}">ユーザー登録</a></p> <p><a href="{% url 'account_login' %}">ログイン</a></p> {% endif %} </body> </html>ユーザー登録テスト
では、トップページを確認してみます。
メールが送信されました。
今はテストなのでコンソール(ターミナル画面)にメール文が流れます。terminal[30/Aug/2020 11:45:57] "GET /accounts/signup/ HTTP/1.1" 200 1330 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [example.com] Please Confirm Your E-mail Address From: webmaster@localhost To: lisa@example.com Date: Sun, 30 Aug 2020 11:46:30 -0000 Message-ID: (省略) Hello from example.com! You're receiving this e-mail because user Lisa has given yours as an e-mail address to connect their account. To confirm this is correct, go to http://127.0.0.1:8000/accounts/confirm-email/NA:1kCLn4:g2Po76yr88dimfzO641FunQcGbYPexyYkTE4j0JLZ4Q/ Thank you from example.com! example.com ------------------------------------------------------------------------------- [30/Aug/2020 11:46:30] "POST /accounts/signup/ HTTP/1.1" 302 0この表示の途中にある、
"http://127.0.0.1:8000/accounts/confirm-email/NA:1kCLn4:g2Po76yr88dimfzO641FunQcGbYPexyYkTE4j0JLZ4Q/"
にアクセスするとemail認証が完了します。email認証が完了したらログインしてみましょう。
ログインすると次の画面になります。
ログアウトすると最初の画面に戻ります。
おしまい。じゃあまたね!ワン!
- 投稿日:2020-08-30T21:16:03+09:00
python3の地味なトリビア
他の人の記事眺めてて、どうもご存じないらしい、という点を幾つか。
コロンの後ろには1文だけ書ける
なので
exec("for i in range(10):\n\tprint(i)")これは
for i in range(10):print(i)もともと1行で書ける。execとか要らない。
これはコロンを使う全ての構文であてはまる。def,class,while,try/catch,if....他なにかあったっけ。
他所様のフレームワークのソースを探検してたら、こういうのよく見かけない?
class OreOreException(Exception): pass式の継続が明らかな場合は、任意箇所で改行できる
例えば内包表記
xx = ["even" if i % 2 == 0 else "odd" \ for i in range(10)]これは
xx = [ "even" if i % 2 == 0 else "odd" for i in range(10) ]こう書けます。
同じ理由で、通常の式を括弧で囲んで、無理くり複数行にも書ける。
一瞬でもカンマを使うと、その1部分だけがタプル表記に化けるので要注意。
タプル...内包表記...?
内包表記関連の記事を眺めててギョッとしたのだが、たまーにタプル内包表記という日本語を散見する。
Pythonのtuple内包表記の落とし穴 - done is better than perfect
Python タプルの内包表記とジェネレータ式 - Qiita
python2.5から、括弧内包表記はジェネレータとして機能するようだ。
関数型プログラミング HOWTO — Python 2.7.18 ドキュメント
2.4までなら確かに「タプル内包表記」だった可能性があるが。
タプルはそもそも、(括弧と)カンマで成立する構文。
5. データ構造 — Python 3.8.5 ドキュメント
タプルはコンマで区切られたいくつかの値からなります。例えば以下のように書きます:
括弧要らないらしい.... だがカンマは必須。
逆に、要素1個のときはどう書くの?という問いが出てくる。
python - How to create a tuple with only one element - Stack Overflow
>>> type( ('a') ) <type 'str'> >>> type( ('a',) ) <type 'tuple'>ポイントは末尾のカンマね。
ちなみにtuple()に突っ込むとタプルが出来上がるのは仕様です。
list()やらtuple()やらはdict()やらは、ジェネレータ/イテレータを直接受け付けるため。tuple([iterable])って書いてあるでしょ?
- 投稿日:2020-08-30T21:14:26+09:00
AtCoder Beginner Contest 177 問題C 間違った理由を調べてみた
はじめに
2020/8/29に行われたContest177の問題Cで、絶望するほどハマったので、調べてみました。
問題
https://atcoder.jp/contests/abc177/tasks/abc177_c
考えたこと
まともに計算すると、多分TLEになると思った。
でもちょっと工夫すればなんとかなりそう。しばし熟考。
求める和が、{(A1+A2+…+An)^2-(A1^2+A2^2+…+An^2)}÷2 だという結論に達し、コード化。
およそ10分後、完成。c.pyn = int(input()) l = input() a = l.split(' ') tmp = 0 tmp2 = 0 for i in range(n): tmp += int(a[i]) for i in range(n): tmp2 += int(a[i])*int(a[i]) print(int((tmp*tmp-tmp2)/2)%1000000007)よっしゃ、今日は調子ええで!4問いけるかもしれんな!
と思った矢先、無常の判定が。
ここでフリーズしてしまい、この日は2問で終了。なんで
コンテスト終了後、解説を見る。わからない。なんで?累積和でもこの計算方法でも、数学的には同じ値になるはずやん。
土曜日はとにかく納得いかなかったので、寝て、心を落ち着かせ、
日曜日に累積和バージョンを書いて、提出してみた。c_.pyn = int(input()) l = input() a = l.split(' ') tmp = 0 total2 = 0 for i in range(n): tmp += int(a[i]) for i in range(n): total2 += int(a[i])*(tmp-int(a[i])) tmp -= int(a[i]) print(total2%1000000007)えええええええええええええ
なんで?なんで同じことをしているのに、片方はWAで片方はACなん???
色々試してみた
まずpythonでint型の数字が、どれぐらい大きな数字を扱えるか確認した。
結果、pythonのver3では、メモリが許す限りいくらでも大きな値が使えるとのことだった。
とすると、おかしいのは÷2のところ?
きっと大きな数字の時に、変なことが起きているに違いない。適当な大きさの数字を10個入力して、2で割る前の数字と2で割った時の数字を表示させてみた。
input_data110 1234567 2345678 3456789 4567890 5678901 6789012 7890123 8901234 9012345 1111111 2257615544087530 1128807772043765これは大丈夫そう。もうちょっと大きくしてみる。
input_data210 12345678 23456789 34567890 45678901 56789012 67890123 78901234 89012345 90123456 11111111 225761590208477104 1128807951042385601の位を見ると、2で割る前が4だから、2で割ったものは2か7になるはず。
なのに0。
ということで、大きな数字を2で割ると、どうもおかしな事が起こるらしい。どうして2で割れないの
「python 割り算 おかしい」でググったら、以下の記事を見つけました。
Python3で巨大な浮動小数計算の結果が変だったので理由を調べてみた
https://paiza.hatenablog.com/entry/2017/08/01/Python3%E3%81%A7%E5%B7%A8%E5%A4%A7%E3%81%AA%E6%B5%AE%E5%8B%95%E5%B0%8F%E6%95%B0%E8%A8%88%E7%AE%97%E3%81%AE%E7%B5%90%E6%9E%9C%E3%81%8C%E5%A4%89%E3%81%A0%E3%81%A3%E3%81%9F%E3%81%AE%E3%81%A7%E7%90%86うーん、分かったような・・・でもやっぱり分からない。。。
ただ大きい数字は、割り算するとおかしな事が起こりうる、ということなんですね。
一つ賢くなりました。ありがとうございました。・・・ああああ、悔しい。
- 投稿日:2020-08-30T20:30:50+09:00
VScode+RemoteWSLでWindowsのPython実行環境を作る
この記事ではVScodeとWSLを用いてWindows上で動くPythonの実行環境を作成する方法を解説します。
メリット
具体的な構築方法に入る前にまず、多くあるPython実行環境の中でこれを選ぶメリットを解説したいと思います。
1. VSCode上でステップ実行等の高度な機能が利用できる
2. インストールするパッケージの管理が容易
3. 必要な機能を選んで使用することができる。まず1に関してVSCodeを用いることで特別な設定をほぼ行うことなくプログラムのステップ実行(一行ずつ挙動を確認しながら実行すること)やオートフォーマット(スペーシングなどのプログラムの見た目の調節を自動で行う機能)を利用することができます。
次に2に関して。この環境ではPythonにデフォルトで備わっている仮想環境の機能を使用します。そのため、pipによるコマンドベースでのパッケージの管理を行うことができます。また、VSCodeから使用することでactiveな仮想環境の切り替えを容易に行うことが可能です。
最後に3つ目に関してです。今回構築する環境でインストールするのはVSCode関連では拡張機能のRemote-WSLとPython、WSL関連ではPythonのみです。ミニマムな構築からVSCodeの拡張機能を用いた便利機能満載の構築まで自分で選択して行うことができます。構築手順
それでは実際の構築手順を確認していきます。すでに多くの記事が存在しているため、VSCodeのインストールおよびWSLのインストールに関しては省略させていただきます。
WSL側のセットアップ
Pythonのインストール。お好みでバージョン指定なんかを入れてください。
sudo apt install -y python
プロジェクトのディレクトリを作り、その配下に仮想環境フォルダを作成します。
mkdir sample_project
cd sample_project
python3 -m venv venv
VS Code側のセットアップ
拡張機能の「Remote-WSL」をインストールしてください。
その後、一度Remote-WSLを開き(左下、緑色のアイコン)、Remote-WSL側に拡張機能の「Python」をインストールしておいてください。(Windows側とRemote-WSL側で拡張機能が分けて管理されています。すでにPythonの拡張をインストールしている場合でも追加の設定が必要です)コード実行方法
Remote-WSLの機能を用いてVS Code上からWSL上に作成したsample_projectフォルダを開きます(メニューのファイル→フォルダを開くより)。
この状態でVS Codeがフォルダに配置されている仮想環境を認識し、自動で読み込みを行ってくれます。実行したい.pyファイルを開き、F5キーを押すことでフォルダ内の仮想環境がactivateされた状態でコード実行を行うことができます。ブレイクポイントの設定等も可能です。
- 投稿日:2020-08-30T20:03:02+09:00
[CovsirPhy] COVID-19データ解析用Pythonパッケージ: SIR model
Introduction
COVID-19のデータ(PCR陽性者数など)のデータを簡単にダウンロードして解析できるPythonパッケージ CovsirPhyを作成しています。パッケージを使用した解析例、作成にあたって得られた知識(Python, GitHub, Sphinx,...)に関する記事を今後公開する予定です。
英語版のドキュメントはCovsirPhy: COVID-19 analysis with phase-dependent SIRs, Kaggle: COVID-19 data with SIR modelにて公開しています。
今回は基本モデルSIR modelについて紹介します。実データは出てきません。
英語版:Usage (details: theoretical datasets)1. 実行環境
CovsirPhyは下記方法でインストールできます!
Python 3.7以上, もしくはGoogle Colaboratoryをご使用ください。
- 安定版:
pip install covsirphy --upgrade
- 開発版:
pip install "git+https://github.com/lisphilar/covid19-sir.git#egg=covsirphy"
# データ表示用 from pprint import pprint # CovsirPhy import covsirphy as cs cs.__version__ # '2.8.0'
実行環境 OS Windows Subsystem for Linux Python version 3.8.5 2. SIR modelとは
Susceptible(感受性保持者)がInfected(感染者)と接触したとき、感染する確率をEffective contact rate $\beta$ [1/min]と定義します。$\gamma$ [1/min]はInfectedからRecovered(回復者)に移行する確率です12。
\begin{align*} \mathrm{S} \overset{\beta I}{\longrightarrow} \mathrm{I} \overset{\gamma}{\longrightarrow} \mathrm{R} \\ \end{align*}3. 連立常微分方程式
総人口$N = S + I + R$として、
\begin{align*} & \frac{\mathrm{d}S}{\mathrm{d}T}= - N^{-1}\beta S I \\ & \frac{\mathrm{d}I}{\mathrm{d}T}= N^{-1}\beta S I - \gamma I \\ & \frac{\mathrm{d}R}{\mathrm{d}T}= \gamma I \\ \end{align*}4. 無次元パラメータ
このまま扱っても良いですが、パラメータの範囲を$(0, 1)$に限定するため無次元化します。今回の記事では出てきませんが、実データからパラメータを計算する際に効果を発揮します。
$(S, I, R) = N \times (x, y, z)$, $(T, \beta, \gamma) = (\tau t, \tau^{-1}\rho, \tau^{-1}\sigma)$, $1 \leq \tau \leq 1440$ [min]として、
\begin{align*} & \frac{\mathrm{d}x}{\mathrm{d}t}= - \rho x y \\ & \frac{\mathrm{d}y}{\mathrm{d}t}= \rho x y - \sigma y \\ & \frac{\mathrm{d}z}{\mathrm{d}t}= \sigma y \\ \end{align*}このとき、
\begin{align*} & 0 \leq (x, y, z, \rho, \sigma) \leq 1 \\ \end{align*}5.(基本/実効)再生産数
(基本/実効)再生産数 Reproduction numberは次の通り定義されます3。
\begin{align*} R_t = \rho \sigma^{-1} = \beta \gamma^{-1} \end{align*}6. データ例
パラメータ$(\rho, \sigma) = (0.2, 0.075)$及び初期値を設定してグラフ化します。
# Parameters pprint(cs.SIR.EXAMPLE, compact=True) # {'param_dict': {'rho': 0.2, 'sigma': 0.075}, # 'population': 1000000, # 'step_n': 180, # 'y0_dict': {'Fatal or Recovered': 0, 'Infected': 1000, 'Susceptible': 999000}}(基本/実効)再生産数:
# Reproduction number eg_dict = cs.SIR.EXAMPLE.copy() model_ins = cs.SIR( population=eg_dict["population"], **eg_dict["param_dict"] ) model_ins.calc_r0() # 2.67グラフ表示:
# Set tau value and start date of records example_data = cs.ExampleData(tau=1440, start_date="01Jan2020") # Add records with SIR model model = cs.SIR area = {"country": "Full", "province": model.NAME} example_data.add(model, **area) # Change parameter values if needed # example_data.add(model, param_dict={"rho": 0.4, "sigma": 0.0150}, **area) # Records with model variables df = example_data.specialized(model, **area) # Plotting cs.line_plot( df.set_index("Date"), title=f"Example data of {model.NAME} model", y_integer=True, filename="sir.png" )7. 次回
基本モデルSIR modelをCOVID-19用に改変したモデルSIR-F modelについて紹介します。
- 投稿日:2020-08-30T19:34:37+09:00
環境構築
初めに
pythonの環境を構築しました。経緯をまとめます。
VSCODEのインストール
VSCODEのダウンロード
https://code.visualstudio.com/
を開いたらダウンロードのボタンを押す。
ダウンロードが完了したら指示に従いインストールする。
指示に従い「次へ」
OSを再起動する。pythonのインストール
任意の場所に「mypythonproject」というフォルダを作成する。
ターミナル(右下のエリア)に記載のある
「PS C:\Users\simps\OneDrive\ドキュメント\MyPythonProject>」←の後に「python」と入力
入力後、「ENTER」を押す。WINDOWSがインストール画面(python3.8)を開く。
インストールする。
- 投稿日:2020-08-30T19:33:38+09:00
Python3のリスト・辞書型の書き方
型について
# List型 sampleA = [[name,"Sato"],[age,1]], [[name,"Mori"],[age,20]] # Dictionary型 sampleB = {"apple":1, "orange":2} # 型を調べる type(sampleA) type(sampleB) #List型からKey/Valueを取り出す for index, value in enumerate(sampleA) #辞書型からKey/Valueを取り出す for index, value in items(sampleA)
- 投稿日:2020-08-30T19:11:09+09:00
Colaboratoryで競馬データスクレイピング
Colaboratoryで競馬データのスクレイピング
競馬データのスクレイピングをしたい、かつ機械学習となったら、Colaboratory便利なので、
Colaboratoryで競馬のスクレイピングをしたコードをメモします。(htmlの変更でスクレイピングできなくなるかもしれませんので注意してください。2020.8/30動作確認済み)
以下コード
sample.ipynb#Chromiumとseleniumをインストール #「!」印ごとColaboratoryのコードセルに貼り付けます。 !apt-get update !apt install chromium-chromedriver !cp /usr/lib/chromium-browser/chromedriver /usr/bin !pip install selenium#BeautifulSoupのライブラリをインポート from bs4 import BeautifulSoup import requests import re import pandas as pdrace_date ="2020" race_course_num="06" race_info ="03" race_count ="05" race_no="01" url = "https://race.netkeiba.com/race/result.html?race_id="+race_date+race_course_num+race_info+race_count+race_no+"&rf=race_list" # 該当URLのデータをHTML形式で取得 race_html=requests.get(url) race_html.encoding = race_html.apparent_encoding race_soup=BeautifulSoup(race_html.text,'html.parser')# 無駄な文字列を取り除いてリストへ格納 def make_data(data): data = re.sub(r"\n","",str(data)) data = re.sub(r" ","",str(data)) data = re.sub(r"</td>","'",str(data)) data = re.sub(r"<[^>]*?>","",str(data)) data = re.sub(r"\[","",str(data)) return data# レース表だけを取得して保存 HorseList = race_soup.find_all("tr",class_="HorseList") # レース表の整形 # 表の横列の数=15("着順,枠,馬番,馬名,性齢,斤量,騎手,タイム,着差,人気,単勝オッズ,後3F,コーナー通過順,厩舎,馬体重(増減)) col = ["着順","枠","馬番","馬名","性齢","斤量","騎手","タイム","着差","人気","単勝オッズ","後3F","コーナー通過順","厩舎","馬体重(増減)","出馬数"] # 出馬数をカウント uma_num = len(HorseList) df_temp = pd.DataFrame(map(make_data,HorseList),columns=["temp"])df = df_temp["temp"].str.split("'", expand=True) df.columns= col df["出馬数"] = uma_num df最後に
あとは日付などを変えていけばたくさんスクレイピングできます。
環境構築もいらないColaboratoryはやっぱり便利ですね。参考
https://qiita.com/Mokutan/items/89c871eac16b8142b5b2
https://qiita.com/ftoyoda/items/fe3e2fe9e962e01ac421
- 投稿日:2020-08-30T18:50:31+09:00
TestData 作成ライブラリ Mimesisの紹介
はじめに
大量の(それっぽい)企業データがあったら良いなぁという状況があり、Mimesisというライブラリが便利だったので紹介します
対象
フロントエンジニア、テストデータを作成したい人、Pythonが好きな人
python3.6以上がインストールされていることゴール
インストール〜簡易的なJSONテストデータを出力できるまで
Mimesisとは
テストデータ作成ライブラリ
Fakerが有名のようだが、法人データ作成がないと思われた
Mimesisでは法人データが作成でき、かつちょっと変わったHomePageだとか
IPアドレスのようなデータも作成できるようなので、このライブラリを使用してデータを作成してみました
Mimesis公式インストール
Mimesisのインストールはpipのみ
$ pip install Mimesispipってとても優秀なやつだと、毎回思います(コピペ)
Getting Started
簡単な使い方
main.pyimport mimesis # テストデータを日本語に設定 g = mimesis.Generic('ja') # 会社名と法人タイプ print("{0} {1}".format(g.business.company_type(), g.business.company())) # homePage print(g.internet.home_page())起動&閲覧
ターミナルで
$ python main.py 株式会社 デンカ生研 https://bantay.moscowなんとなくありそうな会社名じゃないでしょうか
JSONデータとして出力
せっかくなのでJSONのテストデータを作成してみます
(SwaggerにしろImportするにしろそのほうが便利なので)ソース
先程のソースを
main.pyimport json from datetime import date, datetime import mimesis # 日付と数値のフォーマットを揃える(JSON出力時にはこれを用意しておくと便利) def format_default(obj): if isinstance(obj, datetime) or isinstance(obj, date): return obj.isoformat() if isinstance(obj, decimal.Decimal): #小数部分が無ければIntで返す if float(obj).is_integer(): return int(obj) else: return float(obj) raise TypeError # 複数件セットできるようにDictを準備 data = [] g = mimesis.Generic('ja') # 複数件生成してDictにセット(必要件数分ここを変更 for idx, x in enumerate(range(0, 2)): ins_data = {} ins_data['company_id'] = idx # Id ins_data['company_name'] = "株式会社 {0}".format(g.business.company()) # 会社名 ins_data['foundation_date'] = g.datetime.formatted_date("%Y/%m") # 設立年月日 ins_data['postal_code'] = g.address.postal_code() # 郵便番号 ins_data['state'] = g.address.state() # 都道府県 ins_data['city'] = g.address.city() # 〜市 ins_data['street'] = g.address.street_name() # 番地 ins_data['home_page'] = g.internet.home_page() # homepage data.append(ins_data) # Dictに追加 # DictをJSONとしていい感じに整形して出力 print(json.dumps(data, default=format_default, indent=2, ensure_ascii=False))$ python main.py [ { "company_id": 0, "company_name": "株式会社 三菱UFJフィナンシャルグループ", "foundation_date": "2003/08", "postal_code": "559-9285", "state": "秋田県", "city": "松山市", "street": "目黒", "home_page": "https://trachytes.frl" }, { "company_id": 1, "company_name": "株式会社 大丸", "foundation_date": "2004/02", "postal_code": "564-0918", "state": "福島県", "city": "太田市", "street": "要町", "home_page": "https://arvin.tui" } ]どうでしょう、見てるだけでも楽しい感じがしますね
あとはこんな感じで
$ ptyhon main.py > testData.jsonテストデータ(JSON)を作成して、SwaggerやFrontのMock用データに利用しています
おまけ
最後に公式に書いてあることですがどんなデータを作成できるか紹介しておきます
(他にも使い所がわからないような面白いものもあるのでRandom
idやamountなど
Address
countryやcityなど
Business
companyやcompany_typeなど
Datetime
daysやhoursなど
Food
drinkやvegetableなど (これ面白いですよね
Person
first_nameやemail, blood_typeなど
Text
alphabetやanswerなど
Development
osやversionなど
File
file_nameやmime_typeなど
Hardware
cpuやscreen_sizeなど
Internet
home_pageやhttp_method, ip_v4など
Numbers
complex_numberやintegers(start=0, end=10, n=10)など
Path
homeやrootなど
Path
homeやrootなど
- 投稿日:2020-08-30T18:31:37+09:00
[Python × AWS × Serverless] PyCon JP 2020 の登壇で言い残したこと
Python × AWS × Serverless 初学者が次の一歩を踏み出すためのテクニック
というタイトルで登壇してきました。社外の登壇は超久々でとても緊張しました。
当初考えてた内容から路線変更したりとか、人はなぜ締め切りをめいっぱい使ってしまうのか、とか、色々考えたりしながらなんとか形にはなりましたが、構成や個別のトピックとか思い返せばまだ言い足りないことがあったなと思いましたのでここで補足しようと思います。
スライドは SpeakerDeck で公開しています。
スライドタイトルについて
内容的に Tips の紹介という趣のスライドなので、「これで初学者を抜け出せるのか?」って疑問にはハッキリ回答できてないかもなぁ、、、と感じました。
もちろんタイトル詐欺の意図はありません。自分なりにこのタイトルと内容を選んだ背景が(一応は)ありますので記しておきます。
私のバックグラウンドですが、AWS方面は割と知見があるものの、アプリ開発のがっつりした経験は持っていない、といったスキルセットです。このような背景があって、基本的なディレクトリ構成とか、どうやって設計していくかとか、そのへんの慣れがなく戸惑っていた側面が大きかったんです。で、わからないがゆえに「サーバーレスに固有な何かがあるのでは?」みたいな不安も出てきて、どうするのが良いのかわからなかったです(自覚的ではなかったのでなかなか解消できませんでしたが)。
その後、(本発表でも喋ったように)サーバーレスといえどアプリ開発部分の考え方は普通に言語のプラクティスに従えば良いとわかりました。これからサーバーレスに挑戦される皆さまには、必要以上に恐れることないよということが伝わればいいなと。
私自身も、以前はサーバーレスについて「次何したらいいのかわからなくてモヤモヤする」状態を経験していました。そのときに知りたかった情報というのが今回のネタだった、ということで発表に至っております。
このような前置きの共有って地味に大事な部分ですよね。共感を得られるストーリーとか Why が提起されないプレゼンって訴求力がないので。ここを省いてしまったことで、視聴者のみなさまに訴求するメッセージがわかりにくくなってしまったかもしれないなと、大いに反省しました。
AWS サービスのリソース名を明示的に与える
「ステージ」云々のセクションで説明を入れそびれた部分です。
Serverless Framework は、デプロイするAWSリソースの名前をステージ間で衝突しないようによしなに命名してくれます。よって、 Serverless Framework を使っていれば多くの場合 Name 属性は必須ではありません。
しかし、あえて「リソース名は明示的に命名した方がいいんじゃないかな?」というのがここで述べたいことです。スライドにも実はちょろっと出てきますが、命名規則というのは例えばこういうものです↓
# template.yml # SQS Queue name Name: ${self:service}-my-queue-${self:provider.stage}こういう命名にすることでサービス間/ステージ間で名前が被らない・予測しやすいリソース名管理ができます。
反対意見として、「インフラは全部コードで管理していくんだから、人間が見るための名前に気を遣う必要なくない?」とする考え方もあります。私もどっちかと言えばそっちを支持したい派です。
リソース名の衝突や、文字数制限 (例: lambda の関数名は64文字まで) などの問題を気にしなくて済む分、自分で変に命名しない方が楽だったりします。命名規則の管理も、(スタックの)ソースの記述が増えてうるさくなっちゃいいますし。できるなら名前はおまかせの方が嬉しいんです。
では、何故明示的な命名をした方がよいのでしょうか。個人的にうれしいポイントは2つあって、それぞれ以下の通りです。このへんは自分が受け持ってる案件の状況がバイアスに入ってると思います。たぶん。
Lambda のログを検索しやすい
Step Functions をよく利用するので、複数の Lambda にまたがってログを検索したいシーンがちょいちょい出てきます。
ログの検索にはいまのところ CloudWatch Logs Insights を使うケースがほとんどなのですが、その時のロググループの名前が(明示的に命名規則を付けていると)スッキリしますので、検索がちょっとだけやりやすいです。
おそらく、Datadogを利用しつつログ検索はタグで絞るようにするとか、(命名規則に頼らずとも)色々やりようはあるんだろうと思うのですが、今自分が採れる選択の中ではこのやり方が運用シーンにも即しておりそこそこ妥当性のあるやり方かなと思っています。
※このへんは周辺ツール (CLI や Datadog など)をもうちょっと充実させれば景色も見解も変わってくると思います
リソース名を参照する時に見通しがよく、保守しやすい
スタックの記述とアプリ側の記述、どっちもある程度スッキリ書くためには命名規則はあった方がメリットが大きいと思います(より主張したいメリットはこっちです)。
シンプルな構成図なら CloudFormation の GetAtt やら Ref やらで値を引いてやればいいので、さほど問題じゃありません。命名規則が役立つのは、構成図が膨れてきた頃合いです。
例えば、SQS のキューを複数作っている構成を想像してみます。Python のコード上ではキューのエンドポイント、すなわち Queue URL (AccountId, Region, QueueName の3つによって特定可能) が必要です。
作成したキューの URL を Ref で参照して lambda 環境変数に渡してあげてもよいのですが、個人的にはアカウントID、リージョン、キューの Name 属性をそれぞれ渡してあげれば十分かな、と思います。
アカウントIDやリージョンは他のAWSサービスを利用する場合にも出番がありそうです。あって困るものではなさそうですし、Queue URL を組み立てるユーティリティ関数の実装もごく簡単ですので、小回りが効きそうな方を選択するのが良いだろうと考えています。
Python のコードベースとしての管理は上記の通りとして、今度はスタック側の管理を考えてみます(どちらかと言えば、命名規則のメリットが大きいのはこちらだと考えます)。
さっきの SQS のキュー名の定義を再掲します。キューの名前は、
service
とstage
を join して作るイメージでした。# 【再掲】 template.yml # SQS Queue name Name: ${self:service}-my-queue-${self:provider.stage}実際のスタック定義としては、上記の Name の文字列全体(あるいは
my-queue
の部分)をテンプレートの環境変数 or custom セクションで宣言してあげる感じになります。このやり方をすることで、他所のリソース(例えば IAM ポリシーの定義)からの参照が比較的スッと書けます。名前の部分は変数として参照可能な宣言を行っているので、 ARN などは単に join して組み立てるだけです。
ここで「いやいや、 Ref とか GetAtt とかあるじゃん」という指摘があると思いますが、それらを迂闊に使うのは個人的にあまりおすすめできません。
特にポリシー周りにおいて、これらを濫用すると CloudFormation の循環参照が起こりやすい気がします。このへんは経験則によるところが大きく明確な文章で根拠を示せませんが、私としては軽々しく AWS リソースを直接参照する関数を使いたくないんですよね...。代えて、依存先として害が少ない環境変数や、 custom セクションの変数に依存させる戦略を採っています。意識的に(特にIAMポリシー周りでの) Ref, GetAtt は使用を控えめにしています。
ただでさえ CloudFormation のデバッグは苦行なのに、構成が大きくなってから後で CloudFormation の循環参照問題に頭抱えるハメになるの、イヤですよね...? あれは人類がデバッグするものじゃないです...
このような理由があって、私はリソース名を(多少冗長で面倒であったとしても)明示的に宣言するのが好みです。この節で書いたことはあくまで副次的な効果ではありますが、私としては結構重く見ています。
おわり
サーバーレスのネタって、概要をさらう資料やチュートリアルをこなす程度の情報なら割とそのへんに転がってるのですが、結局それだけでは実際の開発シーンにおいて痒い部分を解消してくれない実感があったので、そのへんを埋めたい思いで応募してみました。
今の私が伝えたいことはおおよそ喋れたと思いますが、AWS周りの基礎知識はもっと深堀りできる部分だったので45分セッションで応募してみても良かったかもしれません(Python 成分が薄くなっちゃうので TPO 的にどうなの、とは思います)
一方で、残念ながら思ったより反応が少なめだったので、今回の話自体に需要があったのかどうか、今回の発表からはいまひとつ感触がつかめないところではありました(裏番組のセッションが強すぎた点、私の発表自体も改善余地があった点、PyCon の主要な参加層にはミートしづらいネタだった、など考えうる要因は複数あります)。需要皆無ということはおそらくないはずなので、場を変えて喋ってみるなどして探っていこうと思います。ベースの資料はできたことですし。
実践寄りな知識をもうちょっと体系立てて書けたら良いなとも思います。次回の技術書典で頑張ってみるというのも良いかもしれませんね。
- 投稿日:2020-08-30T18:16:24+09:00
【Python】いろんな型について調べてみた!(typing)
はじめに
python3.5 より python にも型という概念が組み込まれました。
使用するには、import typing
を指定してあげる必要があり、型のチェックにはmypy
を使用します。mypy について
インストール
まず、
mypy
を使用するためにパッケージをインストールします。$ pip install mypy設定ファイル
mypy.ini
を以下のように作成し、そこに設定情報を記入します。mypy.ini[mypy] python_version = 3.8 disallow_untyped_calls = True disallow_untyped_defs = True disallow_untyped_decorators = True disallow_incomplete_defs = True check_untyped_defs = True pretty = True [mypy-django.*] ignore_missing_imports = True設定できるものについては、こちら を参照してください。
いろんな使用方法
組み込み型
組み込み型は、そのまま使用することができます。
ただ、list
には、typing.List
というのもあり、こちらの場合、配列に入れる変数の型まで指定できるので、typing.List
を使うほうが良いかと思います。num: int = 1 float_num: float = 1.11 is_something: bool = True text: str = 'hoge' byte_str: bytes = b'fuga' arr: list = [0, 1, 2, 3]Any 型
何の型が変数に入るか分からない場合は、
Any
を使用します。
この型が指定されている場合は、型検査は行われません。something: Any = Something()全ての型の基底クラスである
object
も同じように使用できますが、object
は、ある値が型安全な方法で任意の型として使えることを示すために使用し、Any
はある値が動的に型付けられることを示すために使用します。Dict 型
dictionary 型を使用する場合は、
typing.Dict
を使用します。dictionary: Dict[str, int] = {'hoge': 1, 'fuga': 2}Tuple 型
タプル型を使いたい場合は、
typing.Tuple
を使用します。something: Tuple[str, int] = ('hoge', 1)Set 型
set 型を使用したい場合は、
typing.Set
を使用します。
set 型は、重複しないようをのコレクションです。something: Set[int] = {6, 7}Sequence 型
シーケンス型を使用したい場合は、
typing.Sequence
を使用します。
シーケンス型は、順番(シーケンシャル)に処理するためのデータ構造です。something: Sequence = [0, 1, 2]Iterator 型
イテレータ型を使用したい場合は、
typing.Iterator
を使用します。
イテレータ型はイテラブルなクラスで__iter__()
を持っている必要があります。something: Iterator = IterableClass()Mapping 型
マッピング型を使用したい場合は、
typing.Mapping
を使用します。
マッピング型は任意のキー探索を行うためのデータ構造で、Immutable な型です。something: Mapping[str, int] = {'hoge': 1, 'fuga': 2}Union 型
ユニオン型を使用したい場合は、
typing.Union
を使用します。
ユニオン型では、複数の型を指定することができます。something_mapping: Mapping[str, int] = {'hoge': 1, 'fuga': 2} something_union: Union[int, None] = something_mapping.get('hoge') # 1 something_union: Union[int, None] = something_mapping.get('piko') # NoneOptional 型
typing.Optional
は、typing.Union
型のNone
が必ず選択されているようなイメージです。something_mapping: Mapping[str, int] = {'hoge': 1, 'fuga': 2} something_optional: Optional[int] = something_mapping.get('hoge') # 1 something_optional: Optional[int] = something_mapping.get('piko') # NoneCallable 型
関数を使用したい場合は、
typing.Callable
を使用します。def something() -> bool: return True something_callable: Callable = somethingLiteral 型
決まった値しか入らないことを保証したいときは、
typing.Literal
を使用します。mode: Literal['r', 'rb', 'w', 'wb'] = 'r' # OK mode: Literal['r', 'rb', 'w', 'wb'] = 'a' # NGAnyStr 型
他の種類の文字列を混ぜることなく、任意の種類の文字列を許す関数によって使われるようにしたいときは、
typing.AnyStr
を使用します。def concat(a: AnyStr, b: AnyStr) -> AnyStr: return a + b concat(u"foo", u"bar") # OK unicodeが出力されます concat(b"foo", b"bar") # OK bytesが出力されます concat(u"foo", b"bar") # NG unicodeとbytesが混ざっているためエラーになります型のエイリアス
これまでの型を任意の型名で定義することができます。
Vector = List[float] ConnectionOptions = Dict[str, str] Address = Tuple[str, int] Server = Tuple[Address, ConnectionOptions]NewType
異なる型を作るためには
typing.NewType
を使用します。UserId = NewType('UserId', int) some_id = UserId(524313)ジェネリックス
Generic 型を使用するには、以下のようにします。
T = TypeVar('T') # これが Generic 関数 # l にはどんな型でも良い Sequence 型が使える def first(l: Sequence[T]) -> T: return l[0]ユーザ定義のジェネリック型は以下のように使用できます。
T = TypeVar('T') class LoggedVar(Generic[T]): def __init__(self, value: T, name: str, logger: Logger) -> None: self.name = name self.logger = logger self.value = value def set(self, new: T) -> None: self.log('Set ' + repr(self.value)) self.value = new def get(self) -> T: self.log('Get ' + repr(self.value)) return self.value def log(self, message: str) -> None: self.logger.info('%s: %s', self.name, message) logger: LoggedVar[str] = LoggedVar('hoge', 'Test', Logger()) logger.get() # T は、str 型として扱われるキャスト(typing.cast)
一度、定義した型を変更したいときは、
typing.cast
を使ってキャストします。
処理をできる限り速くするため、実行時には意図的に何も検査しません。a = [4] b = cast(List[int], a) c = cast(List[str], a) # [4] が文字列になることはないので、変換したい場合は、自分で変換処理を追加する定数(typing.Final)
定数を使用する場合は、
typing.Final
を使用します。SOMETHING: Final[str] = 'Something'おわりに
いかがだったでしょうか?
型を駆使することでより強固なアプリケーションの作成が行えるかと思います。
ただ、型を指定しているからといって、特別処理が早くなることはないので、そこはご注意ください。
実際に動作を見ていないものもあるので、間違っているところなど見つけましたら、お知らせいただけると助かります。参考
- 投稿日:2020-08-30T18:13:50+09:00
DiscordのSimplePollっぽいやつをスクラッチ実装しようとしたら手間取った
↑こういうのを導入しようという話がありましたが、敢えて他にbotを入れるよりも自前のPython botで作った方が早そう&メンテしやすそうだったのでしました
手間取ったポイント
- 「自分がしたばかりの発言」にリアクションをつける方法
- リアクション絵文字の指定
コード
#poll @bot.command() async def poll(ctx, *args): text = "**{}**".format(args[0]) n = len(args)-1 if(n == 0): await ctx.send("ないようがないよう") return if(n > 20): await ctx.send("ないようがありすぎるよう") return chars = "abcdefghijklmnopqrstuvwxyz" disc = "" for i in range(n): disc += ":regional_indicator_{}:{}\n".format(chars[i],args[i+1]) embed = discord.Embed(description=disc) await ctx.send(text,embed=embed) for i in range(n): emoji = "??????????????????????????" if emoji: lm = ctx.channel.last_message await lm.add_reaction(emoji[i])手間取ったポイント1
- 「自分がしたばかりの発言」にリアクションをつける方法
ctx.message
でコマンドのきっかけとなったメッセージオブジェクトは拾えますが、crx.send
で送ったbot自身の発言のメッセージにリアクションで投票ボタンをつける必要があるので、それは別に取得する必要があります。最初は、https://discordpy.readthedocs.io/ja/latest/api.html#discord.TextChannel.last_message_id
last_message_id
でIDを取得し、https://discordpy.readthedocs.io/ja/latest/api.html#discord.TextChannel.fetch_messagefetch_message
でなんとかなるかと思いましたが、これで得られたmessage
オブジェクトがなんか違うものだったらしく、add_reaction
の属性なしと言われて失敗。結論的にはhttps://discordpy.readthedocs.io/ja/latest/api.html#discord.TextChannel.last_message
last_message
という属性でメッセージオブジェクトがそのまま取得できたのでそれで敢行。これだと任意のメッセージIDから取得することはできないですが、今回はそこまで必要なかったのであきらめました。ちょっと時間差があるらしく、add_reaction
の直前にしないと一つ前のメッセージにリアクションをつけてしまうことがありました。手間取ったポイント2
公式リファレンス → https://discordpy.readthedocs.io/ja/latest/faq.html
テキストであれば、普段discordで打ってるように
:hogehoge:
と打てばそのまま絵文字になってくれますが、リアクションだとそうはいかないようです。Unicode絵文字をそのまま打ったり、{THUMBS UP SIGN}
等のキーワード(Discord内のそれとは別)が必要なようです。
また、今回わかったこととして、サーバー独自の絵文字と、デフォルトの絵文字では方法が異なるということです。サーバー独自の絵文字の場合
サーバーのIDを指定して、絵文字の名前で検索する必要があります。
guild = bot.get_guild('ここにサーバーのID') emoji = discord.utils.get(guild.emojis, name='ここに絵文字のDiscordでの呼び方') if emoji: await message.add_reaction(emoji)また、Discordのチャットで
\:hogehoge:
などと発言するとIDを表示してくれるので、ID直打ちも可です。そんな手間は変わらないですが。サーバーIDが分からなければ、開発者モードを有効にしてサーバーアイコンを右クリックすると得られます。上のコードの
bot
は、参考にしたコードによってはclient
になっているかもしれません。いずれにせよ、最初に指定した変数名と一致させる必要があります。Unicodeにデフォルトで入っている絵文字の場合
こっちの方がめんどくさかったです。この場合はコード内でそのままUnicode絵文字が使えるので、
emoji='?'
というような形で指定することができます。しかし、これのA,B,C……はどうやって打てばよいでしょうか? Discord内ではregional_indicator_a:
等で出力できますが、これはあくまでDiscord内の呼び名であり、例えば上のコードでemoji = discord.utils.get(guild.emojis, name='regional_indicator_a')
とやってもAの絵文字は出てきません。コピペしても:regional_code_a:
というテキストがコピーされて絵文字にはなりません。なんだこの制御。結論的にいうと、
:regional_indicator_a:
を\
でリテラル表示してそれをコピーして文字列を作りました。26文字……。それが??????????????????????????という文字列です。これがUnicode上でどういう位置づけになって、皆様のOS上でどのように表示されているかは分かりませんが、めんどくさかった……。
- 投稿日:2020-08-30T18:02:21+09:00
Windows10でGoogle Assistantを利用する方法
概要
WindowsでGoogle Assistantを利用したいと考えた時に情報が少なかったので、調べたことをまとめておきます。
ただし、2020年8月19日現在では、利用できる機能が限られているようです。詳しくは以下でまとめておきます。実行環境
・python == 3.6
・windows10利用できる機能
こちらから引用しました。
https://developers.google.com/assistant/sdk/overview
Supported architectures All gRPC platforms Supported languages All gRPC languages Hands-free activation No Audio capture and playback Reference code is provided Conversation state management Reference code is provided Timers and alarms No Playback of podcasts and news No Broadcast voice messages No Visual output (HTML5) of Assistant responses Yes 最新情報は、こちらのURLから確認できます。
https://developers.google.com/assistant/sdk/release-notes環境構築
- Pythonの環境構築はここでは省きます。
Google Cloud Platformを開いて、左上の"Create Project"をクリック。https://console.cloud.google.com/cloud-resource-manager
Project nameに好きな名前を入れます。(プロジェクトIDは変更も可能ですが、すべて小文字で半角数字で終わっていなければいけません。)
Google Assistant APIsを別タブで開きます。https://console.developers.google.com/apis/api/embeddedassistant.googleapis.com/overview
自分の作ったプロジェクトを選択し、Openをクリック。
画像の通りに選択欄を埋めます。そして、"What credentials do I need?"をクリック。その後、"SET UP CONCENT SCREAN"をクリック。
Projectnameを決めます。ただし、Googleなど有名な企業名が入っていると認められない場合があります。その後、一番下の"SAVE"をクリック。
右側のカギマークの付いた"Credentials"に戻ります。上にある"+CREATE CREDENTIALS"から"Help me choose"を選択。
コマンドプロンプトから以下を入力し、Google Assistant SDKをインストール。
py -m pip install google-assistant-sdk[samples]16 次に以下を入力し、インストール。
py -m pip install google-auth-oauthlib[tool]17 以下のようにコマンドプロンプトに入力してください。
google-oauthlib-tool --client-secrets C:\先程のjsonファイルの場所\ --scope https://www.googleapis.com/auth/assistant-sdk-prototype --save --headless18 表示されたURLをコピーして、ブラウザで検索。アカウントとの連携を認めるためにAllowを二回クリック。サインインに必よなコードが表示されるのでコピーします。
19 コマンドプロンプトに戻りコピーしたコードを貼り付けてEnterを入力。jsonファイルの情報がアカウントに保存されます。
20 以上の操作が完了していると、以下のコードで録音と再生のテストができます。
py -m googlesamples.assistant.grpc.audio_helpers21 jsonファイルをがあるフォルダにコマンドプロンプト上で移動します。
22 そのフォルダ上で以下のコマンドを入力します。
googlesamples-assistant-devicetool --project-id 自分のプロジェクトID register-model --manufacturer “製造者名を自分で決めてください” --product-name “製品名を自分で決めてください” --type LIGHT --model “なんでもいいです。”23 最後に以下のコマンドを入力します。
py -m googlesamples.assistant.grpc.pushtotalk --device-model-id “自分のモデルID” --project-id 自分のプロジェクトID24 成功すれば、"Please Enter to send a new request..."と表示されます。Enter入力後、話しかけれると答えてくれます。(ただし、現時点では英語です。)
参考
https://www.lifewire.com/google-assistant-on-windows-4628292
- 投稿日:2020-08-30T17:52:40+09:00
Pythonで解く【初中級者が解くべき過去問精選 100 問】(001 - 004 全探索:全列挙)
1. 目的
初中級者が解くべき過去問精選 100 問をPythonで解きます。
すべて解き終わるころに水色になっていることが目標です。本記事は「001 - 004 全探索:全列挙」になります。
2. 総括
Pythonでは組み合わせの列挙等は
itertools
でも行えるが、練習のためfor文で書いた。
特につまずくことはなかった。3. 本編
001 - 004 全探索:全列挙
001. ITP1_7_B - How Many Ways?
回答
answer_list = [] while True: n, x = map(int, input().split()) if n == 0 and x == 0: break count = 0 for first in range(1, n + 1): for second in range(first + 1, n + 1): for third in range(second + 1, n + 1): if first + second + third == x: count += 1 answer_list.append(count) for answer in answer_list: print(answer)問題文は重複無しとあるので、for文の
first
、second
、third
の範囲が重複しないように気を付けます。
具体的にはsecond
のスタートをfirst+1
、third
のスタートをsecond+1
にします。
002. AtCoder Beginner Contest 106 B - 105
回答
def is_target(num): count = 0 for i in range(1, num+1): if num % i == 0: count += 1 if count == 8: return True else: return False if __name__ == "__main__": N = int(input()) count = 0 for num in range(1, N+1, 2): count += is_target(num) print(count)約数が8であるか否かを
True
、False
で返すis_target
という関数を作成します。
そのあと1~Nまでの奇数についてis_target
でチェックして足し合わせます(Trueは1なのでそのまま足せる)。
003. AtCoder Beginner Contest 122 B - ATCoder
回答
target = 'ACGT' S = input() answer = 0 for start in range(len(S)): if S[start] not in target: continue count = 0 for end in range(start, len(S)): if S[end] not in target: break count += 1 answer = max(answer, count) print(answer)文字列Sから取り出す部分文字列は添え字
start
とend
を使ってS[start : end]
と書けますので、start
とend
それぞれについて、ACGT
であるか否かをチェックしていきます。チェックの際は、
start
のfor文ではACGT
じゃない場合はcontinue
、end
のfor文ではACGT
じゃない場合はbreak
であることに注意します。
004. パ研杯2019 C - カラオケ
回答
N, M = map(int, input().split()) A = [list(map(int, input().split())) for _ in range(N)] answer = 0 for song1 in range(M): for song2 in range(song1+1, M): score = 0 for i in range(N): score += max(A[i][song1], A[i][song2]) answer = max(answer, score) print(answer)
song1
とsong2
についてfor文を回します。
そしてその内側で各生徒(添え字i
)についてsong1
とsong2
の大きいほうの点数を採用します。
- 投稿日:2020-08-30T17:21:37+09:00
リスト処理関数(cons,car,cdr,atom,list)実装例まとめ
拙作記事『7行インタプリタ実装まとめ』について,そろそろSchemeとPython以外にも対応しないとなあと思っていろいろ整理した結果,『S式入力の実装部分がほとんどじゃないこれ?』→『あと,リスト処理内容に基準を設けてないと言語ごとに実装方針がバラバラになりそう』となり,とりあえず『
cons
car
cdr
atom
list
が使える』ようにする記述例を先にまとめていくことにした次第.ターゲット言語上での純LISP機能実装に近いとでもいいますか.※Python版については,これを用いたS式入出力記述例も掲載しています(他の言語版も揃ったら別記事に移すかも).
仕様
- ドット対(cons cells)を定義
- アトムは全て文字列,空リストはNULL
cons
car
cdr
を実装- アトムか否かを判定する
atom
を実装- 文字列の羅列から単方向リストを生成する
list
を実装Python(CPython 3.7.3)
ドット対はペア要素のタプルで定義(不変としたいため).空リストNULLは
None
を使用.#### Cons cells are created by using tuple. #### All of atoms are string and the null value is None. def cons(x, y): return (x, y) def car(s): return s[0] def cdr(s): return s[1] def atom(s): return isinstance(s, str) or s == None def list(ts): if not ts: return None else: return cons(ts[0], list(ts[1:]))利用例は次の通り.
def mkassoc(a, b): if a == None or b == None: return None else: return cons(cons(car(a), car(b)), mkassoc(cdr(a), cdr(b))) def assoc(k, vs): if vs == None: return None else: if car(car(vs)) == k: return car(vs) else: return assoc(k, cdr(vs))>>> vs = mkassoc(list(("hoge", "hage", "hige")), list(("10", "20", "30"))) >>> assoc("hage", vs) ('hage', '20') >>> car(assoc("hage", vs)) 'hage' >>> cdr(assoc("hage", vs)) '20'(上記定義を用いたS式入出力記述例)
#### Cons cells are created by using tuple. #### All of atoms are string and the null value is None. def cons(x, y): return (x, y) def car(s): return s[0] def cdr(s): return s[1] def atom(s): return isinstance(s, str) or s == None def list(ts): if not ts: return None else: return cons(ts[0], list(ts[1:])) #### s_read # '(a b c)' # => ['(', 'a', 'b', 'c', ')'] def s_lex(s): return s.replace('(', ' ( ').replace(')', ' ) ').split() # ['(', 'a', 'b', 'c', ')'] # => ('a', 'b', 'c') def s_syn(s): t = s.pop(0) if t == '(': r = [] while s[0] != ')': r.append(s_syn(s)) s.pop(0) return tuple(r) else: if t == 'None': return None else: return t # ('a', 'b', 'c') # => ('a', ('b', ('c', None))) def s_sem(s): if atom(s): return s elif len(s) == 0: return None elif s[0] == '.': return s_sem(s[1]) else: return cons(s_sem(s[0]), s_sem(s[1:])) def s_read(ss): return s_sem(s_syn(s_lex(ss))) #### s_print def s_prcons(s): sa_r = s_print(car(s)) sd = cdr(s) if sd == None: return sa_r elif atom(sd): return sa_r + ' . ' + sd else: return sa_r + ' ' + s_prcons(sd) def s_print(s): if atom(s): return s elif s == None: return None else: return '(' + s_prcons(s) + ')'>>> s_print(s_read('((Apple . 100) (Orange . 120) (Lemmon . 250))')) '((Apple . 100) (Orange . 120) (Lemmon . 250))' >>> x = s_read('((Apple . 100) (Orange . 120) (Lemmon . 250))') >>> car(x) ('Apple', '100') >>> car(car(cdr(x))) 'Orange' >>> s_print(cdr(x)) '((Orange . 120) (Lemmon . 250))'C言語(gcc 8.3.0)
atom
実装のため,まず,文字列とドット対ポインタの両方を扱うことができるnode_t
構造体を定義,それを用いてドット対cons_t
構造体を定義.空リストNULLはNULLポインタを使用.#include <stdio.h> #include <stdlib.h> /* Cons cells are created by using typedef struct. */ /* All of atoms are char* and the null value is NULL. */ typedef unsigned int value_t; enum NODE_TAG { NODE_STRG, NODE_CONS }; typedef struct _node_t_ { value_t value; enum NODE_TAG tag; } _node_t, *node_t; node_t node(value_t value, enum NODE_TAG tag) { node_t n = (node_t)malloc(sizeof(_node_t)); n->value = value; n->tag = tag; return (n); } typedef struct _cons_t_ { node_t x; node_t y; } _cons_t, *cons_t; node_t cons(node_t x, node_t y) { cons_t c = (cons_t)malloc(sizeof(_cons_t)); c->x = x; c->y = y; node_t n = node((value_t)c, NODE_CONS); return (n); } #define str_to_node(s) (node((value_t)(s), NODE_STRG)) #define node_to_str(s) ((char *)(s->value)) #define car(s) (((cons_t)(s->value))->x) #define cdr(s) (((cons_t)(s->value))->y) #define atom(s) (s->tag == NODE_STRG) #define MAXSTR 64 node_t list(const char s[][MAXSTR], const int n) { node_t r = str_to_node(NULL); for (int i = n - 1; i >= 0; i--) { r = cons(str_to_node(s[i]), r); } return (r); }利用例は次の通り.
#include <string.h> node_t mkassoc(node_t a, node_t b) { if (node_to_str(a) == NULL || node_to_str(b) == NULL) { return NULL; } else { return cons(cons(car(a), car(b)), mkassoc(cdr(a), cdr(b))); } } node_t assoc(node_t k, node_t vs) { if (node_to_str(vs) == NULL) { return NULL; } else { if (strcmp(node_to_str(car(car(vs))), node_to_str(k)) == 0) { return car(vs); } else { return assoc(k, cdr(vs)); } } } int main(void) { const char s1[][MAXSTR] = { "hoge", "hage", "hige" }; const char s2[][MAXSTR] = { "10", "20", "30" }; node_t vs = mkassoc(list(s1, 3), list(s2, 3)); node_t k = str_to_node("hage"); node_t r = assoc(k, vs); printf("car(assoc(\"hage\", vs)) = %s\n", node_to_str(car(r))); printf("cdr(assoc(\"hage\", vs)) = %s\n", node_to_str(cdr(r))); free(vs); free(k); free(r); return (0); }car(assoc("hage", vs)) = hage cdr(assoc("hage", vs)) = 20Common Lisp(SBCL 1.4.16)
あくまで参考.ドット対はクロージャで実現.オリジナルと区別するため,
s_cons
s_car
s_cdr
s_atom
s_list
の名前で定義.空リストNULLはNIL
を使用.;;;; Cons cells are created by using lambda closure. ;;;; All of atoms are string and the null value is NIL. (defun s_cons (x y) (lambda (f) (funcall f x y))) (defun s_car (c) (funcall c (lambda (x y) x))) (defun s_cdr (c) (funcall c (lambda (x y) y))) (defun s_atom (s) (and (not (functionp s)) (not (equal s NIL)))) (defun s_list (s) (if (null s) NIL (s_cons (car s) (s_list (cdr s)))))利用例は次の通り.
(defun s_mkassoc (a b) (if (or (equal a NIL) (equal b NIL)) NIL (s_cons (s_cons (s_car a) (s_car b)) (s_mkassoc (s_cdr a) (s_cdr b))))) (defun s_assoc (k vs) (if (equal vs NIL) NIL (if (equal (s_car (s_car vs)) k) (s_car vs) (s_assoc k (s_cdr vs)))))* (defparameter vs (s_mkassoc (s_list '("hoge" "hage" "hige")) (s_list '("10" "20" "30")))) VS * (s_assoc "hage" vs) #<CLOSURE (LAMBDA (F) :IN S_CONS) {50F3E645}> * (s_car (s_assoc "hage" vs)) "hage" * (s_cdr (s_assoc "hage" vs)) "20"Ruby(CRuby 2.5.5)
ドット対は二要素の配列で定義.空リストNULLは
nil
を使用.#### Cons cells are created by using Array. #### All of atoms are string and the null value is nil. def cons(x, y) [x, y] end def car(s) s[0] end def cdr(s) s[1] end def atom(s) s.is_a?(String) || s == nil end def list(s) s.size == 0 ? nil : cons(s[0], list(s[1..-1])) end利用例は次の通り.
def mkassoc(a, b) if a == nil || b == nil then return nil else return cons(cons(car(a), car(b)), mkassoc(cdr(a), cdr(b))) end end def assoc(k, vs) if vs == nil then return nil else if car(car(vs)) == k then return car(vs) else return assoc(k, cdr(vs)) end end end>> vs = mkassoc(list(["hoge", "hage", "hige"]), list(["10", "20", "30"])) => [["hoge", "10"], [["hage", "20"], [["hige", "30"], nil]]] >> assoc("hage", vs) => ["hage", "20"] >> car(assoc("hage", vs)) => "hage" >> cdr(assoc("hage", vs)) => "20"備考
記事に関する補足
- 参照用を想定していることもあり,エラーチェックもモジュール化もガーベジコレクションもなにそれおいしいの状態.実用のS式パーサとかは既にたくさんあるしなあ.
- 現バージョンの
list
だと,Common Lisp版を含めて『リストのリスト』が作れない…cons
使えばいっか(いいかげん).変更履歴
- 2020-08-30:利用例を連想リスト実装に統一
- 2020-08-30:Rubyの実装例を追加
- 2020-08-30:初版公開(Python,C,Common Lisp)
- 投稿日:2020-08-30T16:36:27+09:00
最適化の前に3次元のグラフの書き方
DEAPにある最適化アルゴリズムのグラフの書き方です。
最適化の前に3次元のグラフの書き方です。
まず、どんな3次元のグラフが描きたいかと言うと、DEAPという遺伝的アルゴリズムのライブラリーにある評価関数です。
多目的最適化を検討したいので、どうしても3次元のグラフ描いた方がわかりやすそうなので、まずは3次元のグラフの書き方です。最適化の評価関数はこの辺を参照してください。
DEAP 1.3.1ドキュメント
だれでも分かる多目的最適化問題超入門三次元のグラフを描くためには、まず、X1、x2の2つの軸で、メッシュ状に点をつくる。
その点に、z軸の値を計算する。
x1、x2、Zの値をAxes3Dで3次元のグラフを描きます。1)メッシュ状の点を作る。
1:
numpy
のarange
関数で、等間隔の数値を作る。
np.arange(-30,30,1)
a-30から30まで、1ずつ間を空けた数値ができます。
2:meshgrid(a,b)
で一次元配列のa,bの交点に格子点が得られ、それをX1,X2に入れます。#メッシュの作り方 import numpy as np a = np.arange(-30,30,1) b = np.arange(-30,30,1) print(a) #[-30 -29 -28 -27 -26 -25 -24 -23 -22 -21 -20 -19 -18 -17 -16 -15 -14 -13 # -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 # 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 # 24 25 26 27 28 29] print(b) #[-30 -29 -28 -27 -26 -25 -24 -23 -22 -21 -20 -19 -18 -17 -16 -15 -14 -13 # -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 # 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 # 24 25 26 27 28 29] X1, x2 = np.meshgrid(a, b) print(X1) #[[-30 -29 -28 ... 27 28 29] # [-30 -29 -28 ... 27 28 29] # [-30 -29 -28 ... 27 28 29] # ... # [-30 -29 -28 ... 27 28 29] # [-30 -29 -28 ... 27 28 29] # [-30 -29 -28 ... 27 28 29]] print(X2) #[[-30 -29 -28 ... 27 28 29] # [-30 -29 -28 ... 27 28 29] # [-30 -29 -28 ... 27 28 29] # ... # [-30 -29 -28 ... 27 28 29] # [-30 -29 -28 ... 27 28 29] # [-30 -29 -28 ... 27 28 29]] import matplotlib.pyplot as plt plt.scatter(X1,X2,s=1) plt.show()確認のために、X1,X2の2次元の散布図を描いてみると、次のようになりました。
ちゃんとメッシュになっています。
次にZ軸の値を計算して、3次元のグラフを作ります。
3:Zは、Ackley関数なるものを計算しました。
数式{f(x_{1}, x_{2})=20-20\exp \biggl( -0.2\sqrt{\frac{1}{n}\sum_{i=1}^{n}x_{i}^2} \biggr) +e-\exp \biggl(\frac{1}{n}\sum_{i=1}^{n}\cos(2\pi x_{i}) \biggr) }とりあず、X1,Zでプロットすると、次のようなグラフになります。
4:3次元のグラフを描きます。
from mpl_toolkits.mplot3d import Axes3D
で3次元のグラフを描くライブラリーをインポートします。
ax.plot_wireframe(X1, X2, Z)
で、3次元のグラフになります。
plot_wireframe
:ワイヤーフレームで表示されます。
Z =20 - 20 * np.exp(-0.2 * np.sqrt((X1 ** 2 + X2 ** 2) / 2)) + np.exp(1) - np.exp((np.cos(2 * np.pi * X1) + np.cos(2 * np.pi * X2)) / 2) plt.scatter(X1,Z,s=1) plt.show() from mpl_toolkits.mplot3d import Axes3D fig = plt.figure() ax = Axes3D(fig) #ワイヤーフレームで表示 ax.plot_wireframe(X1, X2, Z) #面で表示 #ax.plot_surface(X1, X2, Z, rstride=1, cstride=1, cmap='hsv') plt.show()
- 投稿日:2020-08-30T14:56:16+09:00
AtCoderBeginnerContest177復習&まとめ(後半)
AtCoder ABC177
2020-08-29(土)に行われたAtCoderBeginnerContest177の問題をA問題から順に考察も踏まえてまとめたものとなります.
後半ではDEの問題を扱います.前半はこちら
問題は引用して記載していますが,詳しくはコンテストページの方で確認してください.
コンテストページはこちら
公式解説PDFD問題 Friends
問題文
人$1$から人$N$までの$N$人の人がいます。
「人$A_i$と人$B_i$は友達である」という情報が$M$個与えられます。同じ情報が複数回与えられることもあります。
$X$と$Y$が友達、かつ、$Y$と$Z$が友達ならば、$X$と$Z$も友達です。また、$M$個の情報から導くことができない友達関係は存在しません。
悪の高橋君は、この$N$人をいくつかのグループに分け、全ての人について「同じグループの中に友達がいない」という状況を作ろうとしています。
最小でいくつのグループに分ければ良いでしょうか?友達の繋がりをたどって,何人と繋がりがあるのか(=公式解説の友達集合の要素数)を,各まとまりごとに調べていく.
友達同士を同じグループに入れないようにするためには,少なくとも一番大きい友達集合の要素数のグループが必要となり,それが出力すべき答えとなる.
実装は,人$1$から人$N$がどこかのグループに属しているかどうか確認するlistを作成して管理することで,計算の重複をしないようにしている.abc177d.pyn, m = map(int, input().split()) set_dict = {} chech_list = [0] * (n + 1) for i in range(m): a, b = map(int, input().split()) if a in set_dict: set_dict[a].add(b) else: set_dict[a] = {b} if b in set_dict: set_dict[b].add(a) else: set_dict[b] = {a} chech_list[a] = 1 chech_list[b] = 1 ans = 1 for i in range(1, n + 1): if chech_list[i] == 0: continue count = 0 temp_set = set_dict[i] while len(temp_set) > 0: x = temp_set.pop() count += 1 chech_list[x] = 0 for y in set_dict[x]: if chech_list[y] == 1: temp_set.add(y) ans = max(count, ans) print(ans)E問題 Coprime
問題文
$N$個の整数があります。$i$番目の数は$A_i$です。
「全ての$1 \leq i < j \leq N$について、$GCD(A_i,A_j)=1$」が成り立つとき、{$A_i$}は pairwise coprime であるといいます。
{$A_i$}が pairwise coprime ではなく、かつ、$GCD(A_1,…,A_N)=1$であるとき、{$A_i$}は setwise coprime であるといいます。
{$A_i$}が pairwise coprime、setwise coprime、そのどちらでもない、のいずれであるか判定してください。
ただし$GCD(…)$は最大公約数を表します。素因数分解を高速でする方法が思いつかなかった.
公式解説のエラトステネスの篩みて納得.abc177e.pydef gcd(a,b): if b == 0: return a else: return gcd(b,a%b) n = int(input()) a_list = list(map(int, input().split())) a_list.sort() ans2 = a_list[0] for i in range(1, n): ans2 = gcd(ans2, a_list[i]) if ans2 == 1: break if ans2 != 1: print("not coprime") else: flag = 1 max_a = a_list[n - 1] + 1 num_flag_list = [True] * max_a d_list = list(range(0, max_a)) d_list[0] = 1 num_flag_list[0] = num_flag_list[1] = False for i in range(2, int(max_a**0.5) + 1): if num_flag_list[i]: for j in range(i**2, max_a, i): if num_flag_list[j] == True: num_flag_list[j] = False d_list[j] = i p_set = set() for a in a_list: if a == 1: continue temp_p_set = set() while True: p = d_list[a] if p not in temp_p_set: if p in p_set: flag = 0 break temp_p_set.add(p) p_set.add(p) a = a // p if a == 1: break if flag == 0: break if flag == 1: print("pairwise coprime") else: print("setwise coprime")後半も最後まで読んでいただきありがとうございました.
- 投稿日:2020-08-30T14:56:04+09:00
AtCoderBeginnerContest177復習&まとめ(前半)
AtCoder ABC177
2020-08-29(土)に行われたAtCoderBeginnerContest177の問題をA問題から順に考察も踏まえてまとめたものとなります.
前半ではABCまでの問題を扱います.
問題は引用して記載していますが,詳しくはコンテストページの方で確認してください.
コンテストページはこちら
公式解説PDFA問題 Don't be late
問題文
高橋君は青木君と待ち合わせをしています。
待ち合わせ場所は高橋君の家から$D$メートル離れた地点であり、待ち合わせの時刻は$T$分後です。
高橋君は今から家を出発し、分速$S$メートルで待ち合わせ場所までまっすぐ移動します。
待ち合わせに間に合うでしょうか?abc177a.pyd, t, s = map(int, input().split()) if d <= t * s: print("Yes") else: print("No")B問題 Substring
問題文
$2$つの文字列$S, T$が与えられます。
$T$が$S$の部分文字列となるように、$S$のいくつかの文字を書き換えます。
少なくとも何文字書き換える必要がありますか?
ただし、部分文字列とは連続する部分列のことを指します。例えば、'xxx' は 'yxxxy' の部分文字列ですが、'xxyxx' の部分文字列ではありません。$S,T$は$1$文字以上$1000$文字以下なので,全部の可能性を時間内に計算することができます.
例えば,7文字の'abcdefg'と3文字の'efh'を考えると,'abc','bcd','cde','def','efg'の各々に対して,何文字書き換える必要があるか計算し,最小のものが答えとなります.abc177b.pys = input() t = input() min_count = 1000 for i in range(len(s) - len(t) + 1): count = 0 for j in range(len(t)): if s[i+j] != t[j]: count += 1 min_count = min(count, min_count) if min_count == 0: break print(min_count)C問題 Substring
問題文
$N$個の整数$A_1,…,A_N$が与えられます。
$1 \leq i < j \leq N$を満たす全ての組$(i,j)$についての$A_i×A_j$の和を$mod(10^9+7)$で求めてください。求める和は,$A_1×(A_2+...+A_N) + A_2×(A_3+...+A_N) + ... + A_{N-1}×(A_N)$となるので,個別に全て計算するよりも,計算量を抑えることができる.
abc177c.pyn = int(input()) a_list = list(map(int, input().split())) ans = 0 total = 0 mod = 10 ** 9 + 7 for i in range(n): total += a_list[i] if total >= mod: total = total % mod for i in range(n - 1): total -= a_list[i] if total < 0: total += mod ans += a_list[i] * total if ans >= mod: ans = ans % mod print(ans)前半はここまでとなります.
最近は公式の解説がとても丁寧に記述してあったので,詳しい解法はそちらを参考にしてもらえたらと思います.
前半の最後まで読んでいただきありがとうございました.後半はDE問題の解説となります.
後半に続く.
- 投稿日:2020-08-30T14:42:49+09:00
グループごとの移動平均
テスト用データの作成
import pandas as pd dt1={ "label": ["A" for x in range(1,100,1)] , "seq": [x for x in range(1,100,1)] , "val": [x for x in range(10,1000,10)] } dt1=pd.DataFrame(dt1) dt2={ "label": ["B" for x in range(1,100,1)] , "seq": [x for x in range(99,0,-1)] , "val": [x for x in range(10,1000,10)] } dt2=pd.DataFrame(dt2) dt3={ "label": ["C" for x in range(1,100,1)] , "seq": [x for x in range(1,100,1)] , "val": [x for x in range(10,1000,10)] } dt3=pd.DataFrame(dt3) dt=pd.concat([dt1 , dt2 , dt3 ]) #内容の表示 print("元データ") print(dt[dt.label=="A"].head(10)) print(dt[dt.label=="B"].head(10)) print(dt[dt.label=="C"].head(10))元データ label seq val 0 A 1 10 1 A 2 20 2 A 3 30 3 A 4 40 4 A 5 50 5 A 6 60 6 A 7 70 7 A 8 80 8 A 9 90 9 A 10 100 label seq val 0 B 99 10 1 B 98 20 2 B 97 30 3 B 96 40 4 B 95 50 5 B 94 60 6 B 93 70 7 B 92 80 8 B 91 90 9 B 90 100 label seq val 0 C 1 10 1 C 2 20 2 C 3 30 3 C 4 40 4 C 5 50 5 C 6 60 6 C 7 70 7 C 8 80 8 C 9 90 9 C 10 100label別でseq順にソートされているときのvalの5回分の移動平均を求める
以下の命令が主要部分
groupby("label").rolling(5 ,on="seq")dt2=dt.sort_values("seq").copy() dt3=dt2.groupby("label").rolling(5 ,on="seq").mean().reset_index() print("結果データ") print(dt3[dt3.label=="A"].head(10)) print(dt3[dt3.label=="B"].head(10)) print(dt3[dt3.label=="C"].head(10))結果データ label level_1 seq val 0 A 0 1 NaN 1 A 1 2 NaN 2 A 2 3 NaN 3 A 3 4 NaN 4 A 4 5 30.0 5 A 5 6 40.0 6 A 6 7 50.0 7 A 7 8 60.0 8 A 8 9 70.0 9 A 9 10 80.0 label level_1 seq val 99 B 98 1 NaN 100 B 97 2 NaN 101 B 96 3 NaN 102 B 95 4 NaN 103 B 94 5 970.0 104 B 93 6 960.0 105 B 92 7 950.0 106 B 91 8 940.0 107 B 90 9 930.0 108 B 89 10 920.0 label level_1 seq val 198 C 0 1 NaN 199 C 1 2 NaN 200 C 2 3 NaN 201 C 3 4 NaN 202 C 4 5 30.0 203 C 5 6 40.0 204 C 6 7 50.0 205 C 7 8 60.0 206 C 8 9 70.0 207 C 9 10 80.0