20211124のPythonに関する記事は30件です。

TensorFlow Certificateに1日で合格する方法

TensorFlow Developer Certificateに合格しました。受験体験記を共有したいと思います。 合格するためにやったこと DeepLearning.AI テンソルフロー開発者 プロフェッショナル認定 - Coursera ローレンスさんとアンドリューさんの講座をやりました。 無料で動画の視聴ができ、登録しても7日以内に解約すれば無料でテストが受けれます。 https://www.coursera.org/professional-certificates/tensorflow-in-practice 4コースx4Weekありますが、集中してやれば1日で終わります。日本語字幕で動画の自動再生をオンにするのがオススメです。 内容を完全に理解する必要はありません。とにかく、最後まで視聴することが大事です。 試験中はコードを書くことは一切ありません。ローレンスのコードをコピペするだけです。 TensorFlow Certificateの受験方法 受験者向けガイドブック 本人確認 TrueAbility で本人確認書類をアップロードします。健康保険証と顔写真だけでもいけました。 本人確認が終わると1万円で試験を購入できます。 PyCharmのインストール sudo snap install pycharm-community --classic TensorFlow Developer Certificateのプラグインもインストールします。 Code With Meプラグインは無効にします。(必要ならば) 環境構築は不要? Ubuntu 21.04にデフォルトで入ってたPython3.9で試験行いましたが、 ローカルマシンでコードを修正したり実行することはないので環境構築は必要ないです。 試験開始前に 以下のURLを予め開いておきましょう https://github.com/https-deeplearning-ai/tensorflow-1-public/blob/main/C1/W1/assignment/C1_W1_Assignment_Solution.ipynb https://github.com/https-deeplearning-ai/tensorflow-1-public/blob/7c1f14e1dba61a789a808eb8b18a3e573bb3c603/C1/W2/assignment/C1_W2_Assignment_Solution.ipynb https://github.com/https-deeplearning-ai/tensorflow-1-public/blob/51c36523d292ecc144e85601e50a48f479e08725/C2/W4/ungraded_lab/C2_W4_Lab_1_multi_class_classifier.ipynb https://github.com/https-deeplearning-ai/tensorflow-1-public/blob/61b9455ec1a80014ba3be1219f2c6a53bc82e7a3/C3/W3/ungraded_labs/C3_W3_Lab_5_sarcasm_with_bi_LSTM.ipynb https://github.com/https-deeplearning-ai/tensorflow-1-public/blob/main/C4/W4/ungraded_labs/C4_W4_Lab_1_LSTM.ipynb https://colab.research.google.com/gist/GitHub30/3b855bcb6a61dd66dfb8ab070332a5ee/tensorflow-certificate-template.ipynb 試験中わからないことがあれば、落ち着いてググったりしましょう。不要なレイヤーがあれば削除しましょう。 Colaboratoryで試験する方法 Colabを開き、ランタイムをGPUに設定します。(必要ならば) starter.pyのコードをすべて選択しコピーしColabのセルに貼り付けます。 Colabでコードを修正し、実行してモデルを.h5で保存ダウンロードします。 ダウンロードした.h5ファイルを該当のCategoryフォルダ内(e.g. Category1)に移動します。 Submit and Test modelをクリックします。1分たってもうまくいかないときはもう一度クリックします。 問題は全部で5問各5点(25点満点)あります。私は、24点でした。 YouTubeで13点で不合格の方がいらっしゃいましたが合格基準は不明です。 Colab ProならK80(無料版)よりも5倍速いP100が使えます。また、Colabを複数タブ開いて並列実行できます。 1000円課金するだけでtry-and-error沢山できてオススメです。 感想 ラスボスと裏ボスがいるので、落ち着いてGitHubからコピペ(Ctrl+C,Ctrl+V)をしましょう。BackSpaceキーを押すことはあるかもしれませんが、それ以外のキーは使用しませんでした。制限時間5時間ですが、1時間ほどでやめました。adam optimizerはいいぞ 参考 TensorFlow User Group Meetup #11 - Road to TF Developer Certificate [Tensorflow2] tf.data.Datasetを使って時系列TimeSeriesデータを学習データに加工する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

直木n十五プログラム

友達が「直木三十五って今だったら直木何になるんだろう」と言っていたので、プログラムはほとんどわからない初心者ながら、作りました。 def int2kanint(num) : suji = ["","一","二","三","四","五","六","七","八","九"] kugiri = ["","十","百","千"] tani = ["","万","億","兆","京","垓","?","穣","溝","澗","正","載","極","恒河沙","阿僧祇","那由他","不可思議","無量大数"] num = list(map(int,list(str(num)))) kansuji = [] for k, v in zip(range(len(num)), reversed(num)) : keta = [] keta.append(suji[v if v>1 else 0 if k%4 else v]) keta.append(kugiri[k%4 if v>0 else 0]) keta.append((tani[0 if k%4 else int(k/4) if any(list(reversed(num))[k:(k+4 if len(num)>=(k+4) else len(num))]) else 0])) kansuji.append("".join(keta)) kansuji = "".join(reversed(kansuji)) return kansuji if kansuji else "零" print("西暦何年の直樹三十五を知りたいですか") x = input() y = int(x) - 1891 print(str(x)+"年") if (y >= 0) : print("西暦"+str(x)+"年の直木三十五は『直木"+int2kanint(y)+"』です。") else: print("生まれてねえよ。") コードの漢数字変換部はこちらを参考というか丸コピしました。ありがとうございます。 https://qiita.com/m_rafa0110/items/a4df610ab89e73ae1457 簡単に説明すると、直木三十五が入力した年数だと直木何になるのか表示してくれるものになります。 まだ直木三十五が生まれていない場合は"生まれてねえよ。"と表示します。 以上です。ありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pyhton_ローカル変数とグローバル変数

関数外で定義した変数はグローバル変数 num = 50 def printnum(): print(num) print(num) printnum() print(num) 50 50 50 関数の中で代入した変数はローカル変数 関数内が範囲 グローバルに同名の変数があっても関数内では優先される num = 50 def printnum(): num = 20 print(num) print(num) printnum() print(num) 50 20 50 関数内でglobal変数を更新する num = 50 def printnum(): global num num = 20 print(num) print(num) printnum() print(num) 50 20 20 ネストした関数で1段上の関数の変数を更新する 何もしていない時はそれぞれのローカル変数を読み込む。 num = 0 def print_num_outer(): num = 1 def print_num_inner(): num = 2 print(num) print_num_inner() print(num) print(num) print_num_outer() print(num) 0 2 1 0 ネストした関数で1段上の関数の変数を参照している num = 0 def print_num_outer(): num = 1 def print_num_inner(): nonlocal num num = 2 print(num) print_num_inner() print(num) print(num) print_num_outer() print(num) 0 2 2 0
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[OpenCV] 100行で作るAR

はじめに 100行のPythonスクリプトでARを作ります。 (正確には98行です) 本記事ではこのスクリプトを100行ARと呼称します。 デモ動画 100行ARのデモ動画です。 環境 以下の環境で実行しました。 Windows11(カメラ付き) Python 3.10.0 OpenCV 4.5.4 環境のセットアップ Windows11環境のセットアップ方法です。 Pythonをインストールする 以下のサイトからインストーラをダウンロードしインストールします。 インストーラの以下の画面で、Add Python 3.10 to PATH にチェックを入れるとPythonに関するパスがWindowsの環境変数に追記されます。 powershellを開き、pythonのバージョンが表示されれば成功です。 PS C:\> python -V Python 3.10.0 PS C:\> pipをアップグレードする OpenCVをインストールする前に、pipをアップグレードしておきます PS C:\> python.exe -m pip install --upgrade pip 念のため、バージョンを確認し最新であることを確認します。 PS C:\> pip -V pip 21.3.1 from C:\Users\hoge\AppData\Local\Programs\Python\Python310\lib\site-packages\pip (python 3.10) PS C:\> OpenCVをインストールする OpenCVをインストールします。 PS C:\> pip install opencv-contrib-python==4.5.4.60 以下のように、OpenCVのバージョンが確認できれば成功です。 PS C:\> python Python 3.10.0 (tags/v3.10.0:b494f59, Oct 4 2021, 19:00:18) [MSC v.1929 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> import cv2 >>> cv2.__version__ '4.5.4' >>> exit() PS C:\> 以上で環境のセットアップが完了しました。 100行ARのスクリプトソース お待たせしました 100行ARのスクリプトソース(100ar.py)です。Powershellで python 100ar.py と実行すれば処理が開始されます。qで停止します。 100ar.py import cv2 import cv2.aruco as aruco import numpy as np targetVideo = 0 # カメラデバイス cap = cv2.VideoCapture( targetVideo ) # 立方体の座標 cube = np.float32([[0.025,-0.025,0], [-0.025,0.025,0], [0.025,0.025,0], [-0.025,-0.025,0], [0.025,-0.025,0.05], [-0.025,0.025,0.05], [0.025,0.025,0.05], [-0.025,-0.025,0.05] ]) while cap.isOpened(): ret, img = cap.read() if img is None : break scale_percent = 50 # percent of original size width = int(img.shape[1] * scale_percent / 100) height = int(img.shape[0] * scale_percent / 100) dim = (width, height) # Check if frame is not empty if not ret: continue # Set AR Marker aruco_dict = aruco.getPredefinedDictionary(aruco.DICT_4X4_50) parameters = aruco.DetectorParameters_create() corners, ids, _ = aruco.detectMarkers(img, aruco_dict, parameters=parameters) center = (width, height) focal_length = center[0] / np.tan(60/2 * np.pi / 180) camera_matrix = np.array( [[focal_length, 0, center[0]], [0, focal_length, center[1]], [0, 0, 1]], dtype = "double" ) dist_coeffs = np.zeros((4,1)) # レンズ歪みなしの設定 if len(corners) > 0: for i, corner in enumerate(corners): rvecs, tvecs, _objPoints = aruco.estimatePoseSingleMarkers(corner, 0.05, camera_matrix, dist_coeffs) tvec = np.squeeze(tvecs) rvec = np.squeeze(rvecs) rvec_matrix = cv2.Rodrigues(rvec) # 回転ベクトルからrodoriguesへ変換 rvec_matrix = rvec_matrix[0] # rodoriguesから抜き出し # cubeの座標をARマーカに合わせる imgpts, jac = cv2.projectPoints(cube, rvecs, tvecs, camera_matrix, dist_coeffs) outpts = [] for lp in imgpts: lp_int = lp.astype(np.int64) outpts.append( tuple(lp_int.ravel()) ) # ARマーカに合わせたcube座標を描画:底面 cv2.line(img,outpts[0],outpts[2],(255,0,0),2) cv2.line(img,outpts[1],outpts[2],(255,0,0),2) cv2.line(img,outpts[1],outpts[3],(255,0,0),2) cv2.line(img,outpts[3],outpts[0],(255,0,0),2) # ARマーカに合わせたcube座標を描画:上面 cv2.line(img,outpts[4],outpts[6],(0,255,0),2) cv2.line(img,outpts[5],outpts[6],(0,255,0),2) cv2.line(img,outpts[5],outpts[7],(0,255,0),2) cv2.line(img,outpts[7],outpts[4],(0,255,0),2) # ARマーカに合わせたcube座標を描画:支柱 cv2.line(img,outpts[0],outpts[4],(0,0,250),2) cv2.line(img,outpts[1],outpts[5],(0,0,250),2) cv2.line(img,outpts[2],outpts[6],(0,0,250),2) cv2.line(img,outpts[3],outpts[7],(0,0,250),2) # ARマーカに合わせたcube座標を描画 cv2.circle(img, outpts[0], 10, (0, 240, 160), thickness=-1, lineType=cv2.LINE_AA) cv2.putText(img, '0', outpts[0], cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 0), thickness=2) cv2.circle(img, outpts[1], 10, (40, 200, 200), thickness=-1, lineType=cv2.LINE_AA) cv2.putText(img, '1', outpts[1], cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 0), thickness=2) cv2.circle(img, outpts[2], 10, (80, 160, 240), thickness=-1, lineType=cv2.LINE_AA) cv2.putText(img, '2', outpts[2], cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 0), thickness=2) cv2.circle(img, outpts[3], 10, (120, 120, 40), thickness=-1, lineType=cv2.LINE_AA) cv2.putText(img, '3', outpts[3], cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 0), thickness=2) cv2.circle(img, outpts[4], 10, (160, 80, 80), thickness=-1, lineType=cv2.LINE_AA) cv2.putText(img, '4', outpts[4], cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 0), thickness=2) cv2.circle(img, outpts[5], 10, (200, 40, 120), thickness=-1, lineType=cv2.LINE_AA) cv2.putText(img, '5', outpts[5], cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 0), thickness=2) cv2.circle(img, outpts[6], 10, (0, 0, 255), thickness=-1, lineType=cv2.LINE_AA) cv2.putText(img, '6', outpts[6], cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 0), thickness=2) cv2.circle(img, outpts[7], 10, (0, 255, 255), thickness=-1, lineType=cv2.LINE_AA) cv2.putText(img, '7', outpts[7], cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 0), thickness=2) cv2.imshow('frame', img) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows() 実行例です。 100ar.pyの実行方法 PS C:\Users\hoge\100ar> dir ディレクトリ: C:\Users\hoge\100ar Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---l 2021/11/23 11:34 4732 100ar.py PS C:\Users\hoge\100ar> python .\100ar.py 仕組み 立方体の座標算出 100行ARでの立方体の座標算出の流れを紹介します。 (Fig-1)の向かって左が3次元空間に配置したARマーカー、向かって右がカメラで撮影した映像です。 (Fig-1) 3次元空間のARマーカーとカメラ映像 (Fig-2)は、(Fig-1)の3次元空間に配置したARマーカーに対しカメラの撮影位置を算出した結果を図示したものです。 カメラで撮影した映像から3次元空間に配置したARマーカーを撮影したカメラ位置の算出には、estimatePoseSingleMarkers()を使用します。 正確には、ARマーカーとカメラ位置の相対的な位置関係を算出します。原点(0,0,0)はARマーカーの中点のようです。 (Fig-2) カメラ位置の算出 (Fig-2)の処理は以下になります。以降の変数tvec,rvec,rvec_matrixの処理は立方体の算出で使用します。 100ar.py_ARマーカーとカメラ位置の相対的な位置関係を算出 rvecs, tvecs, _objPoints = aruco.estimatePoseSingleMarkers(corner, 0.05, camera_matrix, dist_coeffs) tvec = np.squeeze(tvecs) rvec = np.squeeze(rvecs) rvec_matrix = cv2.Rodrigues(rvec) # 回転ベクトルからrodoriguesへ変換 rvec_matrix = rvec_matrix[0] # rodoriguesから抜き出し estimatePoseSingleMarkers()の詳細はこちら ここまでで、(Fig-3)のように撮影したARマーカーの映像からカメラ位置が算出できました。 (Fig-3) 算出されたカメラ位置 ここからは立方体に対する処理になります。 (Fig-4)は、向かって左が3次元空間に配置した立方体、中央のカメラがさきほどestimatePoseSingleMarkers() で算出したカメラの位置、向かって右がそのカメラで撮影した映像です。 算出したカメラ位置から立方体を撮影するとどのような座標になるかを projectPoints() で算出します。 (Fig-4) 3次元空間の立方体とカメラとカメラ映像 (Fig-4)の3次元空間に配置した立方体の座標は以下です。 100ar.py_立方体の座標 # 立方体の座標 cube = np.float32([[0.025,-0.025,0], [-0.025,0.025,0], [0.025,0.025,0], [-0.025,-0.025,0], [0.025,-0.025,0.05], [-0.025,0.025,0.05], [0.025,0.025,0.05], [-0.025,-0.025,0.05] ]) (Fig-4)のそのカメラで撮影した映像が以下です。正確には映像ではなく座標です。 変数 cube が変換前の立方体の座標です。変換後の座標は変数 imgpts に格納されます。 変数 cube には3次元の座標が、imgpts には2次元の座標が格納されています。 100ar.py_立方体の座標変換 # cubeの座標をARマーカに合わせる imgpts, jac = cv2.projectPoints(cube, rvecs, tvecs, camera_matrix, dist_coeffs) projectPoints()の詳細はこちら (Fig-5)のように立方体の底面がARマーカーと一致するようにしています。 (Fig-5) ARマーカーと立方体 立方体の描画 座標変換ができれば、あとは描画するだけです。スクリプトソースの以下のところから描画処理になります。 100ar.py_底面に沿う直線を描画 # ARマーカに合わせたcube座標を描画:底面 cv2.line(img,outpts[0],outpts[2],(255,0,0),2) cv2.line(img,outpts[1],outpts[2],(255,0,0),2) cv2.line(img,outpts[1],outpts[3],(255,0,0),2) cv2.line(img,outpts[3],outpts[0],(255,0,0),2) 以下は、立方体のポイントに円と数字0を描画しています。 100ar.py_円と数字0を描画 # ARマーカに合わせたcube座標を描画 cv2.circle(img, outpts[0], 10, (0, 240, 160), thickness=-1, lineType=cv2.LINE_AA) cv2.putText(img, '0', outpts[0], cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 0), thickness=2) 立方体の各ポイントに割り振った数値は(Fig-6)の通りです。 (Fig-6) 立方体の模型 余談ですが、(Fig-6)の模型は今回のプログラムを作ために作成しました。紙に絵を描いていたのですが、分けがわからなくなったので模型を作りました。 100行ARは以上です。 可読性を高めるため描画処理を冗長にしたため100行になりましたが、最適化すればもっと短いプログラムになりそうです。 ※補足 ARマーカーの作成はこちらが参考になるかも さらなる高みへ ここからは、100行ARを改良するとどのように進化するかを紹介します。 箱を描画 立方体の各面について、描画順序を判定する処理を追加しました。これにより各面に画像をマッピングしても箱の形状を維持できるようになりました。 凹むAR 画像処理を追加し、凹むエフェクトができました。 仮想の穴 ストリーミングと組み合わせ穴が空いたようなエフェクト(仮想穴)ができました。この仮想穴は、私が開発を続けているもので ウソ穴 と命名しています。 2壁貫通の仮想穴 複数のストリーミングとAR処理のタイミングを追加し、2つの壁を貫通する仮想穴ができました。デモ動画では仮想の穴は2つですが、理論的には無数の壁を貫通できます。 (処理量が多すぎるためか、動画がひどくコマ落ちしています。処理の無駄を省き最適化したいところ) おわりに 100行ARのスクリプトソースを紹介しました。 100行ARでは、立方体の座標を使用しましたが、今後は3Dオブジェクトデータの描画にも挑戦したいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python_*args, **kwarge(可変長引数)

*args いくつでもの引数をタプルとして受け取る *argsはargsではなくどんな文字列でもよい。 def challenge_args(*args): print(args) print(args[0]) print(sum(args)) challenge_args(1, 2, 3, 4) 実行結果 (1, 2, 3, 4) 1 10 def challenge_apple(*apple): print(apple) print(sum(apple)) challenge_apple(1, 2, 3) 実行結果 (1, 2, 3) 6 **kwargs いくつかのキーワード引数を辞書として受け取る。 def challenge_kwarges(**kwargs): print(kwargs) apple = kwargs.get('apple', None) print(apple) challenge_kwarges(apple='red', banana='yellow') 実行結果 {'apple': 'red', 'banana': 'yellow'} red
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

sqlalchemy パスワードに@を含む場合の対処方法

概要 sqlalchemyとを使用してDBに接続する際に、パスワードに@を含む場合の対処方法です。 パスワードに@が含まれている場合、URLの @ と区別が出来ずエラーになります。 対処方法 quote_plusを使用してパスワードをURLエンコードしてから使用します。 URLエンコードすることでパスワード中の @ が %40 にエンコードされます。 python from urllib.parse import quote_plus import pandas as pd import sqlalchemy as sa user = 'username' # ユーザ名 password = 'p@ssword' # パスワード host = 'localhost' # ホスト名 or IP db = 'database' # データベース port = 3306 # ポート # パスワードをURLエンコード p@ssword -> p%40ssword password = quote_plus(password) url = f'mysql+pymysql://{user}:{password}@{host}:{port}/{db}?charset=utf8' # engine作成 engine = sa.create_engine(url, echo=False) # pandasのread_sql関数にselect文とengineを指定する query = "select * from table" df = pd.read_sql(query, con=engine)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

OpenCVのマウス操作でトリミング

はじめに opencvのマウスイベント機能を使って、画像トリミングプログラムを作成しました。 マウスの左クリックでトリミング座標取得、右クリックでトリミング実行、 ESCキーでプログラム終了をします。timeoutの指定もできます。 環境 anaconda windows 10 pro python = 3.9 opencv = 4.5 参考: 今回作成したプログラムのひな型1(座標取得) Python、OpenCVでマウスのクリック位置を取得する 今回作成したプログラムのひな型2(描画) 【Python】OpenCVを使ったマウス操作での直接描画 – setMouseCallback() OpenCVのマウスイベントの組み合わせについて参考にした。 OpenCVでマウスイベントを取得する ~GUIな集中線ツールを作る~ OpenCVのマウスイベントについてまとまっています。 画像情報処理研究室 【OpenCV】cv2.putText関数の使い方【文字を描画する】 ‐ 画像のトリミングについて参考にした。 Python, NumPyで画像処理(読み込み、演算、保存) 座標の取り扱いなどわかりやすい。 【Python】OpenCVによる画像の画素へのアクセスと切り取り (初心者向け) 作成プログラム プログラムの解説は、プログラム中にコメントで記述しています。 import time import cv2 def pos_est_rect(file_name, wh=100, time_out=120): """trimming position estimation Args: file_name (str):file name wh (int, optional): trimming size. Defaults to 1100. time_out (int, optional): time out [second]. Defaults to 120. """ start = time.time() img = cv2.imread(file_name) def printCoor(event,x,y,flags,param): # OpenCVマウスイベントのcallbackは上記のような引数をとる。 # nonlocalの宣言をして、この関数外にある変数にアクセス。 nonlocal img nonlocal img_mes nonlocal file_name if event == cv2.EVENT_LBUTTONDOWN: # 元の画像に直接書き込むと前の描画がそのまま残ってしまうため、コピーを作成。 img_tmp = img_mes.copy() # 直線で書きたい場合 # cv2.line(img_tmp,(x,y),(x+wh,y),(255,255,255),1) # cv2.line(img_tmp,(x,y),(x,y+wh),(255,255,255),1) # cv2.line(img_tmp,(x+wh,y),(x+wh,y+wh),(255,255,255),1) # cv2.line(img_tmp,(x,y+wh),(x+wh,y+wh),(255,255,255),1) cv2.rectangle(img_tmp,(x,y),(x+wh,y+wh),(255,255,255), thickness=1) # 座標は左上が原点 x座標:左から右 y座標:上から下 行列では,行(height):y、列(width):x # orgは文字オブジェクトの左下の座標 cv2.putText(img_tmp, text=f'(x,y):({x},{y})',org=(x, y-10), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=0.5, color=(255,255,255),thickness=1,lineType=cv2.LINE_4) print(f'start x:{x}, y:{y} --wh:{wh}-- end x:{x+wh}, y:{y+wh}') cv2.imshow('image',img_tmp) elif event == cv2.EVENT_RBUTTONDOWN: # cv2.imshow('image',img) idx = file_name.rindex('.') trim_name = f'{file_name[:idx]}_trim.jpg' trim_array = trim(array=img, x=x, y=y, width=wh, height=wh) cv2.imwrite(trim_name, trim_array) h,w,_ = img.shape img_mes = img.copy() print(img.shape) print('Quit -> ESC Key ') cv2.namedWindow('image',cv2.WINDOW_NORMAL) cv2.setMouseCallback('image',printCoor) cv2.moveWindow('image',100,100) #100,100はwindows上に表示される位置を指定。 cv2.putText(img_mes, text=f'Quit -> ESC Key',org=(5,10), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=0.3, color=(255,255,255),thickness=1,lineType=cv2.LINE_4) # cv2.imshow('image',img) cv2.imshow('image',img_mes) # 第一引数の名前が同じだと同じウィンドウに上書き表示(名前が異なると別のウインドウが作成される)。 while True: elasped_time = time.time() - start if cv2.waitKey(20) & 0xFF == 27: break if elasped_time > time_out: # Exit with a timeout print('time out') break cv2.destroyAllWindows() def trim(array, x, y, width, height): """ Function specified by the upper left coordinates and the width / height of the area to be trimmed The return value of shap is the order of rows and columns. Note that the order is y and x in terms of xy coordinates. The origin is on the upper left. Args: array (2Dndarray): image data x (int): start x y (int): start y width (int): width height (int): height Returns: [ndarray]: trim image Example: im_trim2 = trim(im, 128, 192, 256, 128) # (128, 256, 3) Ref: https://note.nkmk.me/python-numpy-image-processing/ """ array_trim = array.copy() array_trim = array_trim[y:y + height, x:x+width] print(f'Original h(Y), w(X) : {array.shape}') print(f'Trimmed h(Y), w(X) : {array_trim.shape}') return array_trim if __name__ == '__main__': # test sampleとしてlenaさんを利用しています。 pos_est_rect(file_name='lena.jpg', time_out=120) 実行結果 プログラム実行中画像 左クリックでトリミング位置表示 右クリックでトリミング実行 ESCで終了 トリミング結果 読み込ませる画像(画像の保存から、プログラムを実行するときに利用してください。) まとめ このプログラムの作成にあたり - opencvマウスイベント - 矩形表示、文字表示 - トリミング - 画像読み込み、保存 の要素を組み合わせています。 このプログラムが少しでも皆さんの参考になればと思っています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python: C# の GroupBy(g=> g).Where(w=> w.Count() == 2) の実現方法

背景 Python だと全然よくわからず・・調べた記録 C#-LINQでの取得方法 list.GroupBy(g=> g).Where(w=> w.Count() == 2) python は書きやすいと思っていたので、Linq のような簡単な方法があるんじゃないのかなぁ・・と思い調べたが見つからず・・ 試行 groupby 普通にこれでやれると思ったら、思ったように取得出来なかった・・ 使い方が悪いのかと試行錯誤したあとで、 以下の Sample で、同一の文字列突っ込んでみたら別グループにされてしまったので、諦め ゴリゴリ・・ 結局、以下で実現。 Unique Key の抽出 Key で Filterx[1]を指定しないといけないのも感覚的じゃない・・ 中身の長さで Filter byCollection cards = [1, 2, 5, 8, 5, 2, 2] groupedValues = {uniqueValue:[inputValue for inputValue in cards if inputValue == uniqueValue] for uniqueValue in unique(cards)} listPairs = filter(lambda x: len(x[1])==2, groupedValues.items()) # x[0]: Index, x[1]: value(s) print(list(listPairs)) 取得結果 [(5, [5, 5])] pandas ちょっと諦めきれなかったので、調査中に見つけた有名な Library で試してみた。 ゴリゴリのよりは、まだ分かりやすい・・。でも、groupby の指定がなんか感覚的に違和感・・ 基本的に行列の処理用だから、Column 名や Index でグルーピングなんだろうな、とは byPandas df = pd.DataFrame([1, 2, 5, 8, 5, 2, 2]) grouped = df.groupby([0]) print(grouped.filter(lambda x: x.size == 2)) 取得結果 0 2 5 4 5 keyword how to retreive 'groupby' when same data exists in the list
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MySQLからデータを取得してpandasで使用する

概要 sqlalchemyを使用してDBに接続し、selectの結果をpandasのDataFrameとして取得する方法です。 使用方法 インストール sqlalchemyとPyMySQLがインストールされていない場合はpipでインストールします。 console pip install sqlalchemy pip install PyMySQL DB接続+データ取得 DBに接続するには create_engine() 関数にURLを指定します。 pandasのread_sql関数にselect文とcreate_engineで作成したengineを指定し実行すると、sqlの実行結果がDataFrameとして取得できます。 python import pandas as pd import sqlalchemy as sa user = 'username' # ユーザ名 password = 'password' # パスワード host = 'localhost' # ホスト名 or IP db = 'database' # データベース port = 3306 # ポート url = f'mysql+pymysql://{user}:{password}@{host}:{port}/{db}?charset=utf8' # engine作成 engine = sa.create_engine(url, echo=False) # pandasのread_sql関数にselect文とengineを指定する query = "select * from table" df = pd.read_sql(query, con=engine)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

組み合わせ問題を1からコーディング

プログラミングを学び初め、ある程度の知識を取得したと思うので、身近な題材にアプローチしてみた。 その題材とはこれ!! “とあるスーパー、過去3ヵ月以内のレシートを合計N万円分集めると500円券をN枚貰える“ なぜこの題材なのか、、、 計算が億劫で溜まってしまったレシート、迫る期限、端数を最小限にしたいドケチ精神、だからと言って欲しい金額になるように買い物するのは無駄な買い物しそうで嫌だ このような理由から、自動で最適な組み合わせを教えてくれるコードを作ることにした。余談ですが、最近、車輪の再発明という慣用句を知りました。 構想 レシート5枚の時、1枚組は5通り、2枚組は10通り、3枚組は10通り、4枚組は5通り、5枚組は1通りとなる。20枚とか計算する気にならない、、、 どうやってコードにすればええんやと悩み続けたところ、こんな方法を思いついた。 〇をレシート、番号をレシートの古い順、棒線を加算とした時、左上から順に探索すれば全パターンの合計値がわかるはずである。これをデータフレームで表し、各要素に加算していき、目標の金額を超えた段階で抽出するという方法である。 コード 構想を形にするべく破壊と創造を繰り返し、数多のエラーを潜り抜けた末、以下のコードが完成した。 import pandas as pd import numpy as np class ReceiptCal(): def __init__(self,List_money,money): self.List_money=List_money self.money=money def Cal(self): List_columns=[] for columns in range(len(self.List_money)): List_columns.append(columns) List_index=[] for index in range(2**(len(self.List_money)-2)): List_index.append(index) #表を作成(df1:合計値、df2:使用したレシート番号) df1=pd.DataFrame(0,index=List_index ,columns=List_columns) df2=pd.DataFrame(str(0),index=List_index ,columns=List_columns) #N万円を超えた要素を格納するところ df_result1=pd.DataFrame(columns=["合計"]) df_result2=pd.DataFrame(columns=["番号"]) #------------------------ここから計算------------------------ #カラム毎に計算(1層目を実行) for col in List_columns: df1.iloc[0:2**max(0,len(List_columns[col:])-2),col]+=List_money[col] df2.iloc[0:2**max(0,len(List_columns[col:])-2),col]=str(col) #層毎に計算(2層目以降) for i in range(len(List_columns[col:])-1): i+=1+col a=0 #次の層に行く条件(処理中のカラムの最大indexまで処理が済むまで) while a!=2**max(0,len(List_columns[col:])-2): count=2**max(0,len(List_columns[i:])-2) List=[] for c in range(max(1,count)): List.append(","+str(List_columns[i])) df1.iloc[a:count+a,col]+=List_money[i] df2.iloc[a:count+a,col]+=List a+=count i+=1 #直前の処理でN万円超えた要素があれば、抽出し、dfは-1000000にする if sum(df1[col]>=self.money)>0: df_result1=df_result1.append( df1[df1[col]>=self.money][[col]].rename(columns={col:"合計"}) ) df_result2=df_result2.append( df2[df1[col]>=self.money][[col]].rename(columns={col:"番号"}) ) df1[col]=df1[col].where(df1[col]<self.money,-1000000) #ひとつ前の要素が処理最後ならば if (max([int(n) for n in df2.iloc[a-1,col].split(",")]) ==max(List_columns)): #そこが最大indexでない及び処理最後であれば次のindexへ while (a!=2**max(0,len(List_columns[col:])-2) and (max([int(n) for n in df2.iloc[a,col].split(",")]) ==max(List_columns))): a+=1 #そこが最大indexでないならば、処理を行うために加算値を代入 if a<2**max(0,len(List_columns[col:])-2): i=max([int(n) for n in df2.iloc[a,col].split(",")])+1 df_result1=pd.concat( [df_result1,df_result2],axis=1 ).drop_duplicates(subset=["番号"]).reset_index(drop=True) print("古い順",df_result1,sep="\n") print("お得順",df_result1.sort_values("合計"),sep="\n") すごく見にくいです。申し訳ありません。 実行結果(レシート6枚) まずは、手持ちのレシート6枚だけで実行してみた。 List_money=[347,2599,1706,1560,2405,5638] money=10000 ReceiptCal(List_money=List_money,money=money).Cal() 結果 古い順 お得順 合計 番号 合計 番号 0 10290 0,1,2,5 3 10096 0,2,4,5 1 10144 0,1,3,5 1 10144 0,1,3,5 2 10989 0,1,4,5 0 10290 0,1,2,5 3 10096 0,2,4,5 9 10642 1,4,5 4 11850 0,1,2,3,5 2 10989 0,1,4,5 5 12695 0,1,2,4,5 14 11309 2,3,4,5 6 12549 0,1,3,4,5 10 11503 1,2,3,5 7 11656 0,2,3,4,5 7 11656 0,2,3,4,5 8 14255 0,1,2,3,4,5 4 11850 0,1,2,3,5 9 10642 1,4,5 12 12202 1,3,4,5 10 11503 1,2,3,5 11 12348 1,2,4,5 11 12348 1,2,4,5 6 12549 0,1,3,4,5 12 12202 1,3,4,5 5 12695 0,1,2,4,5 13 13908 1,2,3,4,5 13 13908 1,2,3,4,5 14 11309 2,3,4,5 8 14255 0,1,2,3,4,5 できてます!!!! 因みに、処理後のdf1とdf2はこうなってます。 df1 0 1 2 3 4 5 0 -1000000 -1000000 -1000000 9603 8043 5638 1 -1000000 -1000000 8904 7198 0 0 2 -1000000 -1000000 9749 0 0 0 3 -1000000 9943 7344 0 0 0 4 -1000000 -1000000 0 0 0 0 5 -1000000 9797 0 0 0 0 6 -1000000 -1000000 0 0 0 0 7 8584 8237 0 0 0 0 8 -1000000 0 0 0 0 0 9 9251 0 0 0 0 0 10 -1000000 0 0 0 0 0 11 7691 0 0 0 0 0 12 9950 0 0 0 0 0 13 7545 0 0 0 0 0 14 8390 0 0 0 0 0 15 5985 0 0 0 0 0 df2 0 1 2 3 4 5 0 0,1,2,3,4,5 1,2,3,4,5 2,3,4,5 3,4,5 4,5 5 1 0,1,2,3,5 1,2,3,5 2,3,5 3,5 0 0 2 0,1,2,4,5 1,2,4,5 2,4,5 0 0 0 3 0,1,2,5 1,2,5 2,5 0 0 0 4 0,1,3,4,5 1,3,4,5 0 0 0 0 5 0,1,3,5 1,3,5 0 0 0 0 6 0,1,4,5 1,4,5 0 0 0 0 7 0,1,5 1,5 0 0 0 0 8 0,2,3,4,5 0 0 0 0 0 9 0,2,3,5 0 0 0 0 0 10 0,2,4,5 0 0 0 0 0 11 0,2,5 0 0 0 0 0 12 0,3,4,5 0 0 0 0 0 13 0,3,5 0 0 0 0 0 14 0,4,5 0 0 0 0 0 15 0,5 0 0 0 0 0 実行結果(レシート20枚) 手持ちのレシート全て計算させてみました。 import time start_time = time.process_time() List_money=[347,2599,1706,1560,2405,5638, 2876,3375,2261,2746,2579,2067, 2296,1239,534,1677,1985,989, 4565,562 ] money=40000 ReceiptCal(List_money=List_money,money=money).Cal() end_time = time.process_time() p_time = end_time - start_time print(p_time) 結果 古い順 合計  番号 0 40244 0,1,2,3,4,5,6,7,8,9,10,11,12,13,16,18 1 40682 0,1,2,3,4,5,6,7,8,9,10,11,12,15,16,18 2 40361 0,1,2,4,5,6,7,8,9,10,11,12,13,15,16,18 3 40111 0,1,2,4,5,6,7,8,9,10,11,12,15,16,17,18 4 40215 0,1,3,4,5,6,7,8,9,10,11,12,13,15,16,18 .. ... ... 151 43097 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18 152 40284 1,2,3,4,5,6,8,9,10,11,12,13,14,15,16,17,18,19 153 40498 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18 154 40071 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,18,19 155 40526 2,3,4,5,6,7,8,9,10,11,12,13,15,16,17,18,19 お得順 合計   番号 85 40004 0,1,3,4,5,6,7,8,9,10,11,13,14,15,16,17,18,19 93 40014 1,2,4,5,6,7,8,9,10,11,12,13,15,16,18 143 40015 1,2,3,5,6,7,8,9,10,11,12,14,15,16,17,18,19 67 40021 0,1,2,3,4,5,6,7,8,10,11,12,14,15,16,17,18,19 147 40032 1,2,4,5,6,7,8,9,10,12,13,14,15,16,17,18,19 .. ... ... 51 42455 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,18 123 42563 1,2,3,4,5,6,7,8,9,10,11,12,13,15,16,17,18 55 42910 0,1,2,3,4,5,6,7,8,9,10,11,12,13,15,16,17,18 151 43097 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18 91 43444 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18 16633.125 出力されました。計算も合ってそう。でも感動が薄い。待ってる間の約5時間、不安に苛まれていたからです。 まとめ 今回、参考資料無しで、頭の中の構想をそのままコードにしてみました。まるで芸術作品みたいです。一方で、とてもありがたいことに、世の中には組み合わせ問題の様々なアルゴリズムが発明されています。それらを活用して処理時間の短縮をテーマに改良していきたいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LR法

LR 法 この呼び方は一般的な用語ではないですが、ある場所でよく使っていたのでここでもそのまま使っています 1 。 やりたいこと $N$ 要素からなる集合 $\lbrace 0,\ \cdots,\ N-1 \rbrace$ がある。このとき次のクエリを処理してください。 ${\rm delete}(i)$: 要素 $i$ を削除する ${\rm left}(i)$: $i$ 未満の要素の中で最大の要素の値(なければ $-1$ )を返す ${\rm right}(i)$: $i$ より大きい要素の中で最小の要素の値(なければ $N$ )を返す すでに削除された $i$ が引数として与えられないことが保証されるパターンと与えられる可能性があるパターンがあります。ここでは後者でも対応できるものを想定しています。 平衡二分木 平衡二分木があればすべて処理できます。ややオーバーキルですが、組み込みで平衡二分木がある言語ならそれで十分かもしれません。 実装 データの持ち方 各要素 $i$ に対して、その左隣の要素 2 と右隣の要素 3 を配列で記録します。本記事では、それぞれ配列 $L$ および $R$ で表します 4 。また $i$ がまだ削除されていないかどうかを表す長さ $N$ の配列 $X$ も用意しておきます。最初はすべての $0\le i\lt N$ について $X[i] = 1$ です。 $i$ が削除されたら $X[i]=0$ になります。 経路圧縮 Union Find の実装と同様に、一度通った経路は圧縮しておくと次からのクエリで同じ部分を再度通らずに処理できます。 コード Python でのコード例を書いておきます。実際の使い方は「例」の項を参照してください。 test.py class LR_trick(): def __init__(self, n): self.n = n self.L = [i - 1 for i in range(n)] self.R = [i + 1 for i in range(n)] self.X = [1] * n def delete(self, i): self.X[i] = 0 def left(self, i): i = self.L[i] li = [] while i >= 0 and self.X[i] == 0: li.append(i) i = self.L[i] for j in li: self.L[j] = i return i def right(self, i): i = self.R[i] li = [] while i < self.n and self.X[i] == 0: li.append(i) i = self.R[i] for j in li: self.R[j] = i return i 計算量 上の実装の場合、構築は $O(N)$ 、削除クエリは $O(1)$ です。 $\rm left,\ right$ クエリは、削除された要素が与えられない場合はならし $O(1)$ です。与えられる可能性がある場合はならし $O(\log N)$ になる気がします。ちゃんと証明していないので違っていたら教えてください。 例(ネタバレ注意) 例 1 ABC 218-H AC コード DeletableHeapq と LR 法を使うと実装がラクですね。 Dancing Links と呼ばれるものが近そうですが、目的などが少し違いそうな気がします。 ↩ それより小さい要素の中で最大のもの ↩ それより大きい要素の中で最小のもの ↩ LR 法という名前の由来です。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

一文字一文字に心を込めるプログラム

はじめに パソコンで文章を書いていると、手書きに比べてどこか心が込もっていないと感じることはないでしょうか。 そこで本記事では、Pythonを使って「これはペンです」という文章に、心を込めるプログラムを作成してみました。 目次 素直な実装 joinメソッドを用いた実装 1. 素直な実装 文字数分だけループを回します.1 a = "これはペンです" for i in range(len(a)): print(a[i] + "心", end='') 出力結果 こ心れ心は心ペ心ン心で心す心 2. joinメソッドを用いた方法 a = "これはペンです" print("心".join(a)) 出力結果 こ心れ心は心ペ心ン心で心す おわりに い心か心が心だ心っ心た心で心し心ょ心う心か心。心P心y心t心h心o心n心を心使心っ心て心た心く心さ心ん心心心の心込心も心っ心た心文心章心を心書心い心て心い心き心ま心し心ょ心う心。 参考文献 Pythonにおける文字列操作について もう少し簡潔に記述することもできます。 ↩ a = "これはペンです" for i in a print(i + "心", end='')
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Boston データセットで Gaussian Process を試す

はじめに 分布を予測するガウス過程回帰(GPR : Gaussian Process Regression)を用いて、Boston Housing Dataset の住宅価格予測を行ってみる。参考文献を以下に示す。 GPR : Gaussian Process Regression について ガウス過程回帰(Gaussian Process Regression, GPR)~予測値だけでなく予測値のばらつきも計算できる!~ Gaussian Process Boston Housing Dataset について Boston Housing:ボストンの住宅価格(部屋数や犯罪率などの13項目)の表形式データセット データセットの準備 scikit-learn のデータセットを用いる。人種差別問題に関わる B の項目については除外して分析を行う。GPRでは目的変数の標準化が必須のようなので、それをStandardScalerを用いて行う。その後、データセットを訓練データ、テストデータに分ける。 データセットの準備 # 必要なモジュールのインポート import numpy as np import pandas as pd import sklearn from sklearn.datasets import load_boston from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # データセットの読み込み boston = load_boston() X_org = boston.data y = boston.target.reshape(-1, 1) # 'B'の項目の削除 X = np.delete(X_org, 11, 1) # 目的変数の標準化 scaler_y = StandardScaler().fit(y) y_ss = scaler_y.transform(y) # データセットの分割(train:test = 9.0:1.0) X_train, X_test, y_train_ss, y_test_ss = train_test_split(X, y_ss, test_size=0.1, random_state=0) GPR のモデル作成、学習 平均と標準偏差を予測するモデルを作成。学習(fit)する。カーネル関数は良く使われる、「ConstantKernel() * RBF() + WhiteKernel()」を使用した。カーネル関数の比較については別記事で行いたいと思う。 # 必要なモジュールのインポート from sklearn.gaussian_process import GaussianProcessRegressor from sklearn.gaussian_process.kernels import ConstantKernel, RBF, WhiteKernel # カーネル関数の設定 kernel = ConstantKernel() * RBF() + WhiteKernel() # モデルの作成 gpr = GaussianProcessRegressor(kernel=kernel, alpha=0, random_state=0) # モデルの学習 gpr.fit(X_train, y_train_ss) 出力 GaussianProcessRegressor(alpha=0, kernel=1**2 * RBF(length_scale=1) + WhiteKernel(noise_level=1), random_state=0) 学習結果のカーネル関数のパラメータを確認する。 カーネル関数のパラメータ確認 # kernel関数のパラメータの確認 print(gpr.kernel_) 出力 213**2 * RBF(length_scale=447) + WhiteKernel(noise_level=0.0976) 学習済みモデルを用いてテストデータの予測 作成したモデルを用いて予測(predict)を行う。 予測 # 学習済みモデルを用いて予測を行う pred_mu_ss, pred_sigma_ss = gpr.predict(X_test, return_std=True) #標準化を戻す y_test = scaler_y.inverse_transform(y_test_ss) pred_mu = scaler_y.inverse_transform(pred_mu_ss) pred_sigma = pred_sigma_ss.reshape(-1, 1) * scaler_y.scale_ # リストにまとめる result = list(zip(y_test.reshape(-1), pred_mu.reshape(-1), pred_sigma.reshape(-1))) # データフレームへ df_result = pd.DataFrame(result, columns=['obs', 'pred_mu', 'pred_sigma']) 予測結果からエラーバー(1σ)も込みで Observed-Predicted Plot (yyplot) を作成してみる。 yyplotの可視化 # 必要なモジュールのインポート import matplotlib.pyplot as plt def make_yyplot(df_result, filename, xy_min = 0, xy_max = 60): # 直線の準備 x = np.arange(xy_min, xy_max) y = x # Observed-Predicted Plot(yyplot) の作成 fig = plt.figure(figsize=(6, 6)) ax = fig.add_subplot(111) ax.plot(x, y) ax.errorbar(df_result['obs'], df_result['pred_mu'], yerr = df_result['pred_sigma'], fmt='o') ax.set_xlim(xy_min, xy_max) ax.set_ylim(xy_min, xy_max) ax.set_xlabel('y_observed') ax.set_ylabel('y_predicted') plt.savefig(filename, bbox_inches="tight") # png で保存する場合 plt.close() make_yyplot(df_result, 'GPR_boston_yyplot.png') NGBoost の時(Boston データセットで NGBoost を試す)と同様、概ね予測は正しくできてそうだが、(y_observed, y_predicted) = (50, 28) 付近の2点に外れ値がある。 負の対数尤度 NLL:Negative Log Likelihood による比較 NGBoost の元論文ではいろんな手法を負の対数尤度の平均値を使って比較していた。もちろん GPR を用いて Boston データセットでの値も論文に載っているので、この分析結果も論文の値が再現できているか比較してみる。 NGBoost の元論文 負の対数尤度について Boston のデータセットに対してGPRの論文の結果では、「$2.37 \pm 0.24$」とあった。 NLLの計算 # NLL を計算する関数を定義 def calc_NLL(x, mu, sigma): term_1 = 1 / 2 * np.log(2 * np.pi * sigma ** 2) term_2 = ((x - mu) ** 2) / (2 * sigma ** 2) mns_l = term_1 + term_2 mean = round(mns_l.mean(), 2) std = round(mns_l.std(), 2) print(mean, '+/-', std) return None # NLL を出力 calc_NLL(df_result['obs'], df_result['pred_mu'], df_result['pred_sigma']) 出力 3.24 +/- 4.49 NLLの平均値もNLLの標準偏差も論文よりだいぶ大きな値となってしまった。これは前述した二つの外れ値による影響が大きいと思われる。 説明変数の標準化 目的変数については標準化したが、説明変数は標準化していない。サンプルコードによっては標準化しているものもあったので、参考までに検証してみる。 説明変数の標準化 #説明変数の標準化 scaler_X = StandardScaler().fit(X) X_ss = scaler_X.transform(X) # データセットの分割(train:test = 9.0:1.0) X_train_ss, X_test_ss, y_train_ss, y_test_ss = train_test_split(X_ss, y_ss, test_size=0.1, random_state=0) # モデルの作成 gpr2 = GaussianProcessRegressor(kernel=kernel, alpha=0, random_state=0) # モデルの学習 gpr2.fit(X_train_ss, y_train_ss) 出力 GaussianProcessRegressor(alpha=0, kernel=1**2 * RBF(length_scale=1) + WhiteKernel(noise_level=1), random_state=0) カーネル関数のパラメータ確認 # kernel関数のパラメータの確認 print(gpr2.kernel_) 出力 1.48**2 * RBF(length_scale=3.35) + WhiteKernel(noise_level=0.0611) 予測 # 学習済みモデルを用いて予測を行う pred_mu_ss2, pred_sigma_ss2 = gpr2.predict(X_test_ss, return_std=True) #標準化を戻す pred_mu2 = scaler_y.inverse_transform(pred_mu_ss2) pred_sigma2 = pred_sigma_ss2.reshape(-1, 1) * scaler_y.scale_ # リストにまとめる result2 = list(zip(y_test.reshape(-1), pred_mu2.reshape(-1), pred_sigma2.reshape(-1))) # データフレームへ df_result2 = pd.DataFrame(result2, columns=['obs', 'pred_mu', 'pred_sigma']) yyplotの可視化 make_yyplot(df_result2, 'GPR_boston_yyplot2.png') NLLの計算 # NLL を出力 calc_NLL(df_result2['obs'], df_result2['pred_mu'], df_result2['pred_sigma']) 出力 3.13 +/- 5.28 NLL の平均値は少しだけ改善されたが、その分標準偏差が大きくなってしまった。 RMSE, R2 の値 モデルの当てはまりの良さも確認しておきたいので、RMSE、R2の値も計算しておく。 RMSE,R2の計算 # 必要なモジュールのインポート from sklearn.metrics import mean_squared_error from sklearn.metrics import r2_score # RMSE の出力 print('RMSE:', np.sqrt(mean_squared_error(df_result['obs'], df_result['pred_mu']))) # R2 の出力 print('R2:', r2_score(df_result['obs'], df_result['pred_mu'])) 出力 RMSE: 4.767112929953258 R2: 0.7358254691417548 説明変数を標準化した場合の値は RMSE,R2の計算 # RMSE の出力 print('RMSE:', np.sqrt(mean_squared_error(df_result2['obs'], df_result2['pred_mu']))) # R2 の出力 print('R2:', r2_score(df_result2['obs'], df_result2['pred_mu'])) 出力 RMSE: 4.3180096352226 R2: 0.7832559028849013 となる。説明変数を標準化した方が、少しだけ当てはまりが良くなる。 NGBoost の結果と比較すると、すこしだけ GPR の方が精度が良いように見える。 おわりに NGBoost と同様、あまり意味のない考察だが、二つの外れ値が無かった場合 NLL の値がどうなっていたかを検証してみる。index 番号が 1, 37 のデータが外れ値であったので、 外れ値を除外した場合の検証 # 外れ値だけ取り除いた結果のデータフレーム _df_result = df_result.drop(index=[1, 37]).copy() # NLL を出力 calc_NLL(_df_result['obs'], _df_result['pred_mu'], _df_result['pred_sigma']) 出力 2.36 +/- 0.43 となる。だいぶ論文の値と近くなった。 NGBoost と同様に、データセットの選び方によっても結果が有る程度変わると思われるので、本当に精度を調べようとしたら交差検証などが必要かもしれない。また、実装のサンプルはあってもNLLの部分についてのコードは公開されていないので、計算方法が若干違うかもしれない…。 標準化の前処理だけで GPR を実装することができた。カーネル関数の組み合わせや選び方は多岐にわたるので、次の記事では代表的ないくつかのカーネル関数について、検討を行ってみたいと思う。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MD5によるダイジェスト値を取得する

$ cat test1.py #!/usr/bin/env python3 import hashlib import datetime class Animal: def __init__(self, name='John', height=180): dt_now = datetime.datetime.now() self._id = hashlib.md5(str(dt_now).encode()).hexdigest() self._name = name self._height = height def __del__(self): print(self._id, 'is destruted...') @property def id(self): return self._id @property def name(self): return self._name @property def height(self): return self._height @name.setter def name(self, name): self._name = name @height.setter def height(self, height): self._height = height def get(self): return self._id, self._name, self._height if __name__ == '__main__': # just for testing # dt_now = datetime.datetime.now() # print(dt_now) # hs = hashlib.md5(str(dt_now).encode()).hexdigest() # print(hs) a1 = Animal() print(a1.id) print(a1.name) print(a1.height) print(a1.get()) a2 = Animal(name='Paul', height=183) print(a2.id) print(a2.name) print(a2.height) print(a2.get()) a3 = Animal(name='George', height=105) print(a3.id) print(a3.name) print(a3.height) print(a3.get()) del a1, a2, a3 % ./test1.py d097b3d674dda8dec898a8e4def18ab1 John 180 26c1a6744fa3d9f7e63640a3a9a8f79a Paul 183 ('26c1a6744fa3d9f7e63640a3a9a8f79a', 'Paul', 183) 3caf70515aa5aa97c9ce6dffb58ce8f8 George 105 ('3caf70515aa5aa97c9ce6dffb58ce8f8', 'George', 105) d097b3d674dda8dec898a8e4def18ab1 is destruted... 26c1a6744fa3d9f7e63640a3a9a8f79a is destruted... 3caf70515aa5aa97c9ce6dffb58ce8f8 is destruted... PS D:\MyWork\python-test\object-test> python .\test1.py 3580ad44cd6fcdf2b1576354954507df John 180 ('3580ad44cd6fcdf2b1576354954507df', 'John', 180) c13b973b08263fbff9ddcaf64865277c Paul ]] 183 ('c13b973b08263fbff9ddcaf64865277c', 'Paul', 183) c13b973b08263fbff9ddcaf64865277c George 105 ('c13b973b08263fbff9ddcaf64865277c', 'George', 105) 3580ad44cd6fcdf2b1576354954507df is destruted... c13b973b08263fbff9ddcaf64865277c is destruted... c13b973b08263fbff9ddcaf64865277c is destruted...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Python]pipであるはずのバージョンのライブラリがインストールできない(Could not find a version)

WindowsでPython環境を構築していたときのこと。 以前構築できていた環境を再現するだけなので問題ないと思っていた、インストールできるはずのライブラリのバージョンが見つからないとエラーがでた。 #例 $ pip install tensorflow==1.14.0 ERROR: Could not find a version that satisfies the requirement のような形。 調べた結果単純な間違いがあったため備忘録として以下に実施したことを記す。 実施したこと pipのアップグレード $ pip install -U pip なにはともあれアップグレードはやってみるべし思ったので実施したが変わらずライブラリは... ERROR: Could not find a version that satisfies the requirement と言われてしまう。 さらに調べた結果どうやらインストールしているPythonが怪しいらしい。 というのも確認してみたところインストールしているPythonが32bit版のものをインストールしていた。 ただしくは64bit版をインストールしなければいけないらしい。 これはPython.orgでのダウンロードファイル一覧だが、「Windows x86-64 ~~~」を利用しなければいけないところを誤って「Windows x86 ~~~」を利用してPythonをインストールしていたようだ。 pipのアップグレードで解決しない場合は利用しているPythonを一度確認してみると良いかもしれないい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js & Flask & Bolt for Python でSlackアプリを作るチュートリアル

2021年の7月あたりから、フロントエンドをVue.js バックエンドをFlaskで書いたアプリケーションの開発を行い、公開してきました。 その顛末はこちら 今回は、誰でも簡単にSlackアプリを作ることができるように、チュートリアル形式でテンプレートをビルドしていきたいと思います。 開発環境はMacを想定して書いてあります。 コードはこちら https://github.com/geeorgey/vue_flask_template 準備する Github右上のForkボタンからコードをforkしてください。 リモートリポジトリの設定と、ローカルへのコードのダウンロード $ git init $ git remote add origin Forkしたgitのアドレス $ git pull $ git checkout main pythonの仮想環境を作る $ python3 -m venv .venv $ source .venv/bin/activate これで仮想環境で作業できるようになります ローカル環境にPostgreSQLをインストールする PostgreSQLのクライアントはPostico使えば良いと思います。無料版もあります。 https://eggerapps.at/postico/ 利用するDBを作成する $ psql psql $ create database slack; pssql $ \q slack部分はデータベース名なので、適宜変更してください。 pipenvのインストール pythonのパッケージ管理ツールをインストールします https://original-game.com/how-to-build-a-python-development-environment-on-mac-using-pipenv/ yarnのインストール こちらはフロントエンド開発に使います。 https://zenn.dev/oimo_revolution/articles/c61c034f7af41e ローカル環境を立ち上げる python環境を作る $ pipenv install -r requirements.txt これでOKです。 DBを初期化する $ flask db init $ flask db upgrade こちらでDBが初期化されます。 こんな風になります。 Slackアプリを立ち上げる アプリの立ち上げ方は @seratch さんのこちらの記事を参照。 YAMLはこんな感じです appmanifest.yml _metadata: major_version: 1 minor_version: 1 display_information: name: vue_flask_template description: Bolt for python sample bot background_color: "#070d1f" long_description: "Bolt for Pythonを使って、どのような実装ができるかをテストするためのアプリケーション。\r An application to test what kind of implementation can be done with Bolt for Python.\r https://qiita.com/geeorgey/items/85905a4772903b180fad" features: bot_user: display_name: vue_flask_template always_online: true oauth_config: redirect_urls: - https://vueflasktestgeeorgey.herokuapp.com/slack/install scopes: bot: - chat:write - channels:history - groups:history - im:history - mpim:history settings: event_subscriptions: request_url: https://vueflasktestgeeorgey.herokuapp.com/slack/events bot_events: - app_home_opened - app_uninstalled - message.channels - message.groups - message.im - message.mpim - team_access_revoked - tokens_revoked interactivity: is_enabled: true request_url: https://vueflasktestgeeorgey.herokuapp.com/slack/events org_deploy_enabled: false socket_mode_enabled: false is_hosted: false token_rotation_enabled: false URL部分はngrokのURLに適宜置き換えて下さい。 環境変数を登録します。ターミナルで以下を設定します。 export SLACK_CLIENT_ID= export SLACK_CLIENT_SECRET= export SLACK_SIGNING_SECRET= export SLACK_SCOPES=chat:write,users:read,users:read.email,channels:join,groups:write,im:write,mpim:write,channels:read,app_mentions:read,im:read,mpim:read,channels:history,groups:history,im:history,mpim:history,commands botを立ち上げる $ flask run これでbotが動き始めます。簡単! インストールしてみる https://ngrokのURL/slack/install にアクセスすると、インストールボタンが現れますので、そこからインストールしてみましょう。 Boltの本体について 本体はbackend/bolt.py にあります。 インストール時にDMでインストールされたことを伝える機能 全ての公開チャンネルにjoinする機能 ホーム画面からユーザーにDMを送る機能 等が組み込まれています。 Vue.jsを使うには こちらはオプションです。 フロントエンドはこんな形になっています。 https://vueflasktestgeeorgey.herokuapp.com/ 詳細はこの辺をみてみてください。 こちらは、もともとはGoogleのOAuthを紐付けるために作成した機能です。 Slackのアプリ上で出来ないようなことをやる必要がある場合はWebのフロントエンドが必要になりますので、必要になった場合に使ってみて下さい。不要な場合は特に使う必要はありません。 本番環境を構築しよう こちらのアプリはHerokuで動かせるようになっています。 やり方はこちらを参照して下さい。 これを元にして5つのアプリを作りました! Slackアプリの開発の共通テンプレートとしてつかっているのですが、これを元にして5つのアプリを作りました。 よろしければインストールして使ってみて下さい! OYASUMI bot https://lne.st/oyasumilink TIPS https://lne.st/tips TimeLine for Slack https://lne.st/wr21 YOKOKU for Slack https://lne.st/4cdy TASUKARU-TaskALL- https://lne.st/3x1u OYASUMI bot Googleカレンダーのスケジュールに「休み」等の、休暇判定キーワードが入っていた場合に、自動的にSlackをスヌーズ状態に変更します。 メンションがあった場合に、メンションした相手にその日は休暇ですというレスを付けます。 スタッフの休暇を気持ちよく過ごしてもらう為のアプリです。 TIPS Slack用のリマインダーアプリです。 デフォルト機能のリマインダーは、一つのスケジュールに一つのメッセージの設定しか出来ません。 このアプリでは、一つのスケジュールに複数のメッセージを登録し、ランダムで指定された時間にpostします。 チャンネル特有のノウハウ等をスケジュール登録しておくことで、自然と情報が浸透する状態を作ります。 TimeLine for Slack 全ての公開チャンネルのpostを一つのチャンネルにまとめるタイムラインチャンネルを生成するアプリです。 一部のチャンネルのみを集めたミックスチャンネルを作成すると、自分が必要なチャンネルだけのタイムラインを作り出すことが出来ます。 オプション機能として、メッセージ転送時にDeepL翻訳を挟むことが出来ます。 YOKOKU for Slack OYASUMI botの応用です。 例えば「営業」というキーワードが入った予定だけを抽出したいと思ったことはありませんか? カレンダーをいちいち検索するのは手間がかかります。 このアプリを用いれば、特定のキーワードが入った予定を、特定のチャンネルに流すことが出来ます。 TASUKARU Slackで色々な人からメンションをもらって混乱したことはありませんか? 何か頼まれていたはずだけど思い出せない… そんな状態からあなたを救います。 TASUKARUはSlack専用のタスクマネージャーです。 デフォルトのスレッドやメンション画面では、処理が終わったpostをアーカイブすることが出来ませんが、TASUKARUならそれが出来ます。 今アクションが必要なものだけに集中することができる、それがTASUKARUです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

自由に移動できる3D掲示板を作った

はじめに 3D掲示板という3Dな掲示板を作りました。 3D空間にコメントを投稿できる掲示板です。 無料で使えます。 3D掲示板 - NARUPORT なんで開発したの? Three.jsというJavaScriptの3Dライブラリを使って何か作ってみたかったのですが、横になってボーっとアイデアを考えていたら突如、この3D掲示板が浮かびました。 3D空間にコメントを投稿できるというのは、たぶんVR Chatとかだと普通なんでしょうがWebの世界ではあまり見かけないなぁと思いました。 「3D掲示板」でググってもそれらしいのはヒットしなかったので、たぶん日本初だと思います。 掲示板の使い方 キーボード操作です。↓が対応です。 エンター ... コメント投稿フォームの表示 W ... 前進 S ... 後退 D ... 右に移動 A ... 左に移動 F ... 上に移動 C ... 下に移動 上のキーを使うと3D空間上を自由に移動できます。 視点はコメントが見えづらくなるので固定にしてます。 エンターキーを押すとコメント投稿フォームが現れます。 コメントを書いて「投稿する」ボタンを押すとコメントが投稿されます。 書き込める文字は↓の通りです。 ひらがな カタカナ 大小英数字 ほか記号すこし 3D空間にしてみて面白かったところ 3D空間上にコメントが配置されるので、話題ごとにクラウドができて視覚的に面白いなぁと思いました。 3D空間内にオブジェクトなどを配置すればさらに面白くなるかもしれません。 たとえば掲示板をスレッド式にして、スレッドごとに背景を設定できるようにするとか面白いと思います。 いまのところ3D掲示板はスレッド式ではなくて単一の掲示板になっていますので、そういう仕様にする場合はカスタムが必要になりそうです。 技術的なところ 今回使った技術は↓の通りです。 Django Django REST Framework Vue Three.js なぜかSPAになっているので、Vueを使ってます。 問題としては生成されるjsファイルが重いというところです。 特にダイエットなどはしていないので1MBぐらいあります。 技術的な詳細は↓の記事をご覧ください。 Three.jsで「3D掲示板」を作った話【Django】 - narupoのブログ おわりに これからインターネット回線がもっと太くなれば3D掲示板などは一般的になるのでしょうか? 個人的にはVRにも興味があるのですが、なかなか手を出せずにいます。 3D掲示板、もしよかったら使ってみてください。 3D掲示板 - NARUPORT
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

自由に移動できる3D掲示板を作った【個人開発】

はじめに 3D掲示板という3Dな掲示板を作りました。 3D空間にコメントを投稿できる掲示板です。 無料で使えます。 3D掲示板 - NARUPORT なんで開発したの? Three.jsというJavaScriptの3Dライブラリを使って何か作ってみたかったのですが、横になってボーっとアイデアを考えていたら突如、この3D掲示板が浮かびました。 3D空間にコメントを投稿できるというのは、たぶんVR Chatとかだと普通なんでしょうがWebの世界ではあまり見かけないなぁと思いました。 「3D掲示板」でググってもそれらしいのはヒットしなかったので、たぶん日本初だと思います。 掲示板の使い方 キーボード操作です。↓が対応です。 エンター ... コメント投稿フォームの表示 W ... 前進 S ... 後退 D ... 右に移動 A ... 左に移動 F ... 上に移動 C ... 下に移動 上のキーを使うと3D空間上を自由に移動できます。 視点はコメントが見えづらくなるので固定にしてます。 エンターキーを押すとコメント投稿フォームが現れます。 コメントを書いて「投稿する」ボタンを押すとコメントが投稿されます。 書き込める文字は↓の通りです。 ひらがな カタカナ 大小英数字 ほか記号すこし 3D空間にしてみて面白かったところ 3D空間上にコメントが配置されるので、話題ごとにクラウドができて視覚的に面白いなぁと思いました。 3D空間内にオブジェクトなどを配置すればさらに面白くなるかもしれません。 たとえば掲示板をスレッド式にして、スレッドごとに背景を設定できるようにするとか面白いと思います。 いまのところ3D掲示板はスレッド式ではなくて単一の掲示板になっていますので、そういう仕様にする場合はカスタムが必要になりそうです。 技術的なところ 今回使った技術は↓の通りです。 Django Django REST Framework Vue Three.js なぜかSPAになっているので、Vueを使ってます。 問題としては生成されるjsファイルが重いというところです。 特にダイエットなどはしていないので1MBぐらいあります。 技術的な詳細は↓の記事をご覧ください。 Three.jsで「3D掲示板」を作った話【Django】 - narupoのブログ おわりに これからインターネット回線がもっと太くなれば3D掲示板などは一般的になるのでしょうか? 個人的にはVRにも興味があるのですが、なかなか手を出せずにいます。 3D掲示板、もしよかったら使ってみてください。 3D掲示板 - NARUPORT
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Anacondaでjupyter notebookを使っていてgraphvizのPathが通らない時の解決法のメモ

状況 Anacondaでjupyter notebookを使っていてgraphvizのPathが通らないので、ググりながら全部試したがやっぱりだめ。 解決方法:下記であっさりできました まず、pipでインストールしてしまっていたgraphvizおよびpydotplusを削除。 anaconda navigatorから、grapgviz、python-graphviz、pydotplusを入れる。(全部必要だったのかはよくわからない。) 色々、数日間試しましたがこれであっさりいけたので忘れないうちにメモでした。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonで指数平滑移動平均をやってみた

指数平滑移動平均とは? 移動平均について  移動平均とは、一定の期間の値から平均値を計算して表す値です。単純な平均値とは違い、時系列データの前後のいくつかのデータを足して平均をとるため、平均値が移動していくことから移動平均と呼ばれます。 指数平滑移動平均について  では指数平滑移動平均とは何でしょうか。これは単純な移動平均とは違い、過去の値よりも直近の値になるほど比重を置いて計算された平均値です。過去の値が減少し、直近の値の変動に対する反応が早くなるという特徴があります。 Pythonでのやり方 calc.py import time import math import datetime import csv import pandas as pd import numpy as np def get_yaw(count): acc = [0.0,0.0,0.0] mag = [0.0,0.0,0.0] date,acc[0],acc[1],acc[2],mag[0],mag[1],mag[2] = custum_get(count) yaw_list = list() for i in range(len(acc[0])): acc[0][i] = float(acc[0][i]) acc[1][i] = float(acc[1][i]) acc[2][i] = float(acc[2][i]) mag[0][i] = float(mag[0][i]) mag[1][i] = float(mag[1][i]) mag[2][i] = float(mag[2][i]) roll_error, pitch_error = initial_error(acc[0][i],acc[1][i],acc[2][i]) roll = math.atan2(acc[1][i] , math.sqrt(acc[0][i]**2+acc[2][i]**2)) pitch = -math.atan2(acc[0][i] , math.sqrt(acc[1][i]**2+acc[2][i]**2)) numerator = math.cos(roll)*mag[1][i] - math.sin(roll)*mag[2][i] denominator = math.cos(pitch)*mag[0][i] + math.sin(pitch)*math.sin(roll)*mag[1][i] + math.sin(pitch)*math.cos(roll)*mag[2][i] yaw = math.atan2(numerator,denominator) roll = math.degrees(roll) + roll_error pitch = math.degrees(pitch) + pitch_error yaw = math.degrees(yaw) if yaw < 0: yaw = 360 + yaw yaw_list.append(yaw) date_list=list() for d in date: d_s = d.split(' ') date_list.append(d_s[1]) yaw_list = pd.Series(yaw_list)   #指数平滑移動平均の計算 yaw_list_x = yaw_list.ewm(span=10).mean() yaw_list = yaw_list_x.values.tolist() return yaw_list,date_list if __name__ == '__main__': count = 150 yaw,date = get_yaw(count) print(len(yaw)) print(len(date)) 実行結果 今回は地磁気センサから取得したデータからyaw角を計算するプログラムで指数平滑移動平均を用いた 平滑化前 平滑化後 yaw_list_x = yaw_list.ewm(span=10).mean() pandasのDataFrameおよび、Seriesに定義されているewm関数を使っています。 参考 この記事を元に平滑化を行いました。 https://analytics-note.xyz/programming/pandas-ewma/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

空のデータフレームにデータを挿入する時にハマった話

はじめに Python の pandas.DataFrame の扱いについてちょっとミスをしてしまった事があったので、備忘録として記事に残しておく。 空のデータフレームへのデータ挿入 空のデータフレームのA列に 'aaa' というデータを挿入する場合を考える。 以下、A列とB列を持つ空のデータフレームを作成する。 import pandas as pd test = pd.DataFrame(columns=['A', 'B']) print(test) 出力 Empty DataFrame Columns: [A, B] Index: [] ここへA列に aaa という文字列を挿入してみると…。 test['A'] = 'aaa' print(test) 出力 Empty DataFrame Columns: [A, B] Index: [] エラーは出ないが、結果、データフレームには値が挿入されない。 A B 0 aaa NaN となる事を想定していた。以下の処理でデータフレームに値が入っているものだとしてエラーがでたので、この仕様に気が付くことができた。 結果、index の指定が無い状態だとデータの長さが決まっていないので、test['A'] = 'aaa'の挿入方法ではデータが挿入されない、という事が分かった。 回避方法その1:index を指定した空のデータフレームを作る そもそもindex を指定してやれば NaN が入ったデータフレームが作成されるので、この問題は起こらない。 test = pd.DataFrame(columns=['A', 'B'], index = [0]) print(test) 出力 A B 0 NaN NaN この状態で挿入する test['A'] = 'aaa' print(test) 出力 A B 0 aaa NaN これで値が挿入される。 回避方法その2:文字列ではなく pandas.Series を挿入する index の情報を持つ、pandas.Series にしてから挿入してやることでも、これを回避できる。 test = pd.DataFrame(columns=['A', 'B']) print(test) 出力 Empty DataFrame Columns: [A, B] Index: [] 'aaa'を挿入する際に pnadas.Series に変換する。 test['A'] = pd.Series('aaa') print(test) 出力 A B 0 aaa NaN この方法でもデータを挿入することができた。 おわりに 少し考えれば簡単な話でも、正しい挙動や仕様を理解してないとミスをしてしまう事を忘れないためにも、記事を残していければと思う。 #こうしたミスを晒すのは恥ずかしい面もあるが…。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Django】メールログイン用Userモデルをサクッと定義する

備忘録です。 Djangoはデフォルトだとusernameとpasswordでの認証となっているため、emailとpasswordで認証できるようにユーザーモデルをサクッと実装してみる。 1. accountsアプリを作成 まずは認証用のアプリ(accounts)を作成して、settings.pyに登録する。ここまでは、至って普通。 python manage.py startapp accounts INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'accounts', ] 2. カスタムユーザーとカスタムマネージャーの作成 ここからが本番。accountsのmodels.pyに定義していく。 django.contrib.auth.models.AbstractBaseUserを継承したカスタムユーザーのクラスを作成し、必要なフィールド情報を記入する。このとき、PermissionsMixinも一緒に継承する(☆1)。 パスワードのフィールドはAbstractBaseUserに存在しているので定義する必要はない。PermissionsMixinにスーパーユーザーかどうかを識別するis_superuserというフィールドが入っているので、定義する必要はない。 USERNAME_FIELD = 'email'とすることで、メールアドレスでこのテーブルのレコードを一意に識別することができる、つまりメールアドレスでのログインが可能となる(☆2)。 カスタムマネージャー(UserManager)は、django.contrib.auth.models.BaseUserManagerを継承して作成し、create_userと create_superuserメソッドを追加して、ユーザ作成時、スーパーユーザー作成時の処理を追加する(☆3)。 accounts/models.py from django.db import models from django.contrib.auth.models import ( BaseUserManager, AbstractBaseUser, PermissionsMixin ) class UserManager(BaseUserManager): # ☆3 def create_user(self, username, email, password=None): if not email: raise ValueError('Enter Email!') user = self.model( username=username, email=email ) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, username, email, password=None): user = self.model( username=username, email=email, ) user.set_password(password) user.is_staff = True user.is_active = True user.is_superuser = True user.save(using=self._db) return user class User(AbstractBaseUser, PermissionsMixin): # ☆1 # 使いたいFieldを追加 username = models.CharField(max_length=255) email = models.EmailField(max_length=255, unique=True) is_active = models.BooleanField(default=False) is_staff = models.BooleanField(default=False) objects = UserManager() USERNAME_FIELD = 'email' # ☆2 このテーブルのレコードを一意に識別 REQUIRED_FIELDS = ['username'] # スーパーユーザ作成時に入力する class Meta: db_table = 'users' def __str__(self): return self.email 3. settings.pyに追記 settings.pyに、カスタムユーザーモデルを以下のように追記する。 settings.py AUTH_USER_MODEL = 'accounts.User' 4. マイグレーション $ python manage.py makemigrations account $ python manage.py migrate 5. 管理者の作成 $ python manage.py createsuperuser 6. 管理サイトを開いて見る こんな感じで、メールアドレスとパスワードでの認証システムを実装することができる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pyspark の DataFrame を利用してデータ抽出変換をしてみました (1レコード → 複数レコード)

概要 Azure Databricks 上にて、1レコードに複数(MAX:10)存在する類似データを、その類似データ毎のレコードとして新たに生成し、ファイルに保存する Python プログラムです。(イメージは以下、、、、) ## 元データ 3001,WS1,中野 一郎,79,1630000,WS2,上野 二郎,104,1000000,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2021/7/27, 3002,PS1,下野 三郎,32,800000,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2021/6/29, 3003,ST1,中川 四郎,100,1656000,ST2,前川 五郎,64,1104000,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2021/8/30, ↓↓↓ ## 抽出変換データ 3001,WS1,中野 一郎,79,1630000,2021/7/27 3001,WS2,上野 二郎,104,1000000,2021/7/27 3002,PS1,下野 三郎,32,800000,2021/6/29 3003,ST1,中川 四郎,100,1656000,2021/8/30 3003,ST2,前川 五郎,64,1104000,2021/8/30 ローカル環境 macOS Monterey 12.0.1 python 3.8.3 Azure CLI 2.28.0 前提条件 Azure環境がすでに用意されていること(テナント/サブスクリプション)。 ローカル環境に「azure cli」がインストールされていること。 事前準備 Azure 環境の準備 ### ローカル環境変数の定義 export RG_NAME=rg-ituru_bricks02 export SUBS_NAME=PSP-01 export DB_WORKSPACE_NAME=db-ituru_workspace02 ## 使用するテナントへのログイン $ az login --tenant <tenant_id> ## 使用サブスクリプションの定義 $ az account set --subscription $SUBS_NAME ## 使用サブスクリプションの確認(IsDefault=True) $ az account list --output table ## Azure Data Bricks 用のリソースグループ作成 $ az group create --name $RG_NAME --location japaneast Azure Data Bricks の作成 14日間無料のトライアル版で構成します ## Azure Databricks Workspace の作成・・・完了まで数分かかります az databricks workspace create --resource-group $RG_NAME --name $DB_WORKSPACE_NAME --location japaneast --sku trial Spark クラスタの作成 Azure portal で、作成した Databricks サービスに移動し、「ワークスペースの起動」 を選択します Azure Databricks ポータルにリダイレクトされます。 ポータルで [New Cluster] を選択します 以下のパラメータ入力後、「Create Cluster」ボタンを押します。クラスターが作成され、起動されます 項目 値 Cluster Name db_ituru_cluster02 Cluster Mode Single Node Databrickes Runtime Version 9.1 LTS (Scala 2.12, Apache Spark 3.1.2) AutoPilotOprions Terminate after 45 minutes of inactivity Node Type Standard_DS3_v2 (14GB Memory 4Cores) データファイルのアップロード Azure Databricks ポータルで [Import & Explorer Data] を選択します 「Drop files to upload, or click to browse」に対象のファイルをドラッグ&ドロップします 「DBFS」タブを選択し、「FileStore - tables」フォルダ配下にドロップしたファイル(Operation_SE_Cost_short.csv)を確認することができます Notebook の作成 Azure Databricks ポータルで [New Notebook] を選択します 以下のパラメータ入力後、「Create」ボタンを押します。新規の Notebook 画面が表示されます 項目 値 Name Pandas_02 Default Language Python Cluster db_ituru_cluster02 Notebook の実装 変換元ファイルの確認 cmd_1 display(dbutils.fs.ls("/FileStore/tables/")) データの抽出変換 cmd_2 import pandas as pd # 1レコードに存在する複数分の類似データをその類似毎のレコードとして新たに生成 def DataSpilit(): # csv型式のデータファイルをDataFrameとして読取る df = spark.read.csv('/FileStore/tables/Operation_SE_Cost_short.csv', header='true', inferSchema='true', encoding='ms932') # columns は全ての列名を list で返す colist = df.columns # print(colist) # 類似データあたりの登録項目数 n = 4 # ダミーデータを使用した、最終形のDataframeの作成 dfl = spark.createDataFrame([(0,'PPP','USER_J',0,0,'2020/1/1')],['Num', 'Dept', 'J_Name', 'W_Time', 'UP_Cost', 'Fix_Date']) display(dfl) # MAX10の類似データ for j in range(1,11) : # select(*cols) は列名または式の抽出を行い, 結果を DataFrame で返す dfs = df.select(colist[0], colist[(j*n)-3], colist[(j*n)-2], colist[(j*n)-1], colist[j*n], colist[41]) # display(dfs) # null の行の削除 # dropna は欠測値を含む行を削除した新しい DataFrame を返す dfd = dfs.dropna() display(dfd) # union は2つの DataFrame を統合し新しい DataFrame を返す dfl = dfl.union(dfd) # ダミーデータの削除 → 最終データ display(dfl) dff = dfl.filter(dfl.Num > 0) display(dff) # ファイルへ保存 cnt = DataToFile(dff) return cnt # 変換データをファイルへ保存 def DataToFile(dff): # PySpark Dataframes から Pandas への変換 pdf = dff.toPandas() # カラム:Data の型を str型 から datetime型 にデータ変換する # (クエリ処理で、Dateを範囲指定(Betweem)で行いたいため) pdf['Fix_Date'] = pd.to_datetime(pdf['Fix_Date'].astype(str), format='%Y-%m-%d') # print(type(pdf['Date'][0])) # display(pdf) # Pandas から PySpark Dataframes への変換 df_fix = spark.createDataFrame(pdf) display(df_fix) # DBFSへのデータの保存 df_fix.write.mode('overwrite').json("/FileStore/tables/tech_perup_short.json") return df_fix.count() # 実行関数のコール result = DataSpilit() print(result) 変換済みデータファイルの確認_1 cmd_3 display(dbutils.fs.ls("/FileStore/tables/")) 変換済みデータファイルの確認_2 cmd_4 display(dbutils.fs.ls("/FileStore/tables/tech_perup_short.json/")) 変換済みデータファイルの読込再確認 cmd_5 rdf = spark.read.json('/FileStore/tables/tech_perup_short.json') display(rdf) Azure Data Bricks リソースのクリーンアップ ## リソースをクリーンアップするには、ワークスペースを削除します az databricks workspace delete --resource-group $RG_NAME --name $DB_WORKSPACE_NAME まとめ Pyhton の Dataframe と Pyspark の Dataframe って扱い方がちょっと異なるんですね、、、、、 参考記事 以下の記事を参考にさせていただきました。感謝申し上げます。 【Spark】pyspark.sql.DataFrame クラスのメソッド
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LT大会のランキングを設計してみた

エイチームフィナジーアドベントカレンダー1日目は私 s2terminal が担当します。私は普段Web開発やデータサイエンスの仕事していますが、その中でWebサイトに掲載する商材の口コミを集計したり、社内行事で表彰制度の投票集計に携わったりすることで ランキング という物に触れる機会が多少ありました。 先日、弊社グループ内で大規模なLT大会が開催されました。そこではLTの視聴者による投票が行われ、最も良かったLTのランキングを決めることになりました。その際に私は、ランキングのアルゴリズムの設計・実装を担当しました。 本稿では、単純なランキング集計にはどういう問題点があるかと、このLT大会において「良いLT」が上位になるようなランキングをどのように考えたかを書きます。LT大会のランキングを題材としていますが、たとえばECサイトの星5段階評価からランキングを考える場合などにも応用できると思います。本稿に於ける手法は完璧な物ではありませんが実際に利用されたものであり、何かの参考になれば幸いです。 なお参考までに、弊社グループのLT大会のレポート記事を掲載します。(この記事は2020年に開催されたものですが、本稿で対象としているのは2021年開催したLT大会になります) 投票で順位を決めることの難しさ まず「投票によってランキングを作る」事を考える前に、この問題の難しさについて説明します。投票制度設計について「ある"良い"条件を満たすような投票制度は作れない」という 不可能性定理 がいくつか知られています。その中でもノーベル経済学賞を受賞したケネス・アロー(Kenneth Joseph Arrow)による アローの不可能性定理 を紹介します。これは、以下に示すような条件をすべて同時に満たすような投票制度は作れない、というものです。 定義域の非制限性(Unrestricted domain) 投票者は選択肢に対して自由に投票できる 無関係な選択肢からの独立性(Independence of Irrelevant Alternatives、IIA) ある投票者の部分集合の中でAよりもBが良いという結論になったら、他の選択肢によってその結果が変わることはない 例えば、他の選択肢Cが失格となって候補から消えたとしても、AよりもBが良いという結論が変わってはいけない パレートの原則(Pareto efficiency、全会一致の原則) すべての投票者がAよりもBが良いと投票した時、AよりもBのほうがランキングが上になる 非独裁制(Non-dictatorship) ランキングを左右するような独裁的な権限を持っている投票者がいてはならない どの条件も、"良い"投票制度を設計しようと考えた時に満たすべき条件として違和感は無いと思います。ですが、これらをすべて満たすような制度は設計できないという事が証明されています。 理論的な話は省いて、もう少しイメージしやすい具体例を挙げてみます。「じゃんけんの手{グー, チョキ, パー}の中から最強はどれか?」を投票で決めるという問題を考えてみてください。 投票の結果「グーに投票した人が最も多かったので、グーが最強」と言われたとしたら、どう思いますか?「パーならグーに勝てるから、最強ではない」と納得のいかない人が必ず出てきます。この問題は、投票では誰もが納得行くような結果にすることはできません。 このように、多くの条件を満たす理想的な投票ランキングを設計するのは難しいことが知られています。完璧なランキングはそもそも作れないので、大切なのは 「どういう選択肢が上位に来るようなランキングにしたいか?」を考えて、アルゴリズムを設計・選択すること だと思います。 LT大会の前提条件 具体的な説明に入る前に、今回の「LT大会」および「投票」がどんなものだったか、前提条件を定義しておきます。おそらく「投票システムがあるLT大会」として一般的にイメージされるような物と相違は無いのでは、と思います。 LT大会の進行について ブレイクアウトセッション形式を採用 (同時に複数トラックに分かれてセッションが行われ、視聴者は任意の時間帯に好きなセッションを自由に選んで見ることができる) すべてのトラックでセッションの開始時間・終了時間は同じとする(セッションの長さはすべて同じで、開始時間がズレて重なったりするケースは無い) 視聴者について 視聴者はLT大会の途中参加・途中退室が認められている つまり、すべての時間帯でセッションを見るとは限らず、時間帯によってセッションを閲覧しない・投票しないこともある 投票について 視聴者は、見たセッションそれぞれが良かったかどうかについて5段階で評価できる 数字が大きいほど「良い」とする また、理論的な条件に関係は無いのですがランキングを考える上で注意すべき前提として、これは 社内でのLT大会であり、LT発表者も視聴者もすべて社内のスタッフ(身内)に限定されている という点を改めて明記しておきます。これにより、例えば5段階評価の偏りに影響が出てきます。LT大会開催前から出ていた仮説として、社内のスタッフに対して厳しい評価を投票する人は少なく、多くの人は高い数値を投票するのでは?と想定されました。 ▲実際のLT大会後に集計した5段階評価別の投票数。約8割の人が「5」を投票し、「1」を投票した人はいませんでした。 単純なアルゴリズムの例とその問題点 実際に、LT大会の投票とランキングについて考えていきます。 LT大会のランキングとは、セッション $i$ に対してレーティング $r_i$ を"良い感じ"に定義し、すべてのセッションのレーティングのベクトル $\boldsymbol{r}=\{r_1,r_2,...,r_n\}$ の各要素を高い順に並べたもの、とします。パラメータとして、セッション $i$ を見た視聴者 $h$ による5段階評価ポイント $p_{hi} \in \{1,2,3,4,5\}$ が与えられますので、これをうまく使って $r_i$ を定義することで、より"良い"ランキングを求めたい、という問題です。 数式ばかりだとイメージしにくいですので、最初は単純なランキングアルゴリズムを具体例として挙げ、その問題点を述べていきます。 5段階評価ポイントの合計点 まずは最も単純な「5段階評価の合計点」で順位を決めてみましょう。一応数式を書くとこういう事です。 $$ r_i = \sum_{h}{p_{hi}} $$ たとえば、3つのセッションがあるLT大会を開催し、投票結果が下記のようになったとします。 視聴者 セッション1 セッション2 セッション3 Aさん 2 2 - Bさん 2 2 - Cさん 2 2 - Dさん 2 2 - Eさん 2 - 5 このときレーティングは各セッションの合計点なので、以下のようになります。 - セッション1 セッション2 セッション3 レーティング 10 8 5 ランキング 1位 2位 3位 この結果を見て、どうでしょうか。 A〜Dさんは何も思わないかもしれませんが、Eさんからすると「セッション1よりもセッション3のほうが内容が良かったのに、視聴者が少なかったせいでランキングが上がらなかった」と不満を持つことになります。たくさんの人が来たほうが有利 となってしまいます。 5段階評価ポイントの平均点 次に、評価の平均値を取ってみましょう。 $$ r_i = \frac{1}{n}\sum_{h}{p_{hi}} $$ これならば、合計点による問題「たくさんの人が視聴したセッションが有利になる」を解消できると思います。ですが、これだと「みんなセッション1よりもセッション2のほうが良いと言っているのに、セッション1のほうが上位に来る」という現象が起きてしまいます。たとえば極端なケースとして、下記のような例を見てみましょう。 視聴者 セッション1 セッション2 セッション3 Aさん 5 5 - Bさん 5 5 - Cさん 5 - - Dさん 5 - - Eさん 2 3 4 - セッション1 セッション2 セッション3 レーティング 4.4 4.33... 4.0 ランキング 1位 2位 3位 Eさんからすると、セッション1よりもセッション2のほうが、セッション2よりもセッション3のほうが良かったですし、A~Dさんとしても異論はありません。ですが順位は逆になってしまいました。5段階評価の基準は視聴者それぞれの主観的なものなので、単純に平均値を取ると 高い数値の評価をする人が多く集まったセッションが有利 となってしまいました。 先に述べたとおり特に今回は社内のLT大会ですので、多くの人は4や5の高い数値を投票するのでは?と想定されました。すると上記の例のように、大多数の人が高い数値を投票する中で、少しだけいる低い数値を投票する人が見に行ったセッションが不利となってしまうことが予想されました。 これまでのアルゴリズムの問題点 これら2つのアルゴリズムの問題点は「セッションを聞いた人」に依存してしまう点だと思います。今回ランキングを考える上で、「どういう人がセッションを聞いたか?」ではなくて、できるだけ 「セッションの中身が良かったかどうか?」で順位を決めたい と思い、別のアルゴリズムを考えることにしました。 ところで、たとえば「中身の良いセッションならば、たくさんの人が聞きに来ているはずだ」という仮定を置くならば、単純な合計点によるランキングでも機能すると思います。これまでに挙げたランキング決定方法は間違っているわけではありません。重要なのは「どういう選択肢がランキング上位に来てほしいか?」をケースによってできるだけ反映できるように設計することだと思います。 5段階評価ポイントの相対評価によるランキング 実際に採用したランキングアルゴリズムについて述べていきます。「良いセッションが上位に来るランキングになってほしい」を実現するために、今回は 相対評価 に着目しました。 例えば、ある人がA・Bふたつのセッションを見て「AよりもBのほうが良かった」と思ったとき、この人はどういう投票をするか考えてみます。 Aに1、Bに5 Aに3、Bに4 Aに2、Bに3 ...などなど色々なパターンが考えられますが、とにかく 「AよりもBのほうが良かったならば、AよりもBに対して高い点をつけるだろう」 というのは自然な発想です。この数値に着目してランキングを作れば、少なくとも投票した人に対して納得感の得やすいランキングが実現できるのではないか、と思いました。 計算 実際の詳細な計算は『レイティング・ランキングの数理』1という本を参照してほしいのですが、プログラムの実装に必要な概要のみ記載します。 まず、セッション $i,j$ の両方を見た人の集合を $H_{ij}$ とし、 $H_{ij}$ によるセッション $i$ の評価について考えます。 $H_{ij}$ の人数を $n_{ij}$ として、$j$ に対する $i$ の評価スコアを以下のように定義します。 $$ s_{ij} = \frac1{n_{ij}}{\sum_{h \in H_{ij}}p_{hi}} $$ 若干ややこしい表現を使いましたが、要するに $i,j$ の両方を見た人に限定した平均値であり、ここまでは先述の単純な方法と大きな変わりはありません。 この平均値評価スコアをすべてのセッションについて計算した行列 $\boldsymbol{S} = [s_{ij}]$ を用いて、$j$ に対する $i$ の相対評価を、$i,j$ 両方の平均値の差を取った $k_{ij}=s_{ij}-s_{ji}$ とします。相対評価スコアによる行列 $\boldsymbol{K} = [k_{ij}]$ を定義すると、以下のように計算できます。 $$ \boldsymbol{K} = [k_{ij}] = [s_{ij} - s_{ji}] = \boldsymbol{S}-\boldsymbol{S}^T $$ "良い感じ"にランキング算出用のレーティングベクトル $\boldsymbol{r}$ 定義することで、この相対評価スコア行列 $\boldsymbol{K}$ を表してほしいわけです。"良い感じ"の $\boldsymbol{r}$ を求めたいので $\boldsymbol{r}$ を変数として、レーティングベクトルについても相対評価スコア行列 $\boldsymbol{K}$ と同様に $i,j$ 間の相対評価を取った行列 $\boldsymbol{R(r)}=[r_i-r_j]$ を定義します。$\boldsymbol{R(r)}$ が $\boldsymbol{K}$ に近くなるような時、つまり $$ f(\boldsymbol{r})=||\boldsymbol{K}-\boldsymbol{R(r)}||^2 $$ この $f(\boldsymbol{r})$ が最小になるような $\boldsymbol{r}$ が、相対評価 $\boldsymbol{K}$ を最もよく反映しているレーティングのベクトルになります。これを計算2すると以下の結果になります。 $$ \boldsymbol{r} = \frac{1}{n}{\boldsymbol{Ke}} $$ 上記 $\boldsymbol{r}$ を計算して数値の大きい順に並べることで、ランキングが得られます。これは相対評価スコア行列 $\boldsymbol{K}$ の、列に対する平均を取ったものです。表現としてはややこしくなりましたが、5段階評価の平均値を算出し、その差を取って、さらに平均したものと言われると、実際の計算としてはシンプルです。 実装 実際にプログラムで計算してみましょう。 今回LT大会の投票はGoogleフォームを使ってリアルタイムで投票されていきました。そのデータをスプレッドシートのピボットテーブルを使って「どの視聴者がどのセッションに何点を投票したか」というテーブルに集計しました。それをPythonのプログラムにて行列データにして処理しました。 今回の要件として順位を求めるのはすべてのセッションが完了した後の1回だけでOKだったので、スプレッドシートからGoogleColabへ手動で貼り付けましたが、リアルタイムでの順位計算が求められる場合はAPIを使って連携などする必要があると思います。 またデータ量について、今回は事前にテストして10,000件以上の投票データがあっても集計がうまくいく事は確認していましたが、不特定多数に投票してもらう場合など大量の投票データがある場合はこの方法ではうまく行かないかもしれません。 例として、4つのセッションからなるLT大会に10人の視聴者が下記のような投票をしたとします。 視聴者 セッション1 セッション2 セッション3 セッション4 視聴者1 4 2 2 - 視聴者2 3 1 - 2 視聴者3 1 - 2 - 視聴者4 2 4 2 - 視聴者5 - 3 2 5 視聴者6 2 3 - - 視聴者7 - 4 1 3 視聴者8 3 1 1 - 視聴者9 - 3 2 5 視聴者10 2 - 2 4 これをPythonのプログラムで以下のように定義されるようにしておきます。 import numpy from numpy import NaN data = numpy.array([ [4,2,2,NaN], [3,1,NaN,2], [1,NaN,2,NaN], [2,4,2,NaN], [NaN,3,2,5], [2,3,NaN,NaN], [NaN,4,1,3], [3,1,1,NaN], [NaN,3,2,5], [2,NaN,2,4], ]) 下記のようなプログラムで、実際に $\boldsymbol{r} = \frac{1}{n}{\boldsymbol{Ke}}$ を求められます。 # a,b両方にスコアが存在する部分のみaから抜き出した行を作る def intersection_values(a, b): return a[~numpy.isnan(a) * ~numpy.isnan(b)] # 平均値スコア行列Sを作る def score_matrix(data): reviewee_count = data.shape[1] s = [] for pi in range(reviewee_count): sr = [] for pj in range(reviewee_count): if pi == pj: sr.append(0) continue hij = intersection_values(data[:,pi], data[:,pj]) nij = hij.size sij = (hij.sum() / nij if nij != 0 else 0) sr.append(sij) s.append(sr) return numpy.array(s) # 相対評価レーティングベクトルを返す def relative_score_rating(data): reviewee_count = data.shape[1] s = score_matrix(data) # 相対評価Kを求める k = s - s.T # 平均値を求める return k @ numpy.ones(reviewee_count) / reviewee_count 先程のデータでrelative_score_rating(data)を実行するとarray([ 0.175 , -0.10833333, -1.06666667, 1.0 ])という結果になります。 - セッション1 セッション2 セッション3 セッション4 レーティング 0.175 -0.10833333 -1.06666667 1.0 ランキング 2位 3位 4位 1位 参考までに、同じデータに対して合計値と平均値でレーティングを算出した時のランキングについても記載しておきます。 - セッション1 セッション2 セッション3 セッション4 合計値によるレーティング 17 21 14 19 合計値によるランキング 3位 1位 4位 2位 平均値によるレーティング 2.42857143 2.625 1.75 3.8 平均値によるランキング 3位 2位 4位 1位 合計値によるランキングは、最も視聴者数が多かったセッション2が1位となりました。また、セッション3も視聴者数が多かったのですが低い投票点数ばかりだったので有利を活かしきれず4位となりました。 平均値によるランキングは、相対評価によるランキングと近いものになっていますが、セッション1とセッション2の順位が入れ替わっています。セッション1と2を両方見たという人は5人いて セッション1のほうが、セッション2よりも良かったと言っている人は3人 セッション2のほうが、セッション1よりも良かったと言っている人は2人 相対評価によるランキングはセッション1が2位、セッション2が3位となっており、この2つのセッションを見た人たちの意見をより反映しているといえると思います。 課題 この相対評価によるランキングについては完璧なものではなく、以下のような問題点もありました。 1つのセッションしか見てない人の投票を加味できない この相対評価に反映されるには、最低でも2つのセッションに対して投票する必要があります 同点が多く出る問題への根本的な解決になっていない 「多くの人が5を入れるだろう」という状況は変わっておらず、そうなると相対評価で差を取っても0点になるケースが多くなるため、同点が多く出る事への根本的な解決にはなっていません 一応「ランキングが同点だった場合は投票者の投票時刻の分散がより小さい方(より即答で評価されたほう)を上位とする」等のロジックは用意しておいて最終的に順位が必ず決まるようにしていましたが、幸い実際に使うことはありませんでした この問題に対処するには「1つのセッションに対して5段階評価する」という投票の方法を変える必要があると思います ランキング上位のトラックに偏りが出た 実際のLT大会ではトラック毎に大まかなジャンルが設定されていたのですが、ランキング上位に特定のトラックのセッションが集中しました ジャンルの異なるトラック間での相対評価がうまく機能しなかった可能性があります まとめ 本稿では相対評価を用いたランキング導出のアルゴリズムと、その実装や課題について紹介しました。 ランキングアルゴリズムについては本稿で紹介した以外にも様々な物があります。ランキングを設計する際は「どういう選択肢が上位に来て欲しいか?」を考えて設計すること、完璧なランキングは作れないということを頭に入れながらアルゴリズムを選択・設計すると良いと思います。 参考文献 『レイティング・ランキングの数理 ―No.1は誰か?』 / Amy N.Langville  Carl D.Meyer  著 岩野 和生 中村 英史 清水 咲里 訳 | 共立出版 『Google PageRankの数理 ―最強検索エンジンのランキング手法を求めて―』 / Amy N.Langville  Carl D.Meyer  著 岩野 和生 黒川 利明 黒川 洋 訳 | 共立出版 『はじめてのゲーム理論』(川越 敏司)ブルーバックス|講談社BOOK倶楽部 『Amazonランキングの謎を解く』 株式会社 化学同人 https://www.kyoritsu-pub.co.jp/bookdetail/9784320123908 第10章「ユーザープレファレンスのレイティング」参照 ↩ 行列ノルムをフロベニウスノルムとして微分した結果得られる。また制約として $\sum_i\boldsymbol{r}_i=0$ を追加することで定数を除去している。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

一つのレコードから複数の列を新たに追加

概要 データの処理の際にレコード内に4つの情報が入っていてそれを取り出す必要があったので備忘録として記録しておきます。(前回はJSONフォーマットでしたが今回は半角スペース区切りでデータが入力されていることが前提) id Jan Feb 1 1位 大阪府 12,345 67% 1位 大阪府 11,111 22% 2 2位 京都府 98,765 43% 2位 京都府 22,222 33% こんな感じで空白区切りでレコード内にデータがいくつか入っているものを id 1月順位 都道府県 数 割合 2月順位 都道府県 数 割合 1 1位 大阪府 12,345 67% 1位 大阪府 11,111 22% 2 2位 京都府 98,765 43% 2位 京都府 22,222 33% こんな感じにするのがゴール コード データフレームの作成 import pandas as pd dict1=dict(Jan=["1位 大阪府 12,345 67%","2位 京都府 98,765 43%"],Feb=["1位 大阪府  11,111 22%","2位 京都府 22,222 33%"]) list1=pd.DataFrame(data=dict1) list1 splitを利用して元のデータフレームに列を追加 cols_name=['Jan','Feb'] list_new =[] list_split=[] for i in cols_name: list_split=list1[i].str.split(' ',expand=True) list_spilit_1=list_split.values.tolist() list_new.append(list_spilit_1) df_list=[] for i in range(len(list1.columns)): list_new_new=list_new[1-i] new_new=pd.DataFrame(list_new_new) df_list.append(new_new) new_df=pd.concat(df_list,axis=1) new_df 列名が「0」「1」「2」「3」みたいに数字になってしまい、それも列追加の際に「順位」「都道府県」みたいにできれば良かったんですが実力不足でできませんでした。。。 なのでかっこ悪いですがこの処理の後にrenameで列名を変えました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

S3上のファイル容量を取得して、Python で集計する

はじめに PoC を担当しているのですが、S3 上の既存ファイルの容量を調査する必要がありました。 具体的には、S3上のファイルサイズの全体感を掴みたいので、 S3上の全てのファイル容量を調べて、最大値、最小値、中央値、平均値を出したいという内容でした。 ネット上にはS3上の情報を取得する記事は数があったのですが、集計まで行なっている記事は少なかったため、記録として残しておきます。 環境 ❯ wmic os get caption Caption Microsoft Windows 10 Pro ❯ aws --version aws-cli/2.2.11 Python/3.8.8 Windows/10 exe/AMD64 prompt/off ❯ python --version Python 3.10.0 AWS CLI でファイルサイズを取得する 設定 AWS CLI のインストールと設定がお済みでない方は、以下のページなどを参考に行なっていただければと思います。 公式ドキュメント AWS CLIのインストールから初期設定メモ aws configureと打って以下のように設定値が表示されるようになったら、準備完了です。 ❯ aws configure AWS Access Key ID [****************AAAA]: AWS Secret Access Key [****************BBBB]: Default region name [ap-northeast-1]: Default output format [json]: S3コマンド まずは、AWS CLI のS3コマンドを試してみます。 aws s3 ls s3://${BUCKET}/${OBJECT_PATH}/ --recursive --human --sum ❯ aws s3 ls s3://BucketName/ObjectPath/ --recursive --human --sum 2021-11-02 10:22:46 1.7 MiB ObjectPath/テスト1.pdf 2021-11-13 22:16:34 4.5 MiB ObjectPath/テスト2.pdf 2021-11-29 13:11:12 8.6 MiB ObjectPath/テスト3.pdf ... recursive: 再帰的にリストを出力する human: ファイルサイズに単位を付与して出力する sum: サマリー (オブジェクト個数と合計サイズ) を出力する 単にファイル容量を確認したいだけなら、これで問題ありません。 しかし、何かしらのフォーマットの出力にはこのS3コマンドは対応していません。 大量のデータを集計をする以上、json など何かしらのフォーマットで扱いたいです。 そこで、S3apiコマンドを使います。 S3apiコマンド 簡単に言うと、S3コマンドのパワーアップバージョンです。 詳しくは、こちらの記事が参考になります。 バケット単位の取得で、S3コマンドのときの--humanオプションは使えないので、単位は byte での出力となります。 aws s3api list-objects-v2 --bucket ${BUCKET} 上記のコマンドでS3上のファイルの全情報を json で出力してもいいのですが、今回は容量の情報だけが必要です。 そのため、ファイル容量の一覧を抽出して配列に入れて、適当なテキストファイルに出力してみます。 抽出には、--queryオプションを使います。 ❯ aws s3api list-objects-v2 --bucket BucketName --query "Contents[].Size" >> s3_search.txt s3_search.txt [ 100960, 1656243, 9890, ... ] サイズを取得して、扱いやすいように配列としてフォーマット化することができました。 ここまでくれば、あとはスクリプトを書いていい感じに集計すれば完了です。 Python でスクリプトを作成する Python を選んだ理由は、以下の2つです。 データ集計系のライブラリが充実している 素早く書いて実行できる S3apiコマンドで出力した配列のテキストファイルを使ってもいいのですが、せっかくなのでスクリプト上でファイル容量の取得まで行います。 まず、Boto3(AWS SDK for Python)Boto3 をインストールします。 これで、S3apiコマンドをPython上で呼び出せます。 pip install boto3 準備は整いましたので、あくまで一例ながら以下のように書いてみました。 import boto3 import statistics # S3apiを叩いてファイル情報を取得 s3_client = boto3.client('s3') response_contents = s3_client.list_objects_v2( Bucket = 'bucket_name' ).get('Contents') # ファイルサイズを配列として取得 size_list = [] for rc in response_contents: size = rc.get('Size') size_list.append(size) # 集計して出力 print('最大値:{}、最小値:{}、中央値:{}、平均値:{}' .format(max(size_list), min(size_list), statistics.median(size_list), statistics.mean(size_list)) ) 出力結果は、以下のような感じです。 最大値:4194340、最小値:98.0、中央値:361.0、平均値:10286.569 ただし、上記のコードでは一度のAPI呼び出しで取得できる1000件を上限にしている点に注意してください。 これは、今回のようなストレージのデータを取得するような API は、レスポンスが膨大な量になることが多いためです。 このような制限を、ページネーションといいます。 長くなってしまうのでここでは詳しく触れませんが、以下の記事などを参考に少し手を加える必要があります。 AWS S3で1000件以上のオブジェクトを操作する方法 おわりに 大学以来、久々に Python を触りました。そういえば、業務では初めてです。 普段は C# をメインで書いているので、こういうのをサクッと書くのにやっぱり Python 向いてるなと思いました。 こういうスクリプト考える作業、とても好きです。 参照 https://dev.classmethod.jp/articles/aws-cli-count-s3-filesize/ https://inokara.hateblo.jp/entry/2020/12/03/000341 https://www.fenet.jp/aws/column/aws-beginner/946/ https://serverfault.com/questions/84815/how-can-i-get-the-size-of-an-amazon-s3-bucket
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

seleniumでreadonlyを解除する

seleniumでreadonlyを解除する seleniumでの操作中に操作したいテキストボックスがDatePickerかつreadonlyで通常の操作が できなかったため、本記事のコードを実行した。 コード from selenium import webdriver driver = webdriver.Chrome("./chromedriver") ... param = "document.getElementsByClassName('readonly解除場所')[1].removeAttribute('readonly');" driver.execute_script(param)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VertexAI Pipelines 入門編 その2 パイプラインを理解する

はじめに 本記事ではVertexAI Pipelinesで用いる基本的なパイプラインの書き方について紹介します。 そもそものVeretexAI Pipelinesについての説明や、コンポーネントの作成から実行までの流れ、コンポーネントの紹介については、以下の記事を先にお読みください。 本記事は以下の記事を前提にお話しますので、VeretexAI Pipelinesが全く初めての方には強く推奨します。 VertexAI Pipelines 入門編その0 Hello World VertexAI Pipelines 入門編その1 コンポーネントを理解する 注意事項 記載されているコードは、2021年11月現在、以下のライブラリでのバージョンで実行が確認されたものです。 kfp==1.8.9 google-cloud-aiplatform==1.7.1 バージョンの互換性によってはエラーとなったり想定外の挙動となる可能性があります。 引数を受け入れるパイプラインを作成する コンポーネントの引数と同様にパイプラインにも引数がサポートされています。 作り方次第では1つのパイプラインで様々なケースに対応できる、汎用的なパイプラインが実現可能です。 以下の例ではそのパイプラインの書き方とコンパイル方法について記述しています。 python from kfp import dsl from kfp.v2.dsl import component from kfp.v2 import compiler # 文字列を受け入れ、文字列を出力するコンポーネント @component(base_image="python:3.7") def generate_name_op(first_name:str, last_name:str) -> str: return f"{first_name} {last_name}" # 文字列の出力を受け入れるコンポーネント @component(base_image="python:3.7") def display_str_op(var: str): print(var) pass @dsl.pipeline( name="tutorial-pipeline" ) def tutorial_pipeline(first_name:str, last_name:str): out_1 = generate_name_op(first_name, last_name) display_str_op(out_1.output) pass compiler.Compiler().compile( pipeline_func = tutorial_pipeline, package_path = "任意のパス", ) コンパイルしたパイプラインを実行します。 引数のparameter_valuesにパイプライン関数での引数名と値を持つdictを挿入します。 python import google.cloud.aiplatform as aip aip.init( project="任意のプロジェクトID", location="us-central1" ) aip.PipelineJob( display_name = "tutorial pipeline", template_path="コンパイル時に指定したpackage_path", pipeline_root=f"gs://任意のバケット名/path/to/xxxx", parameter_values=dict( first_name = "バーテックス", last_name = "太郎" ) ).submit() パイプラインのコンパイルや実行の詳しい説明は、VertexAI Pipelines 入門編その0 Hello Worldをご覧ください。 パイプラインで条件分岐を実装する パイプライン関数では、IF文がサポートされていません。 なぜならパイプライン関数はコンパイル時に一度実行され、その時の各コンポーネントは入出力の型しか返さないため、これらの出力値をIF文に使用しても意味がないからです。 コンポーネントの出力値によって条件分岐をするためには、with dsl.Condition(...)を用います。第一引数に条件文を入れることで、これがTrueとなる場合にのみwithが対象とするスコープが実行されます。 python from kfp import dsl @dsl.pipeline( name="tutorial-pipeline" ) def tutorial_pipeline(): out_1 = generate_name_op("バーテックス", "太郎") with dsl.Condition(out_1.output == "バーテックス 太郎" ,name="condition_1"): display_str_op(out_1.output) with dsl.Condition(out_1.output == "バーテックス 次郎" ,name="condition_2"): display_str_op(out_1.output) pass 実行すると以下のようなパイプラインが表示されます。 condtion_1のみ実行されており、condition_2は実行されていません(グレー表示) パイプラインでfor文を実装する。 パイプライン関数では、IF文と同様にfor文もサポートされていません。理由はIF文と同じです。 for文を実装したい場合は、with dsl.ParallelFor(items) as itemというようなコードで実装します。itemsにはlistをdumpしたものが格納されている必要があります。itemにはitemsの各要素がdumpされたものが渡されます。 このとき、itemsの要素数だけ並列実行されます。あまりにも要素数が多すぎる場合はVertexAI Pipeliensの上限に抵触する恐れがありますので注意が必要です。 https://cloud.google.com/vertex-ai/quotas#vertex-ai-pipelines python from kfp import dsl from kfp.v2.dsl import component @component def generate_items_op() -> str: import json return json.dumps( [ {"hoge":1}, {"hoge":2} ]) @component(base_image="python:3.7") def display_dict_op(var: str): import json print(json.loads(var)) pass @dsl.pipeline( name="tutorial-pipeline" ) def tutorial_pipeline(): out_1 = generate_items_op() with dsl.ParallelFor(out_1.output) as item: display_dict_op(item) # 先述で定義したコンポーネント pass 実行すると、generate_items_opが完了するまでは以下の図のようにloopは1つだけに見えます。 計算が終わり要素数が判明すると2 iterationsとloop数が表れ、問題なく並列実行されていることが分かります。 全体のコード これまでの処理内容を実施できる全体のコードを共有します。 python from kfp import dsl from kfp.v2.dsl import component from kfp.v2 import compiler import google.cloud.aiplatform as aip # 文字列を受け入れ、文字列を出力するコンポーネント @component(base_image="python:3.7") def generate_name_op(first_name:str, last_name:str) -> str: return f"{first_name} {last_name}" # 文字列の出力を受け入れるコンポーネント @component(base_image="python:3.7") def display_str_op(var: str): print(var) pass # itemsを作成するコンポーネント @component def generate_items_op() -> str: import json return json.dumps( [ {"hoge":1}, {"hoge":2} ]) # json dumpされたものを受け入れるコンポーネント @component(base_image="python:3.7") def display_dict_op(var: str): import json print(json.loads(var)) pass # パイプラインの作成 @dsl.pipeline( name="tutorial-pipeline" ) def tutorial_pipeline(first_name:str, last_name:str): out_1 = generate_name_op(first_name, last_name) display_str_op(out_1.output) out_1 = generate_name_op("バーテックス", "太郎") with dsl.Condition(out_1.output == "バーテックス 太郎" ,name="condition_1"): display_str_op(out_1.output) with dsl.Condition(out_1.output == "バーテックス 次郎" ,name="condition_2"): display_str_op(out_1.output) out_2 = generate_items_op() with dsl.ParallelFor(out_2.output) as item: display_dict_op(item) pass # パイプラインのコンパイル compiler.Compiler().compile( pipeline_func = tutorial_pipeline, package_path = "任意のパス", ) # パイプラインの実行 aip.init( project="任意のプロジェクトID", location="us-central1" ) aip.PipelineJob( display_name = "tutorial pipeline", template_path="コンパイル時に指定したpackage_path", pipeline_root=f"gs://任意のバケット名/path/to/xxxx", parameter_values=dict( first_name = "バーテックス", last_name = "太郎" ) ).submit() さいごに パイプラインの説明は以上です。 以下の記事とあわせると大体のパイプラインは作れてしまうかと思います。 VertexAI Pipelines 入門編その0 Hello World VertexAI Pipelines 入門編その1 コンポーネントを理解する 一方で、VertexAI AutoMLとの連携や実際の機械学習、予測データをBigQueryに入れるなどの具体的な実装方法まではご紹介できていないので、後日改めてその記事を書く予定です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VertexAI Pipelines 入門編その2 パイプラインを理解する

はじめに 本記事ではVertexAI Pipelinesで用いる基本的なパイプラインの書き方について紹介します。 そもそものVeretexAI Pipelinesについての説明や、コンポーネントの作成から実行までの流れ、コンポーネントの紹介については、以下の記事を先にお読みください。 本記事は以下の記事を前提にお話しますので、VeretexAI Pipelinesが全く初めての方には強く推奨します。 VertexAI Pipelines 入門編その0 Hello World VertexAI Pipelines 入門編その1 コンポーネントを理解する 注意事項 記載されているコードは、2021年11月現在、以下のライブラリでのバージョンで実行が確認されたものです。 kfp==1.8.9 google-cloud-aiplatform==1.7.1 バージョンの互換性によってはエラーとなったり想定外の挙動となる可能性があります。 引数を受け入れるパイプラインを作成する コンポーネントの引数と同様にパイプラインにも引数がサポートされています。 作り方次第では1つのパイプラインで様々なケースに対応できる、汎用的なパイプラインが実現可能です。 以下の例ではそのパイプラインの書き方とコンパイル方法について記述しています。 python from kfp import dsl from kfp.v2.dsl import component from kfp.v2 import compiler # 文字列を受け入れ、文字列を出力するコンポーネント @component(base_image="python:3.7") def generate_name_op(first_name:str, last_name:str) -> str: return f"{first_name} {last_name}" # 文字列の出力を受け入れるコンポーネント @component(base_image="python:3.7") def display_str_op(var: str): print(var) pass @dsl.pipeline( name="tutorial-pipeline" ) def tutorial_pipeline(first_name:str, last_name:str): out_1 = generate_name_op(first_name, last_name) display_str_op(out_1.output) pass compiler.Compiler().compile( pipeline_func = tutorial_pipeline, package_path = "任意のパス", ) コンパイルしたパイプラインを実行します。 引数のparameter_valuesにパイプライン関数での引数名と値を持つdictを挿入します。 python import google.cloud.aiplatform as aip aip.init( project="任意のプロジェクトID", location="us-central1" ) aip.PipelineJob( display_name = "tutorial pipeline", template_path="コンパイル時に指定したpackage_path", pipeline_root=f"gs://任意のバケット名/path/to/xxxx", parameter_values=dict( first_name = "バーテックス", last_name = "太郎" ) ).submit() パイプラインのコンパイルや実行の詳しい説明は、VertexAI Pipelines 入門編その0 Hello Worldをご覧ください。 パイプラインで条件分岐を実装する パイプライン関数では、IF文がサポートされていません。 なぜならパイプライン関数はコンパイル時に一度実行され、その時は各コンポーネントは入出力の型しか返さないので、IF文をコンポーネントの出力値に対する条件分岐として使用することができないからです。 これをするためには、with dsl.Condition(...)を用います。第一引数に条件文を入れることで、これがTrueとなる場合にのみwithが対象とするスコープが実行されます。 python from kfp import dsl @dsl.pipeline( name="tutorial-pipeline" ) def tutorial_pipeline(): out_1 = generate_name_op("バーテックス", "太郎") with dsl.Condition(out_1.output == "バーテックス 太郎" ,name="condition_1"): display_str_op(out_1.output) with dsl.Condition(out_1.output == "バーテックス 次郎" ,name="condition_2"): display_str_op(out_1.output) pass 実行すると以下のようなパイプラインが表示されます。 condtion_1のみ実行されており、condition_2は実行されていません(グレー表示) パイプラインでfor文を実装する。 パイプライン関数では、IF文と同様にFor文もサポートされていません。理由はIF文と同じです。 for文を実装したい場合は、with dsl.ParallelFor(items) as itemというようなコードで実装します。itemsにはlistをdumpしたものが格納されている必要があります。itemにはitemsの各要素がdumpされたものが渡されます。 このとき、itemsの要素数だけ並列実行されます。あまりにも要素数が多すぎる場合はVertexAI Pipeliensの上限に抵触する恐れがありますので注意が必要です。 https://cloud.google.com/vertex-ai/quotas#vertex-ai-pipelines python from kfp import dsl from kfp.v2.dsl import component @component def generate_items_op() -> str: import json return json.dumps( [ {"hoge":1}, {"hoge":2} ]) @component(base_image="python:3.7") def display_dict_op(var: str): import json print(json.loads(var)) pass @dsl.pipeline( name="tutorial-pipeline" ) def tutorial_pipeline(): out_1 = generate_items_op() with dsl.ParallelFor(out_1.output) as item: display_dict_op(item) # 先述で定義したコンポーネント pass 実行すると、generate_items_opが完了するまでは以下の図のようにloopは1つだけに見えます。 計算が終わり要素数が判明すると2 iterationsとloop数が表れ、問題なく並列実行されていることが分かります。 全体のコード これまでの処理内容を実施できる全体のコードを共有します。 python from kfp import dsl from kfp.v2.dsl import component from kfp.v2 import compiler import google.cloud.aiplatform as aip # 文字列を受け入れ、文字列を出力するコンポーネント @component(base_image="python:3.7") def generate_name_op(first_name:str, last_name:str) -> str: return f"{first_name} {last_name}" # 文字列の出力を受け入れるコンポーネント @component(base_image="python:3.7") def display_str_op(var: str): print(var) pass # itemsを作成するコンポーネント @component def generate_items_op() -> str: import json return json.dumps( [ {"hoge":1}, {"hoge":2} ]) # json dumpされたものを受け入れるコンポーネント @component(base_image="python:3.7") def display_dict_op(var: str): import json print(json.loads(var)) pass # パイプラインの作成 @dsl.pipeline( name="tutorial-pipeline" ) def tutorial_pipeline(first_name:str, last_name:str): out_1 = generate_name_op(first_name, last_name) display_str_op(out_1.output) out_1 = generate_name_op("バーテックス", "太郎") with dsl.Condition(out_1.output == "バーテックス 太郎" ,name="condition_1"): display_str_op(out_1.output) with dsl.Condition(out_1.output == "バーテックス 次郎" ,name="condition_2"): display_str_op(out_1.output) out_2 = generate_items_op() with dsl.ParallelFor(out_2.output) as item: display_dict_op(item) pass # パイプラインのコンパイル compiler.Compiler().compile( pipeline_func = tutorial_pipeline, package_path = "任意のパス", ) # パイプラインの実行 aip.init( project="任意のプロジェクトID", location="us-central1" ) aip.PipelineJob( display_name = "tutorial pipeline", template_path="コンパイル時に指定したpackage_path", pipeline_root=f"gs://任意のバケット名/path/to/xxxx", parameter_values=dict( first_name = "バーテックス", last_name = "太郎" ) ).submit() さいごに パイプラインの説明は以上です。 以下の記事とあわせると大体のパイプラインは作れてしまうかと思います。 VertexAI Pipelines 入門編その0 Hello World VertexAI Pipelines 入門編その1 コンポーネントを理解する 一方で、VertexAI AutoMLとの連携や実際の機械学習、予測データをBigQueryに入れるなどの具体的な実装方法まではご紹介できていないので、後日改めてその記事を書く予定です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PythonのPandasとMatplotlibを使用してCSVファイルをグラフ化する

はじめに 外の気温を温度センサーで計測してCSVファイルに書き込み,PandasとMatplotlibを使用して平均を求め,グラフ化をしてみました. 環境 プログラミング言語 : Python 3.8.10 Pandas : 1.3.4 Matplotlib : 3.4.3 CSVファイル データ件数 : 1900件(8:30~17:33) datetime temperture 2021/11/10-08:30:12.2 17.3125 2021/11/10-08:30:12.2 17.3125 2021/11/10-08:30:12.2 17.25 2021/11/10-08:30:12.2 17.25 2021/11/10-08:30:12.2 17.3125 ... ... 2021/11/10-17:33:28.2 17.25 2021/11/10-17:33:28.2 17.25 2021/11/10-17:33:28.2 17.25 2021/11/10-17:33:28.2 17.25 2021/11/10-17:33:28.2 17.25 プログラム #pandasとmatplotlibをインポート import pandas as pd import matplotlib.pyplot as plt #csvファイルを読み込み df = pd.read_csv('new/20211110/soto.csv', names = ['datetime', 'temperture']) #datetime64型に変換 df['datetime'] = pd.to_datetime(df['datetime']) #pandas.DateFrameの列をインデックス(行名)に割り当てる df1 = df.set_index("datetime") #2次元の表形式のデータに変換 df_sec = pd.DataFrame(df1) #30分ごとに平均を求める(30minを変えるとほかの間隔にできる) ave = df_sec.resample('30min').mean() #折れ線グラフ作成 ave.plot() 結果 表 グラフ 終わりに 今回は,外の気温を温度センサーで計測してCSVファイルに書き込み,pandasとmatplotlibを使用して平均を求め,グラフ化をしてみました.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む