20210910のPythonに関する記事は29件です。

ニュートラルネットワーク(重みとバイアス)

ポイント ・ニュートラルネットワークのノードに入力されたデータは ➀線形変換 ➁非線形変換 を経てノードから出力される。そのデータは出力されたノードと隣あうノードの入力データとなる。この流れが出力層まで繰り返されていく。 ・線形は直線で表現され、非線形は曲線で表現できる。 ・重みとバイアスを用いて入力データに対して線形変換が行われる。 ・非線形変換では今回、シグモイド曲線を用いた。非線形曲線は別名活性化関数と呼ぶ。 ソース import numpy as np import matplotlib.pyplot as plt def nuro(x,y,bias): #xとyについてそれぞれ-1.0,-0.8,-0.6 … 10個のデータを作成する X=np.arange(-1.0,1.0,0.2) Y=np.arange(-1.0,1.0,0.2) #0詰めの10×10の行列を準備 z=np.zeros((10,10)) for i in range(10): for j in range(10): #線形変換の実施 u=X[i]*x+Y[j]*y+bias #非線形変換(シグモイド曲線)の実施 y=1/(1+np.exp(-u)) #非線形変換後の数値を行列に格納する z[j][i]=y #グリッド表示 plt.imshow(z,"gray",vmin=0.0,vmax=1.0) plt.colorbar() plt.show() #メイン関数 def main(): #複数の重みの準備 w_x=[1.0,2.0,3.0] w_y=[3.0,5.0,7.0] #複数のバイアスの準備 bias=[0.1,0.2,0.3] for i,j,k in zip(w_x,w_y,bias): nuro(i,j,k) if __name__ == "__main__": main() 出力 ・図よりわかること 1に近いほど、白く活性化度が強い。逆に、0に近いほど、黒く活性化度が弱い。重みやバイアスを調整することで、微妙に座標に対する活性化度が変化することがわかる。 補足【シグモイド曲線について】 import numpy as np import matplotlib.pyplot as plt def sigmoid(a): return 1/(1+np.exp(-a)) a=np.arange(-5,5) b=sigmoid(a) plt.plot(a,b) plt.show() 出力 ・シグモイド曲線の特徴 シグモイド曲線のおかげで、出力データは0~1の間に収束することができる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python(enumerate,zip,dict,os.path)

今回は、enumerate,zip,dict,os.path について整理していく。 1.zipの基本 #zipでは複数のリストを同時に結び付けることができる。 list1=['東京','大阪','愛知','北海道','埼玉']#都道府県のリスト list2=[1,2,3,4,5]#数値のリスト for i, j in zip(list1,list2): print('{0}:{1}'.format(i, j)) >>> 東京:1 大阪:2 愛知:3 北海道:4 埼玉:5 2.enumerateの基本 #enumerateではインデックスとリストの中身を同時に紐つけることができる。 list1=['東京','大阪','愛知','北海道','埼玉']#都道府県のリスト for i, j in enumerate(list1): print('{0}:{1}'.format(i, j)) >>> 0:東京 1:大阪 2:愛知 3:北海道 4:埼玉 3.zipとdictの組み合わせ list1=['東京','大阪','愛知','北海道','埼玉']#都道府県のリスト list2=[1,2,3,4,5]#数値のリスト #list1とlist2の中身について、zipで結びつけ、dictによってディレクショナリを作成する。 directionary = dict(zip(list1,list2)) print(directionary) >>>{'東京': 1, '大阪': 2, '愛知': 3, '北海道': 4, '埼玉': 5} 4.os.pathによるディレクトリ構造の作成 import os #データ数 num=5 #カレントディレクトリの取得 cur_directory=os.getcwd() #フォルダ名 folders=["learn","validation","test"] #フォルダごとのパス定義 for folder in folders: for i in range(num): print(os.path.join(cur_directory,folder, str(i)+".py")) >>> /content/learn/0.py /content/learn/1.py /content/learn/2.py /content/learn/3.py /content/learn/4.py /content/validation/0.py /content/validation/1.py /content/validation/2.py /content/validation/3.py /content/validation/4.py /content/test/0.py /content/test/1.py /content/test/2.py /content/test/3.py /content/test/4.py ・上記のフォルダ構造の定義に基づき、mkdirをすることでディレクトリ構造の実態化をすることができる。 機械学習を行う前に、大量のデータを用意し、ディレクトリを用意して格納していく必要があるが、その際に自動化を図っておく技術があると便利。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python(enumerate,zip,dict,os.path,directionary)

今回は、enumerate,zip,dict,os.path,directionary について整理していく。 1. zipの基本 # zipでは複数のリストを同時に結び付けることができる。 list1=['東京','大阪','愛知','北海道','埼玉']#都道府県のリスト list2=[1,2,3,4,5]#数値のリスト for i, j in zip(list1,list2): print('{0}:{1}'.format(i, j)) >>> 東京:1 大阪:2 愛知:3 北海道:4 埼玉:5 2. enumerateの基本 # enumerateではインデックスとリストの中身を同時に紐つけることができる。 list1 = ['東京', '大阪', '愛知', '北海道', '埼玉'] # 都道府県のリスト for i, j in enumerate(list1): print('{0}:{1}'.format(i, j)) >>> 0:東京 1:大阪 2:愛知 3:北海道 4:埼玉 3. zipとdictの組み合わせ list1=['東京', '大阪', '愛知', '北海道', '埼玉'] # 都道府県のリスト list2=[1, 2, 3, 4, 5] # 数値のリスト # list1とlist2の中身について、zipで結びつけ、dictによってディレクショナリを作成する。 directionary = dict(zip(list1,list2)) print(directionary) >>>{'東京': 1, '大阪': 2, '愛知': 3, '北海道': 4, '埼玉': 5} 4. directionary(keyとvalue) directionary['三重'] = 6 # 要素の追加 print(directionary) >>> {'東京': 1, '大阪': 2, '愛知': 3, '北海道': 4, '埼玉': 5, '三重': 6} # directionaryから要素の取得:itemsでは、keyとvalueを同時に取得することができる。 for ad_key,ad_val in directionary.items(): print(ad_key,ad_val) >>> 東京 1 大阪 2 愛知 3 北海道 4 埼玉 5 5. os.pathによるディレクトリ構造の作成 import os # データ数 num=5 # カレントディレクトリの取得 cur_directory = os.getcwd() #フォルダ名 folders = ["learn", "validation", "test"] # フォルダごとのパス定義 for folder in folders: for i in range(num): print(os.path.join(cur_directory, folder, str(i) + ".py")) >>> /content/learn/0.py /content/learn/1.py /content/learn/2.py /content/learn/3.py /content/learn/4.py /content/validation/0.py /content/validation/1.py /content/validation/2.py /content/validation/3.py /content/validation/4.py /content/test/0.py /content/test/1.py /content/test/2.py /content/test/3.py /content/test/4.py 上記のフォルダ構造の定義に基づき、mkdirをすることでディレクトリ構造の実態化をすることができる。 機械学習を行う前に、大量のデータを用意し、ディレクトリを用意して格納していく必要があるが、その際に自動化を図っておく技術があると便利。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

画像ファイルのRGBの数値をcsvファイルに出力する

ドット絵をBlender(3Dモデリングソフト)に取り込むために作ったもの。 https://twitter.com/gtyksculpt/status/1436290905221189636?s=20 # ファイル名などに日本語があるとエラーになります。 # 画像のピクセルごとにBlue(RGBのB)の数値0-255をcsvファイルに出力する。 import cv2 import csv import numpy import os ext = "png" # 画像ファイルの拡張子 img_name = "imgfile" # 画像ファイルの名前 csv_name = "csvfile" # csvファイルの名前 fdr = r'C:\Users\User\Documents\python\pixcel' # 画像を置く場所 csvfdr = r'C:\Users\User\Documents\python\pixcel\csv' # csvファイルを出力する場所 fname = img_name + "." + ext fpath = os.path.join(fdr, fname) bgr_array = cv2.imread(fpath) data = numpy.array(bgr_array[:, :, 0]) # ここでは[:, :, 0]の部分が0なのでBlueの値(0~255)が出力される。 # [:, :, 1]ならGreen、[:, :, 2]ならRedの数値が出力される。 csvname = csv_name + ".csv" csvpath = os.path.join(csvfdr, csvname) numpy.savetxt(csvpath, data,fmt="%.0f", delimiter=",")
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LambdaでS3のPDFを画像化して保存(Docker,Python)

はじめに LambdaでPdfを画像化します。windowsであればpopplerのstatic buildがあるので比較的簡単にできますが、Lambdaの環境ではコンテナを使わないとその方法では実行できません。コンテナの勉強を兼ねて作りました。 前提 Dockerの実行環境 AWS CLIとaws configureの設定 やること Dockerイメージの準備 ECRの準備 Lambdaの作成 Dockerイメージの準備 以下のようなファイル構成です。 CreatePdfThumbnail │ app.py │ Dockerfile │ env.txt └─ requirements.txt Dockerfileは以下のようになっています。 FROM public.ecr.aws/lambda/python:3.6 # ベースイメージはPython3.6環境を使いたいのでpublic.ecr.aws/lambda/python:3.6 RUN yum -y install poppler-utils # poppler-utilsをインストール # apt-getは使えないので注意 COPY app.py ${LAMBDA_TASK_ROOT} COPY requirements.txt . RUN pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}" # 必要なライブラリのインストール CMD ["app.lambda_handler"] 以下のapp.pyがLambdaの本体です。 app.py import json import os, io from pathlib import Path from pdf2image import convert_from_bytes, convert_from_path import boto3 import PIL import requests def lambda_handler(event, context): pdf_name = event['queryStringParameters']['pdfName'] # queryStringParamtersはAPI Gateway用 bucket_name = 'my-bucket' s3 = boto3.client('s3') response = s3.get_object(Bucket=bucket_name, Key=pdf_name) data = response['Body'].read() pages = convert_from_bytes(data) # pagesはPILのImageリストなのでbyteに変換 img_bytes = io.BytesIO() pages[0].save(img_bytes, format='PNG') img_bytes = img_bytes.getvalue() base = os.path.splitext(os.path.basename(pdf_name))[0] s3.put_object(Body=img_bytes,Bucket=bucket_name,Key=base+'.png') return { 'statusCode': 200, 'body': 'DONE!' } pdfの一ページ目を画像にしてS3に保存します。pdf2imageを使ってます(これにpopplerが必要)。 requirements.txtでは必要なライブラリを記述します(boto3はいらないかも)。 requirements.txt pdf2image boto3 requests ローカルでテストするときは、Dockerfileのあるディレクトリで以下のコマンドでイメージをビルドしてrunしてください。 docker build -t test . docker run -p 9000:8080 test:latest コードでboto3などを使っていてcredentialが必要な場合は、env.txtに以下のように記述して実行時に渡します。 env.txt AWS_DEFAULT_REGION=ap-northeast-1 AWS_ACCESS_KEY_ID=IAMで発行する適切な権限を持つアクセスキー AWS_SECRET_ACCESS_KEY=そのシークレットキー docker run --env-file ./env.txt -p 9000:8080 test あとはhttp://localhost:9000/2015-03-31/functions/function/invocationsにPOSTリクエストを投げてあげるとテストができます。↓Pythonの例 import requests data = { 'queryStringParameters': { 'pdfName': 'test.pdf' } } response = requests.post("http://localhost:9000/2015-03-31/functions/function/invocations",json=data) ECRの準備 ECRは基本的にボタンをポチポチするだけです。すごく便利。 ↓次にECR側です。コンソールからElastic Container Registryを選択し、リポジトリを作成をクリック。 ↓リポジトリ名を入力し、下にスクロールしてリポジトリを作成をクリック。 ↓プッシュコマンドの表示をクリック。 ↓出てきたコマンドをDockerfileのある場所でコマンドプロンプトからたたけばイメージがECRにpushされる(WindowsでもmacOS/LinuxタブのコマンドでOK)。 ↓こんな感じでpushされてればOK。 Lambdaの作成 コンソールからLambdaを開いて関数の作成をクリック。以下の画像のように「コンテナイメージ」を選択し、関数名と先ほど作成したコンテナイメージURIを入力したあと関数の作成をクリック。関数名はECRのリポジトリ名と同じにする。 S3やDynamoDBにアクセスする際には、設定タブの「一般設定」→「編集」→「IAMコンソールで~~~のロールを表示します」からS3やDynamoDBの適切なポリシーをアタッチしてください。 以上。コードのプレビューはできませんが、テストタブからテストはできます。エイリアスやバージョンの設定も触っていきたい。 おわりに OpenGL対応のFaaSってないですか。間違い等ありましたらご指摘よろしくお願いします。 参考 コンテナイメージで Python Lambda 関数をデプロイする Lambda コンテナイメージをローカルでテストする
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JupyterからQtアプリケーションを動かす

はじめに Jupyter NotebookやJupyter QtConsoleでQtのQWidgetやQMainwindowなどをshow()で動かそうとするとイベントループの関係でフリーズするか"Kernel died"になります。マジックコマンド%gui qtでこれは解決しますが、毎回これを実行するのもだるいし、同じコードブロックで実行すると意味がないとか結構謎な挙動を示します。 ここでは、%gui qtの実行を、Qtウィジェットの__init__内で勝手にやってくれる便利な方法を紹介します。 ポイント QWidget.__init__の前にQApplicationを走らせる。 QApplicationを走らせる前に%gui qtを走らせる。 Garbage collectionでQApplicationが持っていかれないようにする。 方法 まずは%gui qtを走らせてからQApplicationを立ち上げる関数です。 from qtpy.QtWidgets import QApplication APPLICATION = None def gui_qt(): try: from IPython import get_ipython except ImportError: get_ipython = lambda: False shell = get_ipython() if shell and shell.active_eventloop != "qt": shell.enable_gui("qt") return None def get_app(): gui_qt() app = QApplication.instance() if app is None: app = QApplication([]) global APPLICATION APPLICATION = app return app 関数gui_qt内でipythonのシェルを取得し、enable_guiで%gui qtを実行します。Jupyter以外の環境でも問題ないように、try/exceptや場合分けを慎重に行います。 関数get_appでは、gui_qtを呼んでからQApplicationを走らせます。すでに走っている場合、クラスメソッドQApplication.instanceでQApplicationのインスタンスが取得できるので、これがNoneの場合のみQApplication([])で新しく用意します。 最後に、グローバル変数APPLICATIONに残しておくことで、garbage collectionを回避します。 このget_app関数をコンストラクタ内で実行します。ここでまた、app = get_app()のようにQApplicationインスタンスを取っておかないとなぜかフリーズします。 from qtpy.QtWidgets import QMainWindow class MyWidget(QMainWindow): def __init__(self): app = get_app() super().__init__() 以上で問題なく起動します。例えばmywidgetモジュールにこれがあれば from mywidget import MyWidget widget = MyWidget() widget.show() だけでOKです。起動したアプリケーションとJupyterが同時に動かせます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python3について(@,デコーダーについて)

デコーダについて 関数を装飾するとか、よく分からなかったので、自分で検証しました。 また、ふわっとしていますが、覚書として書きます。 deko.py def deko(func): #デコレータ def tent(): print("This") result = func() #これはpen()のこと print("a") return result return tent @deko def pen():#デコレーションされる関数 print("is") return "Pen" if __name__ == "__main__": #deko.pyで直接呼ばれた場合、Pen関数をプリントします print(pen()) >>>This >>>is >>>a >>>Pen 1.@が付いている関数(今回だとpen())が実行される際に、print("is")ではなくdeko(func)が実行されます 2.deko(func)の中のfuncは、pen()の関数が実行されます 3.deko(func)の関数が終了すると、pen()に戻って来ます 4.最後にpen()のreturnが返されます
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Spotify API で遊ぶ ~NetworkX で関連アーティストを可視化~

Spotify API で色々な情報を取得できると知り、面白そうだったので遊んでみました。 n 番煎じですが、あるアーティストに対して、その関連アーティストをグラフにして可視化してみます。 今回のプログラムは Google Colab で実行できるようにしています。 https://colab.research.google.com/drive/1Aev5FZ3an13QMwJuJzP9Ahekv_Oosfs1?usp=sharing GitHub は以下。 https://github.com/yousukeayada/spotify 環境 macOS Catalina 10.15.6 Python 3.8.1 spotipy==2.19.0 networkx==2.6.2 API のテスト まず Spotify for Developers にログインして create an app https://developer.spotify.com/dashboard/ 作成できたら client id と client secret をメモっておきます。 Python のライブラリ spotipy を使ってテストします。 pip install spotipy まず、指定したアーティストの情報を取得してみます。 https://spotipy.readthedocs.io/en/2.19.0/#getting-started アーティスト ID は、アプリだと「リンクをシェア」から URL がわかるので、そこから抜き出します。 import json import spotipy from spotipy.oauth2 import SpotifyClientCredentials def auth_spotify(): client_id = "xxxxx" client_secret = "xxxxx" client_credentials_manager = SpotifyClientCredentials(client_id, client_secret) return spotipy.Spotify(client_credentials_manager=client_credentials_manager, language='en') spotify = auth_spotify() name_id = {"Sambomaster": "5ydDSP9qSxEOlHWnpbblFB", "FLOW": "3w2HqkKa6upwuXEULtGvnY"} artist_info = spotify.artist(name_id["Sambomaster"]) print(json.dumps(artist_info)) 出力を jq コマンドで整形したものが以下になります。 artist.json { "external_urls": { "spotify": "https://open.spotify.com/artist/5ydDSP9qSxEOlHWnpbblFB" }, "followers": { "href": null, "total": 240331 }, "genres": [ "j-pop", "j-poprock", "j-rock" ], "href": "https://api.spotify.com/v1/artists/5ydDSP9qSxEOlHWnpbblFB", "id": "5ydDSP9qSxEOlHWnpbblFB", "images": [ { "height": 640, "url": "https://i.scdn.co/image/ab6761610000e5ebb83514fcda99f392cabbb051", "width": 640 }, { "height": 320, "url": "https://i.scdn.co/image/ab67616100005174b83514fcda99f392cabbb051", "width": 320 }, { "height": 160, "url": "https://i.scdn.co/image/ab6761610000f178b83514fcda99f392cabbb051", "width": 160 } ], "name": "Sambomaster", "popularity": 53, "type": "artist", "uri": "spotify:artist:5ydDSP9qSxEOlHWnpbblFB" } 次に、関連アーティストの取得です。 # (略) artist_info = spotify.artist(name_id["Sambomaster"]) artist_id = artist_info["id"] related_artists = spotify.artist_related_artists(artist_id) print(json.dumps(related_artists)) 出力(長いので一部のみ) related_artists.json { "artists": [ { "external_urls": { "spotify": "https://open.spotify.com/artist/2dP0aHVXt8dDPCw5d2Jw0m" }, "followers": { "href": null, "total": 138679 }, "genres": [ "j-pop", "j-poprock", "j-rock", "japanese alternative rock", "japanese punk rock" ], "href": "https://api.spotify.com/v1/artists/2dP0aHVXt8dDPCw5d2Jw0m", "id": "2dP0aHVXt8dDPCw5d2Jw0m", "images": [ { "height": 640, "url": "https://i.scdn.co/image/ab6761610000e5eb2b458dd9eb80278b2566d299", "width": 640 }, { "height": 320, "url": "https://i.scdn.co/image/ab676161000051742b458dd9eb80278b2566d299", "width": 320 }, { "height": 160, "url": "https://i.scdn.co/image/ab6761610000f1782b458dd9eb80278b2566d299", "width": 160 } ], "name": "GING NANG BOYZ", "popularity": 47, "type": "artist", "uri": "spotify:artist:2dP0aHVXt8dDPCw5d2Jw0m" }, { "external_urls": { ... } 以上でテスト完了です。 関連アーティストの情報をまとめる 流れとしては以下にようになります。 初めに 1 組アーティストを指定して、その関連アーティストを取得する。 それぞれの関連アーティストに対して、またその関連アーティストを取得する。 繰り返しすぎるととんでもない量になるので、注意してください。 また、一部 search に失敗するアーティストがあります(結果が日本語で返ってきたりなど)が、そちらは省くことにしました。調べたところ API 側のエラーっぽいです。 search ではなく spotify.artist(id) を使うことでおそらく確実に取得できます。 https://teratail.com/questions/236378 import pandas as pd import spotipy from spotipy.oauth2 import SpotifyClientCredentials import networkx as nx import matplotlib.pyplot as plt def auth_spotify(): client_id = "xxxxx" client_secret = "xxxxx" client_credentials_manager = SpotifyClientCredentials(client_id, client_secret) return spotipy.Spotify(client_credentials_manager=client_credentials_manager, language='en') def add_new_artist(artist_id, df): artist_info = spotify.artist(artist_id) related_artists = spotify.artist_related_artists(artist_id) related_artist_ids = [artist["id"] for artist in related_artists["artists"]] related_artist_names = [artist["name"] for artist in related_artists["artists"]] row = pd.Series([artist_info["id"], artist_info["name"], artist_info["popularity"], artist_info["genres"], related_artist_ids, related_artist_names], index=df.columns) df = df.append(row, ignore_index=True) return df spotify = auth_spotify() artists_df = pd.DataFrame(columns=["id", "name", "popularity", "genres", "related_artist_ids", "related_artist_names"]) # 最初のアーティストを取得 name_id = {"Sambomaster": "5ydDSP9qSxEOlHWnpbblFB", "FLOW": "3w2HqkKa6upwuXEULtGvnY"} artists_df = add_new_artist(name_id["Sambomaster"], artists_df) # 関連アーティストとその関連アーティストを取得 l = len(artists_df["related_artist_names"][0]) for i in range(l): ids = artists_df["related_artist_ids"][i] names = artists_df["related_artist_names"][i] for id, name in zip(ids, names): print(name) artists_df = add_new_artist(id, artists_df) artists_df = artists_df.drop_duplicates(subset="id") artists_df.to_csv("artists.csv", index=False) 最終的に以下のような DataFrame が作成できました。 NetworkX で可視化 Google Colab を使う場合は元から入っています。 pip install networkx 有向グラフで可視化します。単にグラフを描画するだけだと複雑で見辛いので、ここではいくつかルールを設けます。 popularity が大きいほどノードを大きくする in_degree (自分に向かう矢印)が多いほどノードの色を濃くする グラフを見やすくする工夫は他にもたくさんあると思います。例えば、ジャンルに j-rock が含まれているものだけ色を変えるなど、目的に応じて設定すればいいでしょう。 from ast import literal_eval import pandas as pd import networkx as nx import matplotlib.pyplot as plt from matplotlib.font_manager import FontProperties artists_df = pd.read_csv("artists.csv", converters={"genres":literal_eval, "related_artist_ids":literal_eval, "related_artist_names":literal_eval}) G = nx.MultiDiGraph() # node データの追加 names = [name for name in artists_df["name"]] G.add_nodes_from(names) edges = [] for name, related_artist_names in zip(artists_df["name"], artists_df["related_artist_names"]): for related_name in related_artist_names: if related_name in names: e = (name, related_name) edges.append(e) # print(e) # edge データの追加 G.add_edges_from(edges) for d in zip(*G.in_degree()): max_in_degree = max(d) plt.figure(figsize=(30, 30), dpi=300) pos = nx.spring_layout(G, k=0.3, seed=23) node_size = [popularity*40 for popularity in artists_df["popularity"]] # in_degree を正規化して alpha とする node_alpha = [d[1] / max_in_degree for d in G.in_degree()] nx.draw_networkx_nodes(G, pos, node_color='red',alpha=node_alpha, node_size=node_size, edgecolors="black") text_items = nx.draw_networkx_labels(G, pos, font_size=5, font_weight="bold") nx.draw_networkx_edges(G, pos, alpha=0.4, edge_color='royalblue') # 日本語フォントを使う font_prop = FontProperties(fname="/System/Library/Fonts/ヒラギノ角ゴシック W8.ttc", weight=1000, size=5) for t in text_items.values(): t.set_fontproperties(font_prop) plt.savefig("network.png") これで可視化した画像が保存されます。 サンボマスターを起点とした場合、以下のようなグラフとなりました。 なんとなく 2 つグループに分かれていることが読み取れますが、詳しくはわからないので、拡大して見てみましょう。 真ん中左あたりにコブクロ、いきものがかり、スキマスイッチなど、テレビでもよく見るようなアーティストが集まっていることがわかります。 in_degree が最も多かったのは Straightener 、次点で Base Ball Bear でした。 別のアーティストでもやってみます。 FLOW を指定してみるとわかりやすく複数のグループにわかれました。詳細は画像参照。 また、それぞれのグループの間に橋渡し的な存在を確認できます。例えば、青色と紫色を繋ぐのは UVERworld 、青とオレンジを繋ぐのは藍井エイル、青と緑は ORANGE RANGE です。 最後に、最近話題の YOASOBI でやってみます。 やはり若者に人気のアーティストが多い感じがします。左下には上記と同じような J-Pop 島があります。 J-Pop はあらゆるジャンルからの導線がありそうですね。 ジャンルの集計 関連アーティストのジャンルを多い順に集計します。 from ast import literal_eval from collections import defaultdict import pandas as pd def count_genres(artists_df): genres_dict = defaultdict(int) for genres in artists_df["genres"]: for g in genres: genres_dict[g] += 1 genres_dict = sorted(genres_dict.items(), key=lambda x:x[1], reverse=True) genres_dict2 = {} for k, v in genres_dict: genres_dict2[k] = v return genres_dict2 artists_df = pd.read_csv("artists.csv", converters={"genres":literal_eval, "related_artist_ids":literal_eval, "related_artist_names":literal_eval}) genres_dict = count_genres(artists_df) for k, v in genres_dict.items(): print(k, v) 先ほどの YOASOBI だと以下のような結果が得られます。 j-pop 117 j-rock 53 j-poprock 42 anime 29 japanese alternative rock 24 japanese teen pop 23 anime rock 16 vocaloid 10 japanese r&b 9 japanese singer-songwriter 8 j-division 6 j-pop boy group 6 j-acoustic 5 japanese indie rock 5 japanese soul 4 j-pixie 4 japanese pop rap 4 j-idol 4 japanese electropop 3 j-rap 3 classic j-pop 3 j-pop girl group 3 j-indie 3 japanese punk rock 2 japanese emo 2 japanese alternative pop 1 japanese new wave 1 city pop 1 japanese trap 1 kyushu indie 1 kawaii edm 1 japanese vtuber 1 honeyworks 1 j-punk 1 japanese pop punk 1 visual kei 1 japanese shoegaze 1 hokkaido indie 1 okinawan folk 1 okinawan pop 1 japanese indie pop 1 枝刈り エッジが多すぎて見辛い場合は枝刈りをするといいでしょう。試しに単方向のエッジを削除して、双方向のみ残します。 import networkx as nx def remove_unidirectional(G): unidirectional = [(u, v) for u, v in G.edges() if not (v, u) in G.edges()] for u, v in unidirectional: G.remove_edge(u, v) return G G = nx.MultiDiGraph() # (中略) print(G.number_of_edges()) # 枝刈り前 G = remove_unidirectional(G) print(G.number_of_edges()) # 枝刈り後 # 以下グラフ描画 # ... 上記サンボマスターの例で枝刈りしてみました。エッジの数は 500 ほど減ったんですが、見やすさはあまり変わらないですね... 最後に 普段から Spotify を利用してはいるものの、音楽には全然詳しくないので、これを機に色んな曲を聴いてみようと思いました。みなさんも好きなアーティストでやってみてはどうでしょうか。新しい音楽に触れるきっかけになれば幸いです。 今回のプログラムは Colab 上でも実行できます。 https://colab.research.google.com/drive/1Aev5FZ3an13QMwJuJzP9Ahekv_Oosfs1?usp=sharing 参考 https://aidemy.net/magazine/707/ https://stackoverflow.com/questions/32742976/how-to-read-a-column-of-csv-as-dtype-list-using-pandas https://qiita.com/odanny/items/7c550010f5915ae4acdc https://www.kite.com/python/docs/matplotlib.font_manager.FontProperties
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python で静的メソッドベース API っぽいのを実現するメモ

背景 インスタンスでメソッド呼んだりするのが好きくない. (インスタンスの内部状態に依存しないようにしたい) bora = Bora() dora = bora.Dora("test") みたいなの, 好きくない. インスタンスをメソッドで作る API を Python で実現したい obj = Bora.Dora.NewObject("test") みたいな? 名前空間(namespace)で機能を切り分けして. 名前空間の階層? ディレクトリを作ればいいですが, ファイルが増えて面倒です. とりあえず inner class でいけるっぽい. class Bora: class Dora: ... class method 静的メソッドは @staticmethod, @classmethod でいけます. ここでは @classmethod 使います. class Bora: class Dora: @classmethod def NewObject(cls, name: str): obj = cls() obj.name = name return obj Voila!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonでしか描けない美しいデシジョンツリー(決定木)を描こう!

はじめに Pythonには、デシジョンツリー(決定木)の可視化・モデル解釈のためのdtreevizというライブラリがあります。 このライブラリで表示されるデシジョンツリーは、もう美しいなんてもんじゃない。 「プロに頼んだの?」というくらいのビジュアルに、うっとりしたりしてしまいます。 ビジュアル先行ではあるが、このデシジョンツリーは、かなり使えるヤツでもある。 手元データを、Google colabだけで、コードコピペで描く!ことができるよう、適用の備忘も含めてまとめるものです。 デシジョンツリー(決定木)とは何だ? 「デシジョンツリー(決定木)」そのものの解説は、詳しい先にお預けし、ここでは私が持つ「デシジョンツリー(決定木)」に対する認識をお伝えできればと思う。 まず、以下はデータ分析コンペで有名なKaggleが提供する『Titanic生存者予測』をするためのデータセットで作成した「デシジョンツリー(決定木)」です。 この美しさなら、見た目だけで飛びついてもよいと思いますが、「デシジョンツリー(決定木)」は、データ分析結果の可読性がとてもよいという利点があります。 この『Titanic生存者予測』のデータは、”性別・年齢・客室クラス・・・等”の「説明変数」と、”生存されたか否か?”という「目的変数」で構成されたデータなので、データ分析は回帰分析でも実行できます。 分析の結果は、例えば、 「生存されたか否かは、性別や年齢が影響してそうだ。こんな関係式で表すことができる・・・」といった感じでしょうか。 では「デシジョンツリー(決定木)」ならどうなるでしょう? 「生存者の多くは女性である。残念ながら亡くなった女性の方はある客室クラスに集中しており、お一人か数名で乗船された方が多い傾向がある。 一方、男性は多くの方が亡くなっておられ、これらは若者層に多い傾向がある。」 といった感じだ。 名に「デシジョン」とつくだけあり、これは意思決定の助けになりそうだと直感できる。 また、この図をチマチマと作成しなければならないとなると閉口してしまうが、なんとこの結果は機械的な分類器にかけて表示してくれるし、この結果から気づきを得ることも多い。 高い精度は期待できないといわれるが、まず全体を把握したい、全体の傾向が知りたいというケースも多く、これは本当に重宝する。 テーマや分析条件 テーマ:『Titanic生存者予測』のデータで「デシジョンツリー(決定木)」を描こう ※Kaggleでアップされている『Titanic生存者予測』の元データは、「欠損値処理」や「性別(Male/Female)」データの0/1変換などの前処置が必要ですが、前処理を行ったデータを上げておられる方がおられますので、こちらを利用させていただくことにします。 Google colabで実行 ライブラリのインストール・インポート ⇒ データ読込み ⇒ データ確認 ⇒ 変数設定 ⇒ ロジスティック回帰分析 ⇒ 決定木分析 の流れで進める     決定木を描こう! ライブラリのインストール及びインポート 日本語化 pip install japanize-matplotlib ライブラリインポート %matplotlib inline import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns sns.set(font="IPAexGothic") from scipy.stats import norm import sklearn from sklearn.tree import DecisionTreeClassifier from sklearn import metrics 統計分析ライブラリのインストール pip install statsmodels   データの読み込み データ読込み df_past = pd.read_csv('titanic_train_mod.csv') df_future = pd.read_csv('titanic_test_mod.csv') データ確認 #データ確認(先頭5行) df_past.head() #相関係数行列(ヒートマップ)説明変数間の相関係数が高すぎるものは注意 plt.figure(figsize=(9,7)) sns.heatmap(df_past.corr(), annot=True, vmax=1, vmin=-1, center=0) plt.show() 変数設定 説明変数Xと目的変数Yを設定します。 #カラム=変数名を確認 df_past.columns Index(['PassengerId', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp', 'Parch', 'Ticket', 'Fare', 'Embarked', 'Survived'], dtype='object') #X_nameに説明変数を、y_nameに目的変数を設定 X_name = ['Pclass','Sex', 'Age', 'SibSp', 'Fare'] y_name = ['Survived'] X = df_past[X_name] y = df_past['Survived'] 注意 - 'PassengerId',はただの番号、'Name','Ticket'は文字情報なので説明変数として選択しなかった。 - 'Parch','Embarked'は、この後のロジスティック回帰でP値が大きかったので選択しなかった。'Fare'のP値も大きかったがこれはあえて残した。 - 記事ではこうしていますが、説明変数は任意に変更してください。   ロジスティック回帰分析(0/1データの場合のみ) モデル指示:線形回帰はsm.OLS()、ロジット回帰はsm.Logit()に ※有意ではない説明変数は取り除いて、再度ロジット回帰分析を行う import statsmodels.api as sm from sklearn.linear_model import LogisticRegression #ロジット回帰の場合、このライブラリが必要 model = sm.Logit(y,sm.add_constant(X)) result = model.fit() result.summary() ※有意ではない説明変数は取り除いて、再度ロジット回帰分析 result.predict(sm.add_constant(df_future[X_name])) ※結果は、確率として表現される。 決定木 決定木ライブラリのインストール・インポート pip install graphviz pip install dtreeviz from sklearn import tree from dtreeviz.trees import * import graphviz 決定木の段数設定 ※決定木の段数(max_depth)を任意に指定する dtree = tree.DecisionTreeClassifier(max_depth=2) dtree.fit(X,y) 決定木描画(縦向き) ※class_namesは任意に設定,#orientation ⇒ orientation とすれば表示は横に #決定木 viz = dtreeviz(dtree,X,y, target_name = y_name, feature_names = X_name, #orientation='LR', class_names = ["bad","soso","good"]) viz 『タイタニックでの生死は、性別で大きく傾向が異なり、男性は多くが亡くなり、女性は多くが生存。 男性は、(幼児と高齢者除く)若者層の多くが客室クラスによらず亡くなっている。 女性は、多くは生存されているが、ある客室クラスに亡くなった方が集中している。』といったことが簡単に読み取れる。 最後に 美しいだけでなく、可読性も高い。 う~ん、dtreevizは本当にすばらしい。 スッキリ解釈したい、スッキリしにくいものをうまく共有したい といった場面で絶対に活躍するはずと思います。 ※以下に決定木の学習と予測のコードものせておきます。 学習データとテストデータの設定 # データセットを学習データ(8割)とテストデータ(2割)に分割(random_stateは0) X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, test_size=0.2, random_state=0) # 分割の確認 print('分割の確認:',X_train.shape, X_test.shape, y_train.shape, y_test.shape) # 学習実行 # インスタンスの作成 model = DecisionTreeClassifier(max_depth=2) # モデルの作成 model.fit(X_train, y_train) # 学習データからの予測値 pred_train = model.predict(X_train) # テストデータからの予測値 pred_test = model.predict(X_test)     参考サイト
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ニュートン法(2変数)

ニュートン法(1変数) ニュートン法(2変数) 2変数のニュートン法も作りました。 凸関数 $f(x, y)$ の最小解と最小値をニュートン法で求める \begin{aligned} &\nabla f(x, y)=\left[\begin{array}{ll} f_{x}(x, y) \\ f_{y}(x, y) \end{array}\right] \\ &\nabla^{2} f(x, y)=\left[\begin{array}{ll} f_{x x}(x . y) & f_{x y}(x, y) \\ f_{y x}(x, y) & f_{y y}(x, y) \end{array}\right] \\ &\left(\begin{array}{l} x_{new} \\ y_{new} \end{array}\right)=\left(\begin{array}{l} x_{old} \\ y_{old} \end{array}\right)-\nabla^{2} f(x_{old}, y_{old})^{-1} \nabla f(x_{old}, y_{old}) \end{aligned} 更新式から最小解 $(x,y)$ を求めて、最後に最小値を出力する 実装 import matplotlib.pyplot as plt import numpy as np # 実装 def newton_method_2val(func0,func1, func2, initial_x, initial_y, EPS): """ func0: f(x,y) func1: f_x(x,y) func2: f_y(x,y) funcの部分はlambda関数で設定する。 func = lambda x,y : x+y+3 func(1,3) = 7 EPS:更新幅を止める基準 """ count = 0 # xの更新値の保存 x_new_list = [ ] y_new_list = [ ] diff = 10 # EPS基準で止めに行くが、止まらない値で設定しておく print("========== processing ==========") while diff >= EPS: count += 1 f_x = func1(initial_x, initial_y) f_y = func2(initial_x, initial_y) # nabla^2 f(x,y) h = 1e-10 # 微小区間 # 中心差分公式で微分 f_xx = (func1(initial_x + h, initial_y) - func1(initial_x - h, initial_y))/(2*h) f_xy = (func1(initial_x, initial_y+h) - func1(initial_x , initial_y-h))/(2*h) f_yx = (func2(initial_x + h, initial_y) - func2(initial_x - h, initial_y))/(2*h) f_yy = (func2(initial_x, initial_y+h) - func2(initial_x , initial_y-h))/(2*h) # 更新に必要なものを行列形式にまとめる initial = np.array([[initial_x], [initial_y]]) # 初期値格納 nabla = np.array([[f_x], [f_y]]) # nabla(x,y) nabla2 = np.array([[f_xx, f_xy],[f_yx, f_yy]]) # nabla^2(x,y) inv_nabla2 = np.linalg.inv(nabla2) # nabla^2(x,y) の逆行列 # 更新値を求める new = initial - np.dot(inv_nabla2, nabla) # count と 更新値 print("{}回目:x_new = {},y_new={}".format(count,new[0,0],new[1,0])) x_new_list.append(new[0,0]) # xの更新値をリストに追加 y_new_list.append(new[1,0]) # yの更新値をリストに追加 # 更新幅 diff_x = abs(initial_x - new[0,0]) diff_y = abs(initial_y - new[1,0]) # diffの大きい方でwhileを止める基準にしたい diff = max(diff_x,diff_y) # 値の更新 initial_x = new[0,0] initial_y = new[1,0] if count == 100: # 100回で収束しなければ diff = 0 # diffを強制的に0にして print("========== Do not CONVERSION =========") # while 文から抜ける print("========== Result ==========") print("solution:x = {}".format(new[0,0])) print("solution:y = {}".format(new[1,0])) value = func0(new[0,0],new[1,0]) # 解を代入した値 print("min_value:{}".format(value)) # グラフ描画 trial = np.arange(1, count+1, 1) # 1 ~ n回 fig = plt.figure(figsize=(15, 2.5)) ax = fig.add_subplot(1, 2, 1) x_value = np.array(x_new_list) plt.plot(trial, x_value, color="red") plt.title("x_process") plt.xlabel('trial') plt.ylabel('x_new') ax = fig.add_subplot(1, 2, 2) y_value = np.array(y_new_list) plt.plot(trial, y_value, color="blue") plt.title("y_process") plt.xlabel('trial') plt.ylabel('y_new') plt.show() 投入 $$f(x, y)=\frac{x^{4}}{2}+\frac{x^{3}}{3}-2 x^{2} y+4 x+3 y^{2}+4 y+4$$ newton_method_2val(lambda x,y: (1/2)*x**4+(1/3)*x**3-2*(x**2)*y+3*y**2+3*x-1*y+4, lambda x,y: 2*x**3+x**2-4*x*y+3, lambda x,y: 6*y-2*x**2-1, 9,3, 0.0001) ========== processing ========== 1回目:x_new = 6.956495810248076,y_new=14.90540384620938 2回目:x_new = 4.61060249609451,y_new=5.418088454657781 3回目:x_new = 3.129220719387754,y_new=2.6991904039382333 4回目:x_new = 2.021337899706909,y_new=1.1194672031808257 5回目:x_new = 1.1684879443600464,y_new=0.3793366434284833 6回目:x_new = 0.3027062364792097,y_new=-0.05264901159486629 7回目:x_new = -2.290661442659664,y_new=-0.3261419277396098 8回目:x_new = -2.414577217675341,y_new=2.1049424354116724 9回目:x_new = -2.5851419887405522,y_new=2.384622254255956 10回目:x_new = -2.5704464991029425,y_new=2.368993071767485 11回目:x_new = -2.5702482687485855,y_new=2.368725374647714 12回目:x_new = -2.5702482387915295,y_new=2.3687253363369853 ========== Result ========== solution:x = -2.5702482387915295 solution:y = 2.3687253363369853 min_value:-4.38238055980867 前の記事では縦軸を差分で出していたので、trialを増すごとに差分が小さくなっていく様子が見れますが、今回は解の更新の推移を表示しているので、必ずしも小さくなるとは限りませんが収束はしていきます。 答え合わせ 答え合わせに使用した wolframAlpha https://www.wolframalpha.com/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pip error: invalid command 'dist_info'

$ python -m pip install dist/slz-0.0.0.1.tar.gz DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained. pip 21.0 will drop support for Python 2.7 in January 2021. More details about Python 2 support in pip can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support pip 21.0 will remove support for this functionality. Defaulting to user installation because normal site-packages is not writeable Processing ./dist/slz-0.0.0.1.tar.gz Installing build dependencies ... done Getting requirements to build wheel ... done Installing backend dependencies ... done Preparing wheel metadata ... error ERROR: Command errored out with exit status 1: command: /usr/bin/python /usr/local/lib/python2.7/dist-packages/pip/_vendor/pep517/_in_process.py prepare_metadata_for_build_wheel /tmp/tmpZQQe8U cwd: /tmp/pip-req-build-VTEQLP Complete output (6 lines): usage: _in_process.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] or: _in_process.py --help [cmd1 cmd2 ...] or: _in_process.py --help-commands or: _in_process.py cmd --help error: invalid command 'dist_info' ---------------------------------------- ERROR: Command errored out with exit status 1: /usr/bin/python /usr/local/lib/python2.7/dist-packages/pip/_vendor/pep517/_in_process.py prepare_metadata_for_build_wheel /tmp/tmpZQQe8U Check the logs for full command output. Somehow my machine had several setuptools installation. Removing them all and reinstalling only 44.1.1 fixed the issue.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【超初心者用】PaizaCloud✖Djangoでwebアプリを作る

何の記事? YouTubeの動画を見ながらDjangoでwebアプリを構築しました。 手順の備忘録です。使用するプログラムはpythonです。 https://www.youtube.com/watch?v=nS41IkL13QE&t=1057s 手順 PaizaCloudにログインして新規サーバを作成。言語はDjangoにする ターミナルから、django-admin startproject django_websiteと入力(django_websiteはプロジェクト名) pwd➡cd django_websiteでフォルダを移動 python manage.py runserverで開発用サーバーの立ち上げ django_website/pycache/settings.pyの28行目、ALLOWED_HOSTS = []となっているところを、ALLOWED_HOSTS = [*]に書き換える 左の8000をクリックすると、サイトが表示される ターミナルでpython manage.py startapp websiteと入力してwebsiteというアプリを作成 django_website/pycache/settings.pyのINSTALLED_APPSに新しく追加したアプリ('website')を追加登録 websiteフォルダを右クリックして"templates"フォルダを作り、その中に"index.html"というファイルを作る。 index.html <h1>Hello, django!</h1> 10: websiteフォルダ内のviews.pyを以下のように書き換える views.py from django.views.generic import TemplateView class IndexView(TemplateView): template_name = "index.html" 11: websiteフォルダ内にurls.pyを作成し、以下のように記載 urls.py from django.urls import path from .views import IndexView urlpatterns = [ path('', IndexView.as_view()), ] 12: django_website>pycache>urls.pyを以下に書き換える from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('', include('website.urls')) ] これで晴れて、"Hello, django!"を表示できました。 フォルダ構成は以下の通りです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pytorchによる回帰の実施

はじめに 以前分類を実施したが、回帰もトライしてみた。 ↓前回の記事 環境 windows 10 python 3.8.10 torch 1.9.0+cu111 使うデータセット 下記のサイトから。コンクリートの圧縮力について。 説明変数に水の含有力などがある。 内容 データセットの準備 import pandas as pd import numpy as np import torch from torch import nn from torch.nn import functional as F from torch.utils.data import DataLoader from torch.utils.data import random_split from torchvision import transforms from tqdm import tqdm import matplotlib.pyplot as plt #ファイルの読み込みと確認 df = pd.read_csv('Concrete_Data.csv') df.head() データはこんな感じ。 #データを説明変数と目的変数にわける data = df.drop(df.columns[[-1]],axis=1) target = df.iloc[:,-1] # PyTorch で学習に使用できる形式へ変換 data = torch.tensor(data.values, dtype=torch.float32) target = torch.tensor(target.values, dtype=torch.float32) #今回は回帰なのでfloat32 # 目的変数と入力変数をまとめてdatasetに変換 dataset = torch.utils.data.TensorDataset(data,target) # 各データセットのサンプル数を決定 # train : val : test = 50% : 20% : 20% n_train = int(len(dataset) * 0.6) n_val = int((len(dataset) - n_train) * 0.5) n_test = len(dataset) - n_train - n_val # データセットの分割 torch.manual_seed(0) #乱数を与えて固定 train, val, test = torch.utils.data.random_split(dataset, [n_train, n_val,n_test]) #バッチサイズ batch_size = 32 # 乱数のシードを固定して再現性を確保 torch.manual_seed(0) # shuffle はデフォルトで False のため、学習データのみ True に指定 train_loader = torch.utils.data.DataLoader(train, batch_size, shuffle=True) val_loader = torch.utils.data.DataLoader(val, batch_size) test_loader = torch.utils.data.DataLoader(test, batch_size) # 辞書型変数にまとめる(trainとvalをまとめて出す) dataloaders_dict = {"train": train_loader, "val": val_loader} ネットワークの定義と学習 class Net(nn.Module): # 使用するオブジェクトを定義 def __init__(self): super(Net, self).__init__() self.fc1 = nn.Linear(8, 4) self.fc2 = nn.Linear(4, 3) self.fc3 = nn.Linear(3, 1) # 順伝播 def forward(self, x): x = self.fc1(x) x = F.relu(x) x = self.fc2(x) x = x**2*+1 x = self.fc3(x) x = F.relu(x) return x # インスタンス化 net = Net() # ネットワークの確認 net # 損失関数の設定(最小二乗誤差) criterion = nn.MSELoss() # 最適化手法の選択 optimizer = torch.optim.Adam(net.parameters(), lr=0.01) 入力変数が8、結合層を上記のように設定した(回帰なので最終的には1つに値が決まる)。2層目のところでReLU関数ではなく、放物線を入れ込んでみた。 損失関数は分類と異なり、最小二乗誤差で定義。 def train_model(net, dataloader_dict, lossfun, optimizer, num_epoch): l= [] # 重みを保持する変数 best_acc = 0.0 # GPUが使えるのであればGPUを有効化する device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") net = net.to(device) # (エポック)回分のループ for epoch in range(num_epoch): for phase in ['train', 'val']: if phase == 'train': # 学習モード net.train() else: # 推論モード net.eval() epoch_loss = 0.0 # 第1回で作成したDataLoaderを使ってデータを読み込む for inputs, labels in tqdm(dataloaders_dict[phase]): inputs = inputs.to(device) labels = labels.to(device) labels = torch.reshape(labels,(-1,1)) #サイズ変更 # 勾配を初期化する optimizer.zero_grad() # 学習モードの場合のみ勾配の計算を可能にする with torch.set_grad_enabled(phase == 'train'): outputs = net(inputs) _, preds = torch.max(outputs, 1) # 損失関数を使って損失を計算する loss = criterion(outputs, labels) if phase == 'train': # 誤差を逆伝搬する loss.backward() # パラメータを更新する optimizer.step() epoch_loss += loss.item() * inputs.size(0) # 1エポックでの損失を計算 epoch_loss = epoch_loss / len(dataloaders_dict[phase].dataset) #lossをデータで保存する a_loss = np.array(epoch_loss) l.append(a_loss) #epoch数とlossを表示する print('Epoch {}/{}'.format(epoch + 1, num_epoch)) print('epoch_loss:{:.4f}'.format(epoch_loss)) print('-'*20) #モデルを保存 torch.save(net, 'best_model.pth') #testとvalのlossとaccを抜き出してデータフレーム化 l_train = l[::2] l_train = pd.DataFrame({'train_loss_kaiki':l_train}) l_val = l[1::2] l_val = pd.DataFrame({'val_loss_kaiki':l_val}) df_loss = pd.concat((l_train,l_val),axis=1) #ループ終了後にdfを保存 df_loss.to_csv('loss_kaiki.csv', encoding='shift_jis') #学習と検証 num_epoch = 30 net = train_model(net, dataloaders_dict, criterion, optimizer, num_epoch) labelを途中reshapeしている。なくてもコードは動くが警告が出る(tensorの型がinputと異なるらしい)ので、一応入れている。 感想 割と不完全燃焼感が強い ・関数内でlossをプロットしたかったけど、カーネルが死んでしまう(未解決) ・epochの回数を増やすことでlossは減少傾向だけど、結構大きい(もっとepoch増やしたらいい?) ・力尽きたのであとはできる人に投げよう(暴論)。 ・とりあえず使ったcsvファイルとlossの結果はgithubに挙げた。 https://github.com/dem-kk/File/tree/main/Concrete
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Trible R10 GNSS rtkファイルから測点と座標情報の行だけを抽出する

Trible R10 GNSS 専用のアプリを持ってないので,rtkファイルから測点と座標情報の行だけを抽出するプログラムをつくりました. 以下は,Pycharmのjupyter notebookから持ってきたので,ちょっとおかしな記載がありますが,pyファイルで保存すれば,pythonで動くと思います. #%% md # trimble用 #when calculate again, you need #if you want #define working directroy %cd C:\GNSS\data\vrs #%% md # まずは,入力ファイルを設定します. #%% # inp_file="2021-07-05_10.nmea" # inp_file="VRS直_2021-07-05-12-48-07.rtk" inp_file="VRS直_2021-09-10-15-37-24.rtk" #%% md # ひとまず,上のところ(ファイルが入っているディレクリトリとファイル名)を入力すれば, # あとは全実行で進みます(抽出したテキストファイルが出力される.). #%% import pandas as pd import numpy as np import csv #%% #if you want #check current directory %pwd #%% #check files %ls #%% md # base_nameを取得しておきます. # 出力ファイル名を自動で作成するため #%% type(inp_file) base_name=inp_file.split('.',1)[0] base_name # inp_file="VRS直_2021-07-05-12-48-07_get.rtk" #%% # make output filename from input file automatically # out_file="2021-07-05_10_GNRMC_out_test.csv" # out_file="VRS直_2021-07-05-12-48-07_get.rtk" out_file=base_name+"_get.rtk" out_file # out_file="VRS直_2021-07-05-12-48-07_get.rtk" #%% md # encodeを指定します. ## nmeaはUTF-8か ## trimbleはshift_jisか #%% # enc_sty="UTF-8" enc_sty="shift_jis" #%% # with open("2021-07-05_10.nmea", "r", encoding="UTF-8", errors="", newline="" ) as f: # with open(inp_file, "r", encoding="UTF-8", errors="", newline="" ) as f: with open(inp_file, "r", encoding=enc_sty, errors="", newline="" ) as f: lst = csv.reader(f, delimiter=",") df = pd.DataFrame(lst).rename(columns={0:'ID'}) # df = pd.DataFrame(lst) #%% # df.query('cols in ["GG1","GG2"]') df.query('ID in ["GG1","GG2"]').to_csv(out_file,header=False, index=False) # df[df[0].str.contains(id)].to_csv(out_file,header=False, index=False) #%% df.query('ID in ["GG1","GG2"]') # !!!!FIN
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSLambdaのようなFaaS「PyFuncApi」を作った話

なぜ作ったのか 下記記事を書いていて、保存機能さえあれば AWSLambdaやAzureFunctionsのようなFaaS(Function as a Service)と 同じようなものが出来そうだと思ったからです 技術スタック 言語 python フレームワーク django データベース Firebase RealtimeDatabase コード 整理したら・要望があれば Githubに上げようと思います 使い方 コードを登録 Bodyにコード(Python)を書いて、下記URLにPOSTします 同URLのGETで登録した値を取得できます 実行してからの登録になるので 構文エラーが無ければ登録できます また、上書きされないように現状UPDATEは実装していません /v1 など付けてバージョンを管理したほうが良いかもしれません ※こういうのが出来ますよ!という紹介なので 機密なコードやパスワードなどは入力しないでくださいね! https://pyfuncapi.herokuapp.com/admin/api/{一意のURL} 「exec_input」変数が外部からの値入力 「exec_result」変数がWebApi実行時の戻り値 となります 例: WebApiを実行 https://pyfuncapi.herokuapp.com/api/exec/{登録したURL} bodyに入力した値がexec_inputに入ります サンプルコード/サンプルAPI フィボナッチ数列かどうかを判定するWebApi bodyに数値を入力してPOSTします https://pyfuncapi.herokuapp.com/api/exec/is-fibonacci コード is-fibonacci.py def fib(n): if n > 1: return fib(n-1) + fib(n-2) return n def main(exec_input): if exec_input == '': return num = int(exec_input) i = 0 while True: i +=1 fib_num = fib(i) if fib_num == num: return True if fib_num > num: return False exec_result = main(exec_input) 人口動態調査 / 人口動態統計 月報(概数)WebApi 政府が公開しているオープンデータ(CSVファイル)をjsonに変換して返すWebApiです https://pyfuncapi.herokuapp.com/api/exec/vital-statistics-survey-monthly コード monthly-traffic-accident.py import csv import json import requests import numpy # https://www.e-stat.go.jp/stat-search/files?page=1&layout=dataset&toukei=00450011&cycle=1&stat_infid=000032115375&file_type=1&metadata=1&data=1 # 人口動態調査 / 人口動態統計 月報(概数) # def main(): datas = requests.get( "https://www.e-stat.go.jp/stat-search/file-download?statInfId=000032115375&fileKind=1") datas.encoding = "shift-jis" text = "" for line in numpy.array([s.split(',') for s in datas.text.splitlines()]).T: text += ",".join(line) + "\n" rows = [] lines = text.splitlines() reader = csv.DictReader(lines[1:len(lines)], delimiter=',') for row in reader: rows.append(row) return ''.join(json.dumps(rows, ensure_ascii=False).split()) exec_result = main() この記事を書いていて思ったこと Knativeという超強いものがあったのを知りました・・・。 課題 セキュリティーがガバガバです URLさえ知っていればコードが取得できてしまいます (認証作れば良いんでしょうけど、今回はそこまで作り込みません・・・。) あと、HeaderやステータスコードなどResponse自体を戻り値設定できるようにしたほうが良いなぁと思いつつ レスポンスの値を作るのがめんどくさくなるのでBodyのみ指定できるようにしてあります
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSLambdaやAzureFunctionsのようなFaaS「PyFuncApi」を作った話

なぜ作ったのか 下記記事を書いていて、保存機能さえあれば AWSLambdaやAzureFunctionsのようなFaaS(Function as a Service)と 同じようなものが出来そうだと思ったからです 技術スタック 言語 python フレームワーク django データベース Firebase RealtimeDatabase コード 整理したら・要望があれば Githubに上げようと思います 使い方 コードを登録 Bodyにコード(Python)を書いて、下記URLにPOSTします 同URLのGETで登録した値を取得できます 実行してからの登録になるので 構文エラーが無ければ登録できます また、上書きされないように現状UPDATEは実装していません /v1 など付けてバージョンを管理したほうが良いかもしれません ※こういうのが出来ますよ!という紹介なので 機密なコードやパスワードなどは入力しないでくださいね! https://pyfuncapi.herokuapp.com/admin/api/{一意のURL} 「exec_input」変数が外部からの値入力 「exec_result」変数がWebApi実行時の戻り値 となります 例: WebApiを実行 https://pyfuncapi.herokuapp.com/api/exec/{登録したURL} bodyに入力した値がexec_inputに入ります サンプルコード/サンプルAPI フィボナッチ数列かどうかを判定するWebApi bodyに数値を入力してPOSTします https://pyfuncapi.herokuapp.com/api/exec/is-fibonacci コード is-fibonacci.py def fib(n): if n > 1: return fib(n-1) + fib(n-2) return n def main(exec_input): if exec_input == '': return num = int(exec_input) i = 0 while True: i +=1 fib_num = fib(i) if fib_num == num: return True if fib_num > num: return False exec_result = main(exec_input) 人口動態調査 / 人口動態統計 月報(概数)WebApi 政府が公開しているオープンデータ(CSVファイル)をjsonに変換して返すWebApiです https://pyfuncapi.herokuapp.com/api/exec/monthly-traffic-accident コード monthly-traffic-accident.py import csv import json import requests import numpy # https://www.e-stat.go.jp/stat-search/files?page=1&layout=dataset&toukei=00450011&cycle=1&stat_infid=000032115375&file_type=1&metadata=1&data=1 # 人口動態調査 / 人口動態統計 月報(概数) # def main(): datas = requests.get( "https://www.e-stat.go.jp/stat-search/file-download?statInfId=000032115375&fileKind=1") datas.encoding = "shift-jis" text = "" for line in numpy.array([s.split(',') for s in datas.text.splitlines()]).T: text += ",".join(line) + "\n" rows = [] lines = text.splitlines() reader = csv.DictReader(lines[1:len(lines)], delimiter=',') for row in reader: rows.append(row) return ''.join(json.dumps(rows, ensure_ascii=False).split()) exec_result = main() この記事を書いていて思ったこと Knativeという超強いものがあったのを知りました・・・。 課題 セキュリティーがガバガバです URLさえ知っていればコードが取得できてしまいます (認証作れば良いんでしょうけど、今回はそこまで作り込みません・・・。) あと、HeaderやステータスコードなどResponse自体を戻り値設定できるようにしたほうが良いなぁと思いつつ レスポンスの値を作るのがめんどくさくなるのでBodyのみ指定できるようにしてあります
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【勉強記録】Pythonのif文で「あなたへのオススメ」を表示する

目次 概要 成果 作成したコード 振り返り 2021/9/10: コメント欄にてリファクタリングの例示を2件も頂いております。 ぜひご覧ください。 概要 本記事はPython学習のアウトプットです。 GitHubでリポジトリを作るほどのものでもなかったのでこちらをお借りして記録に残そうと思います。 質問に対してYesかNoで答えていき、「あなたへのオススメを表示する」という簡単なプログラムを組んでみました。 題材はミュージカルですが、作品名は記載しておりません。 成果 作成したコード 同じ形式が続きます if.py musical1 = "snow" musical2 = "ramp" musical3 = "king" musical4 = "opera" musical5 = "robot" musical6 = "friends" musical7 = "tree" recommend_word = "オススメは…… " my_name = "my_name" print("オススメのミュージカルを紹介します。質問にyかnでお答えください。") answer = input("初めての観劇だ y/n") if answer == "y": answer = input("ディズニーと聞いたらヨダレが出る y/n") if answer == "y": answer = input("流行りにはのっておきたい y/n") if answer == "y": print(f"{recommend_word}{musical1}") else: answer = input("ドラマチックな人生に憧れる y/n") if answer == "y": print(f"{recommend_word}{musical2}") else: print(f"{recommend_word}{musical3}") else: answer = input("ちょっぴり怖くても大丈夫 y/n") if answer == "y": print(f"{recommend_word}{musical4}") else: answer = input(f"{my_name}のイチオシが気になる y/n") if answer == "y": print(f"{recommend_word}{musical5}") else: print(f"{recommend_word}{musical6}") else: # 以下省略 振り返り ※ 何かアウトプットしたいなと思っていたところif文の勉強をしましたので作ってみました。 今まで保守性の高いコードを書けていなかったという自覚があるので、少しでも意識できたコードが書けていればと思います。 単純なコードではありますが、 musical1、musical2…より良い命名 yかn以外を入力した場合の処理の記述 それを毎回差し込むのか 分岐した先が重なるような記述が書けないか など、課題がいくつも出てきました。 追記できるものがあれば追記していきたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「”すぐ” このデータまとめて欲しい」に ”すぐ”に超簡単に応えられる Python の・・・

~手元データの要約、視覚化を超簡単に実現して、探索的データ解析を行おう~ はじめに   「おい、悪いが”すぐに”このデータまとめてくれ」   緊急!となれば、そりゃ”すぐに”取りかかるが、”すぐに”まとめることができるかは別の話・・・これはデータ項目が多いときほど悩ましいものです。 じつは、Pythonには、このようなデータセットでも”すぐに”まとめることができる pandas-profiling ってのがある。 「Pythonで相関係数行列グラフとか描けるのは知ってるけど、チマチマやってる時間ないんです」 といった方にとっても、まとめを頼んだ上司にとっても、まちがいなく喜んでくれるはず。 この記事は、私の備忘含め、pandas-profiling が誰でも簡単に実行できるように記録するものです。   実行条件など ・Google colabで実行 ・ボストン住宅価格のデータセットで実行 ※手元データを読込んで実行する場合も記載していますので、簡単にできるはずです。       ボストン住宅価格のデータセットについて 以下サイト(Kaggle)の「Boston.csv」を使わせていただいた。 データ数:506, 項目数:14のデータセットで、住宅価格を示す「MEDV」という項目と、住宅価格に関連するであろう項目が「CRIM:犯罪率」「RM:部屋数」「B:町の黒人割合」「RAD:高速のアクセス性」・・・等、13項目で構成されたデータとなっています。 これだけ項目があると、データ傾向を掴むだけでも、なかなか骨が折れるだろうと想像できますね。 ボストン住宅価格データの項目と内容 項目 内容 CRIM 町ごとの一人当たり犯罪率 ZN 25,000平方フィート以上の住宅地の割合 INDUS 町ごとの非小売業の面積の割合 CHAS チャールズ川のダミー変数(川に接している場合は1、そうでない場合は0) NOX 窒素酸化物濃度(1,000万分の1) RM 1住戸あたりの平均部屋数 AGE 1940年以前に建てられた持ち家の割合 DIS ボストンの5つの雇用中心地までの距離の加重平均 RAD 高速道路(放射状)へのアクセス性を示す指標 TAX 10,000ドルあたりの固定資産税の税率 PTRATIO 町ごとの生徒数と教師数の比率 B 町ごとの黒人の割合 LSTAT 人口の下層階級の比率 MEDV 住宅価格の中央値(1000㌦単位)    プロファイリング(pandas-profiling)してみよう! ライブラリのインストールおよびインポート ライブラリのインストールおよびインポート !pip install git+https://github.com/pandas-profiling/pandas-profiling.git !pip show pandas_profiling import pandas as pd import warnings from pandas_profiling import ProfileReport from pandas_profiling.utils.cache import cache_file warnings.filterwarnings('ignore') データの読み込み ※手元データを読込む場合は、読み込むファイル名を変更してください。 ※Excelファイルを読込む場合は、pd.read_excel('ファイル名.xlsx')となります。 df = pd.read_csv("Boston.csv",index_col=0) profile = ProfileReport(df) **プロファイルを出力 profile :結果をHTML出力(ファイル名は任意) profile.to_file("Profile‗result.html") **出力イメージ プロファイルから読み取った概要に、HTML出力結果にをアタッチして報告すれば、きっと喜んでいただけるでしょう。 最後に Pythonには「こんなことまでできるのか」と驚かされることがいろいろある。 このpandas-profiling もそのひとつ。使わない手はない。   参考サイト   
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

EC2の起動直後のCloudWatchアラームを抑止する

起動直後のCloudWatchアラームの通知を抑止したいという要望があったので、AWSサービスを組み合わせて実現してみました。 構成 EventBridge + Lambdaで実現します。 EC2起動直後の一定時間を待つために、起動時にはStep FunctionsからLambdaを呼び出します。 EventBridgeやStep Functionsは応用の幅が広いので、その一例としても役立ちそうです。 Lambdaコード まずはLambdaコードから見ていきます。 import boto3 target = { "test-instance" : ['test-alarm'] } def lambda_handler(event, context): client = boto3.client('cloudwatch') if event["Action"] == 'enable': response = client.enable_alarm_actions( AlarmNames=target[event["Target"]] ) if event["Action"] == 'disable': response = client.disable_alarm_actions( AlarmNames=target[event["Target"]] ) EventBridgeからキーバリューを受け取ることを前提としたコードです。 Actionキーでアラームの有効or無効を切り替えます。TargetキーでEC2を識別します。ここではインスタンス名(test-instance)とします。あらかじめインスタンス名に対応したアラーム名(test-alarm)を辞書型のtargetで定義しておきます。 これにより、EventBridgeで指定したインスタンスに対応するアラームの有効化or無効化を実行することができます。 IAMポリシー Lambda用のIAMポリシーです。 { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogStream", "cloudwatch:EnableAlarmActions", "cloudwatch:DisableAlarmActions", "logs:CreateLogGroup", "logs:PutLogEvents" ], "Resource": "*" } ] } 標準で付与されるログ周りを除くと、EnableAlarmActionsとDisableAlarmActionsが必要です。 これらのアクションはAWSコンソールからは実行できず、LambdaやCLIで実行する必要があるようです。 Step Functionsステートマシン EC2起動時に実行するステートマシンを定義します。 { "StartAt": "Wait", "States": { "Wait": { "Type": "Wait", "Seconds": 60, "Next": "MainTask" }, "MainTask": { "Type": "Task", "Resource": "arn:aws:lambda:リージョン名:アカウントID:function:AlarmSwitch", "End": true } } } リージョン名とアカウントIDは置き換えてください。ステートマシン内で前述のLambdaを呼び出します。ここでは実行までに1分間待つことにします。 IAMポリシー ステートマシン用のIAMポリシーです。 { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "lambda:InvokeFunction" ], "Resource": [ "arn:aws:lambda:リージョン名:アカウントID:function:AlarmSwitch:*" ] }, { "Effect": "Allow", "Action": [ "lambda:InvokeFunction" ], "Resource": [ "arn:aws:lambda:リージョン名:アカウントID:function:AlarmSwitch" ] }, { "Effect": "Allow", "Action": [ "xray:PutTraceSegments", "xray:PutTelemetryRecords", "xray:GetSamplingRules", "xray:GetSamplingTargets" ], "Resource": [ "*" ] } ] } リージョン名とアカウントIDは置き換えてください。xray周りは標準で付与されるものです。前述のLambdaの実行権限を付与しておきます。 EventBridge あとはEventBridgeで対象のEC2の起動停止をトリガーにして、先ほどのLambdaとステートマシンを実行します。 EC2起動 イベントパターン { "source": ["aws.ec2"], "detail-type": ["EC2 Instance State-change Notification"], "detail": { "state": ["running"], "instance-id": ["インスタンスID"] } } インスタンスIDは置き換えてください。 ターゲット 先ほどのステートマシンを選択します。Lambdaに引き渡すキーバリューをJSONで指定します。 IAMポリシー EC2起動EventBridge用のIAMポリシーです。 { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "states:StartExecution" ], "Resource": [ "arn:aws:states:リージョン名:アカウントID:stateMachine:AlarmSwitchStateMachine" ] } ] } リージョン名とアカウントIDは置き換えてください。先ほどのステートマシンの実行権限が必要です。 EC2停止 イベントパターン { "source": ["aws.ec2"], "detail-type": ["EC2 Instance State-change Notification"], "detail": { "state": ["stopped"], "instance-id": ["インスタンスID"] } } ターゲット 停止時は待機しないので、直接Lambdaを呼び出します。AWSコンソールで作成すれば、実行権限はLambdaパーミッションで自動的に付与されます。CloudFormationで作成する際はLambdaパーミッションの設定に注意です。キーバリューもEC2起動と同じ形式で入力します。 実行結果 準備が整ったら、対象のEC2インスタンスを起動および停止します。 停止すると、インスタンスに対応したアラームが無効になります。 起動すると、ステートマシンが実行され、指定した時間(1分間)待機した後、Lambdaを実行してアラームを有効化します。 これにより、起動後の任意の一定時間、CloudWatchアラームを無効化することができるので、アラームの抑止を実現できました。 今回は停止と起動のセットで動作する構成になりますが、再起動時にも抑止したい場合を考えると、起動時のみをトリガーとしてアラームの有効無効を切り替えるようにするなどの工夫が必要そうです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ラズパイで冷蔵庫のプリンを監視する

皆さんは後で食べようと冷蔵庫に取っておいたプリンが食べられていたことはありますか? 私はあります。 自分へのご褒美にと楽しみにしていたところへその仕打ち、 ショックは計り知れないですよね。 プリンに名前を書いておくような生半可な対策ではなく、 また同じ悲劇が起こることを防ぐシステムを考案しましたので、 その実現までの記録を残したいと思います。 前回のラズパイ記事ではカメラを使ってタイムラプスを作る流れを紹介しましたが、 今度はそれに加えてセンサーを使ってみることにしました。 システム構成イメージ ドアが開けられたことを検知して写真を撮影、冷蔵庫から何を取り出したかを記録。 その写真をLINEで自分に送ることでリアルタイムで事態を把握できるようにします。 「これではプリンが食べられること自体は防げないのでは?」と思われるかもしれませんが、 監視されているという事実を抑止力にプリンを守ろうと仕組みを描いています。 冷蔵庫の開閉検知にはドアセンサーを使用します。 片方のパーツが磁石になっていて、センサーと近づけることで回路が繋がり、離すと回路が切れる仕組みです。 LINEへの画像送信にはLINE Notifyを利用します。 自分のLINEアカウントでログインするだけで簡単に使い始めることでき、 何か通知を受け取る仕組みを構築するのに便利です。 プログラミングはPython(今回は3.5.3)にて行います。 では順を追って、ひとつずつ検証していきたいと思います。 検証 1. ドアの開閉を検知する ラズパイのGPIOにドアセンサーを接続します。 センサーはそのままだと接続できず、はんだ付けが必要ですが、 今回は検証であるため絶縁テープでジャンパ線と繋ぎました。 ピンは好きなGPIOピンとGNDに接続します。 (写真ではGPIO17とその隣のGNDを使っています) センサーを検証するためのPythonソースコードは以下のようにしました。 test_door_sensor.py import RPi.GPIO as GPIO from time import sleep # GPIOピン番号の指定方法を設定 GPIO.setmode(GPIO.BCM) # GPIO17を入力モード、プルアップ抵抗を有効化 # 入力がないときにONとなる GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_UP) status = 1 while True: try: status = GPIO.input(17) if status == 0: print("close") else: print("open") sleep(1) except: break GPIO.cleanup() コードを実行しセンサーを近づけたり遠ざけたりすると、 出力される判定結果が変わることが確認できました。 これでセンサー部の検証はOKです。 2. カメラで写真を撮影する 今回はPythonでカメラモジュールを操作し写真を撮影します。 カメラモジュールのPythonインターフェースであるPicameraを使います。 test_camera.py from picamera import PiCamera camera = PiCamera() camera.capture("photo.jpg") ちょっと近いのでボケてますが、撮れていますね! 3. LINEに画像を送信しスマホで受け取る LINE Notifyにて、自分のLINEアカウントでログインをします。 初めてログインする場合は認証コードの入力が求められるので、スマホのLINEアプリで入力しましょう。 ログイン後のマイページで「トークンを発行する」からアクセストークンの発行ができます。 トークン名は通知メッセージに含まれて表示されるので、 それを踏まえて設定しましょう。 取得したアクセストークンを使って、 PythonソースコードからLINE NotifyのAPIをリクエストします。 test_line_notify.py import requests # LINE Notifyアクセストークン token = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" # LINE Notify通知API url = "https://notify-api.line.me/api/notify" headers = {"Authorization": "Bearer " + token} payload = {"message": "Hello!"} file_name = "photo.jpg" files = {"imageFile": open(file_name, "rb")} requests.post(url, headers=headers, data=payload, files=files) 実行すると画像付きでメッセージが送信されました。 システムの動作確認 検証は完了したのでそれぞれの検証ソースコードをひとつにまとめ、動作確認します。 単純なマージ結果ですが以下のようになりました。 monitoring_pudding.py import RPi.GPIO as GPIO import requests from time import sleep from picamera import PiCamera from time import sleep GPIO.setmode(GPIO.BCM) GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_UP) camera = PiCamera() token = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" url = "https://notify-api.line.me/api/notify" headers = {"Authorization": "Bearer " + token} payload = {"message": "冷蔵庫が開けられました"} file_name = "photo.jpg" status = 1 while True: try: status = GPIO.input(17) if status == 0: print("close") else: print("open") camera.capture(file_name) files = {"imageFile": open(file_name, "rb")} requests.post(url, headers=headers, data=payload, files=files) print("sent message") sleep(3) except: break GPIO.cleanup() 監視システムを設置した様子です。 不格好ですが最終的にはこのような形になりました。。 冷蔵庫を漁る手元を含んだ全身を写すにはある程度の高さが必要で、調整に苦労しました。 あとはPythonコードを実行、監視状態し準備完了です。 プリンを取り出す様子をシミュレーションしてみました。 バッチリ写ってますね! これで安心してプリンを置いておけそうです。  課題 冷蔵庫が空いている間、複数のLINEメッセージが届いてしまうため煩わしい 部屋が暗いと真っ黒な画像になる 赤外線LED付きカメラで暗視対応できそう カメラの設置場所と手元を撮りたい都合で顔までは入らなかった 魚眼レンズ搭載のカメラを使用すればよさそう まとめ 今回は冷蔵庫のプリンを監視すると題しましたが、別に冷蔵庫やプリンのみにとどまらず、 出入り口の監視などの様々な用途に応用できると思います。 初めてラズパイとセンサーを組み合わせてシステムを構築してみたのですが、 触って動作を検証できただけで新しい世界が広がった感覚がありました。 また新しいアイデアが生まれたら、挑戦して記事にしてみたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

sympy: Symbol( ), symbols( ), var( )使い分け

混乱 sympyの使い方を確認しようと検索して出てくるwebページを見ると、変数シンボルの定義に、Symbol( )を使っているもの、symbols( )を使っているもの、var( ) を使っているものなどがあり、どれを使うのが適当なのかよく分からない。 よく理解していないものだから、Symbol( )で複数のシンボルを作ろうとして、エラーになる。 なので、公式ドキュメントを読んで確認したので、その結果を下にまとめる。 Symbol( ) Symbol( )は、Symbol ClassのオブジェクトのConstractor。 なので作れるシンボルオブジェクトは1度の呼び出しで1つ。 # 例 x = Symbol('x') symbols( ) symbols()は引数で与えられた文字列を解釈して、指定された名前を持つSymbolオブジェクトを返す。 返り値を変数で受け取らないと、そのオブジェクトは消えてしまう。 # 例 x, y = symbols('x, y') # 区切りはスペースでも可 便利なシンボルの生成方法 symbols( )は、引数で与える文字列によって複数のシンボルを生成する便利な使用方法がある。 以下の例では、返り値を受け取っている変数名は、生成されているシンボルの名前を示す。 x0, x1, x2 = symbols('x:3') x50, x51 = symbols('x5(:2)') a, b, c = symbols('a:d') [a, b] = symbols(['a', 'b']) var( ) var( )は、引数で与えられたものと同じ引数でsymbols( )を呼び出し、作成されるシンボルオブジェクトを同じ名前の変数として、global name spaceに格納する。 なので、var( )の返り値は変数で受ける必要がない。 var( )使用上の注意 生成されるオブジェクトがglobal name spaceに格納されるゆえ、関数内でvar( )を使うと、変数名の衝突などの問題を起こすゆえ不適当。関数内ではsymbols( )を使う方が良い。 (逆に言えば、トップレベルではvar( )を積極的に使うと良い。)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】10進数を2進数に変えるプログラムを作ってみた!

 Pythonを学び始めたので、とりあえずできそうなプログラムを手当たり次第作っています。今回は10進数を2進数に変えるプログラムを原始的に作ってみました。 def base_number(num): remainders = [] while True: remainder = num % 2 remainders.append(str(remainder)) num //= 2 if num == 0: remainders.reverse() binary = "".join(remainders) print("2進数は" + binary + "です") break num = int(input("数字を入力してください:")) base_number(num) 出力結果は以下のようになります。 数字を入力してください:160 2進数は10100000です  もっと簡単に2進数を出せる数学の公式があれば、より簡潔なコードが書けると思いますし、Pythonにも2進数を簡単に出しやすいメソッドがあるかもしれませんが、アルゴリズムを考える勉強のため、まずは自分なりに考えてみました。  結果的に「reverse」や「join」といったメソッドを使ってしまったため、これを使わない方法も考えてみたいと思います。  またこの2進数の値をInteger型にしようとすると、「00000101」のような場合は「101」になってしまいますし、String型で仕方ないのかなと思っています。  ご意見いただけると勉強になります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Lambdaで独自にログを出力したい(標準のログを出したくない)

問題 Lambda標準のログを抑止してCloudWatchLogsのコストを節約したい。けど、特定の場合にはログを出力したいといった問題を解決します。 Lambdaコード 早速ですが、このような関数を用意してlambda_handler内で使用します。 必要な権限(IAMポリシー)は後で解説します。標準のログを出さないようにするのも、IAMポリシーの定義で行います。 import boto3 import time import datetime logs_client = boto3.client('logs') lgn = '/aws/lambda/PutLog' def lambda_handler(event, context): put_log('test') put_log('テスト') def put_log(message): # ロググループがない場合作成 res = logs_client.describe_log_groups( logGroupNamePrefix=lgn ) if not res['logGroups']: res = logs_client.create_log_group( logGroupName=lgn ) # putするログストリームを取得 lsn = datetime.datetime.now().strftime("%Y/%m/%d") + '[PutLog]' res = logs_client.describe_log_streams( logGroupName=lgn, logStreamNamePrefix=lsn, ) # ログストリームがない場合作成し、取得 if not res['logStreams']: res = logs_client.create_log_stream( logGroupName=lgn, logStreamName=lsn ) res = logs_client.describe_log_streams( logGroupName=lgn, logStreamNamePrefix=lsn, ) # シーケンストークンがある場合指定する if 'uploadSequenceToken' in res['logStreams'][0]: seq_token = res['logStreams'][0]['uploadSequenceToken'] res = logs_client.put_log_events( logGroupName=lgn, logStreamName=lsn, logEvents=[ { 'timestamp': int(time.time()) * 1000, 'message': message }, ], sequenceToken=seq_token ) # ない場合指定しない else: res = logs_client.put_log_events( logGroupName=lgn, logStreamName=lsn, logEvents=[ { 'timestamp': int(time.time()) * 1000, 'message': message }, ] ) コード解説 コードはこちらの記事を参考にしました。 Lambda標準のログと同じような感覚で使いたかったので、ロググループ作成とログストリーム作成を追加しました。これにより、あらかじめそれらを作成する必要がなくなります。 Lambda内の変数lgnでロググループ名の指定と、変数lsnでログストリーム名の指定をします。ここでは、ロググループ名は標準のログと同じ、ログストリーム名は日付+[PutLog]にしています。ログストリーム名を調整すれば、任意の単位でまとめることができると思います。 そして、少し理解が難しいのがシーケンストークンの指定です。参考記事でも触れられていますが、2回目以降同じログストリームに書き込むためには、シーケンストークンの指定が必要です。 IAMポリシー { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:DescribeLogGroups", "logs:DescribeLogStreams" ], "Resource": "arn:aws:logs:リージョン名:アカウントID:*" }, { "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": [ "arn:aws:logs:リージョン名:アカウントID:log-group:/aws/lambda/PutLog:log-stream:*[PutLog]" ] } ] } ポリシー解説 ポリシーの内容も前述の記事を参考にしています。リージョン名とアカウントIDは置き換えてください。 CreateLogStreamおよびPutLogEventsを独自に指定することで、標準のログが出力されるのを回避しています。 ここでは、関数内で指定している日付+[PutLog]に対応させるために*[PutLog]で、ワイルドカードの指定をしています。 実行結果 通常であればこのような文字列が付与されたログストリームが作成され、開始終了のログやprint等で指定した文字列が出力されます。前述のポリシーを定義することで、このような標準のログ出力を抑止します。 こちらがテスト結果です。print等の代わりに、作成したput_log関数で任意の文字列を指定します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

tqdmのインポートミス

はじめに for文などで重めの処理や、Pytorchでの学習などの進捗管理のために使うことも多いtqdm。 importするときによくミスるのでメモ。 Pytorchでの学習を回す時を例に挙げます。 環境 python 3.9.5 macOS 12.0 beta ダメな例 import tqdm for inputs, labels in tqdm(train_dataloader): # ---以下学習処理---- これだと'module' object is not callableとエラーが出る。モジュールは関数として呼べないよって意味。 良い例 ① from tqdm import tqdm for inputs, labels in tqdm(train_dataloader): # ---以下学習処理---- ② import tqdm for inputs, labels in tqdm.tqdm(train_dataloader): # ---以下学習処理---- これでうまくいった。 まとめ モジュールと関数の違いをちゃんと理解する。 でもモジュール名と関数名が一緒なのややこしい...。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

画像解析実践入門_指紋の稜線分析 2(スケルトン化とラベリング)

の続きです。 概要  指紋の稜線を分析します。前回の記事で画像の二値化までしました。 最終的にはこの二値指紋画像をサブ領域に分割して、そこから特徴量(今回は稜線角度)を抽出。 指紋認識が可能か検討してみます。 もくじ 1.二値画像のスケルトン(細線)化処理 2.画像をサブ領域に分割 3.サブ領域ごとに稜線をラベリング 1.二値画像のスケルトン(細線)化処理 前回の記事で指紋画像を二値化しました この二値画像から稜線の情報を解析していきたいのですが、その際に白の線(解析対象)の持つ「線幅」は正直余計な情報と言えます。 この指紋の解析において意味を持つ情報は連結成分の中心線つまり解析対象の「骨格」になります。 そういうう場合はオブジェクトの連結情報を崩さずに境界を侵食していく処理,スケルトン化,細線化 がおすすめです。 スケルトン化について https://scikit-image.org/docs/dev/auto_examples/edges/plot_skeleton.html ではスケルトン化処理を二値画像に適応してみましょう。 skeletonize.py from skimage.morphology import skeletonize #画像のスケルトン化 skelton_img = skeletonize(bin_locla) plt.imshow(skelton_img,cmap='gray') skelton_img.shape #(90,90) スケルトン化できました。次は画像を解析エリアごとに分割して行きます。 2.画像をサブ領域に分割 画像を分割して、その領域ごとに特徴量を計算します。今回は5*5に分割します。 crop.py #画像のサイズと分割時のサイズを計算する H,V=skelton_img.shape #5*5の領域に分割します crop_num =5 #クロップ領域の画像の縦横を計算します。 h_crop=int(skelton_img.shape[0]/crop_num) v_crop=int(skelton_img.shape[1]/crop_num) print(H,V,h_crop,v_crop) #90,90,18,18 #分割してリストで保存 croped_imgs = [skelton_img[h_crop * x:h_crop * (x+1), v_crop * y:v_crop * (y+1)] for x in range(crop_num) for y in range(crop_num)] 参考にした記事 https://tomomai.com/python-opencv-numpy/ 分割した画像はリスト配列で保存したのでfor文で取り出してプロットして見ましょう。 plt_crop_img.py plt.figure(figsize=(10,10)) plt.subplots_adjust(wspace=0.1, hspace=0.1) #画像を取り出して表示 for i in range(len(croped_imgs)): ax = plt.subplot(crop_num,crop_num,i+1) plt.imshow(croped_imgs[i],cmap='gray') plt.axis('off') 3.サブ領域ごとに稜線をラベリング 稜線のラベリング処理を行います。二値画像の中に存在する連結成分にラベル付を行うことで稜線同士を比較することが可能となります。 ラベリング処理関数について https://scikit-image.org/docs/dev/api/skimage.measure.html#skimage.measure.label lable.py #サブ領域ごとにラベリング from skimage.measure import label labeld_imgs = [label(croped_imgs[i]) for i in range(len(croped_imgs))] #ラベリング結果を表示 plt.figure(figsize=(10,10)) plt.subplots_adjust(wspace=0.1, hspace=0.1) for i in range(len(labeld_imgs)): ax = plt.subplot(crop_num,crop_num,i+1) plt.imshow(labeld_imgs[i]) plt.axis('off') ラベリングが完了しました。 右下5行5列目などが誤連結でラベル数少ないですが、とりあえず良しとしましょう。 一応ラベリングされた画像の画素を直接見てみましょう。 num.py labeld_imgs[2]#1行目3列目の画像 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0], [2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3], [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0], [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0], [1, 1, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4], [0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0], [3, 3, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5], [4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6], [5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 0, 0, 0, 0, 0, 0], 連結している画素が同じラベル値として1~6ラベルが含まれていることがわかります。 まとめ 今回は二値画像から細線化処理、ラベリングまでを行いました。 次回からこのラベリング画像を用いて稜線間隔と稜線方位を解析します。 これまではskimageのモジュールを利用してかなり楽をしています。 次回からアルゴリズムに基づいて本格的に解析用のコードを記述していきますのでお楽しみに。 ちなみに参考としている書籍「MATLAB画像処理入門」と同著者,同内容、の論文が 高井 信勝: 稜線方位分布による指紋のコード化 [http://hokuga.hgu.jp/dspace/bitstream/123456789/2093/1/%E8%AB%96%E6%96%8713.pdf] に掲載されています。ご参考までに。 続く
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ディクショナリ型のメソッドまとめ (Python ver.)

このページについて ディクショナリ型のメソッドをまとめたページです 必要に応じて、順次更新 ■ dict.get() 指定したキーに対応する値を取り出す get_example.py animals = {'1':'dog','2':'cat','3':'rabbit'} print(animals['2']) #cat ■ dict.items() →キーと値を取り出す items_example.py score = {'English':70, 'Math':65, 'Japanese':85} for key, value in score.items(): print(key, value) #English 70 #Math 65 #Japanese 85 ■ dict.keys() →キーの一覧を返す keys_example.py score = {'English':70, 'Math':65, 'Japanese':85} print(score.keys()) #dict_keys(['English', 'Math', 'Japanese']) for k in score.keys(): print(k) # English # Math # Japanese ■ dict.pop() →指定したキーと値を削除する pop_example.py fruits = {'apple':15, 'banana':5, 'orange':10} fruits.pop('banana') #5 print(fruits) #{'apple': 15, 'orange': 10} ■ dict.setdefault() →値を取り出す setdefault_example.py fruits = {'apple':15, 'banana':5, 'orange':10} fruits.setdefault('grape':20) print(fruits) #{'apple':15, 'banana':5, 'orange':10, 'grape':20} ■ dict.values() →辞書の値の一覧を返す values_exmaple.py score = {'English':70, 'Math':65, 'Japanese':85} print(score.values()) #dict_values([70, 65, 85]) for k in score.values(): print(k) #70 #65 #85
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

競プロ典型90問 002 - Encyclopedia of Parentheses

問題 競プロ典型90問 002 Encyclopedia of Parentheses 解法 以下の2つを主に考えてコードを書きました。 再起関数を使い長さNになるまで文字を追加していく。 正しいカッコ列の条件を外れた場合、文字を追加しない。 正しいカッコ列の条件 まず、正しいカッコ列の条件を考えたところ、以下の2つになりました。 カッコ列を左から見て行った時に")"の数が"("を超えないこと。 カッコ列の"("の数と")"の数が等しいこと。 ここで長さNのカッコ列を全探索して、全てにおいて上記の判定をしても良いのですが、大変そうだなぁと思ったのでもう少し考えてみました。 2の条件から以下の2つがわかります。 Nが奇数の場合正しいカッコ列は存在しないこと。 カッコ列を左から見て行った時に"("の数がN//2を超えないこと。 ということで、空の文字列から1文字ずつ追加してカッコ列を作る際に、以下の2つを条件判定しながら追加していけば正しいカッコ列が作れるのではないかと思いました。 "("を追加する際は"("の数がN//2を超えないこと。 ")"を追加する際は")"の数が"("の数を超えないこと。 再起関数 上の条件がわかったところで再起関数を使えばあとは簡単にコードが書けるのではないかと思いました。 再起関数とは、「ある関数の中で自分自身を呼び出す関数」のことです。 例 例えば1からNまでを掛け算した結果を求めたい場合を考えます。 これは、1からN-1までをかけた結果にNを掛ければよいですね。 そして、1からN-1までを掛け算した結果を求めたい場合には1からN-2までを掛け算した結果にN-1をかければ良いです。 というふうにこれを1までやっていくのが再起関数の考え方です。 N = int(input()) def saiki(i): if i == 1: return 1 else: return saiki(i-1)*i print(saiki(N)) ソース 今回カッコ列の1つ目は必ず"("であるため、呼び出す際に引数に"("を渡しています。 また、最後は必ず")"であるため、作っている文字列の長さがN-1になった時点で最後に")"を追加して出力しています。 N = int(input()) if N%2 == 1: print() exit() def addChar(str,numL,numR): if numL+numR == N-1: print(str+")") return else: if numL < N/2: addChar(str+"(",numL+1,numR) if numL - numR >0: addChar(str+")",numL,numR+1) addChar("(",1,0) 最後に 公式の解法を見るとbit全探索で解けると書いてあり、全然違う方針でした。 bit全探索自体は調べてみてかなり有効な方法だと思ったのでまた勉強して、この問題もその通り書いてみようかなと思います。 今回は問題自体の難易度がそこまで高くなかったこともあり、プログラム自体は割とすんなりかけたと思います。(最初少し迷走して不安でしたが) それだけに今回は記事を書いている時間の方がかなり長くなってしまいました。 もう少し記事の方もすんなり書けるようになればと思っているのですが、こっちはプログラミングと違って考え方が分かればすんなり書けるようになると言ったものでもなくて難しいですね。「ここの記述はいらない」や逆に「ここをもう少し詳しく書いた方が良い」などあれば参考にさせていただきますのでコメントお願いします。 以上、拙い記事ではありますが何かの参考になれば幸いです。 最後まで読んでいただきありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

リスト型に利用できるメソッド (Python ver.)

このページについて ・リストに使えるPythonのメソッドをまとめたページ ・必要であれば順次更新 ■ list.append(x) →リストの末尾に要素を追加する append_examle.py fruits = ['apple','orange'] fruits.append('banana') print(fruits) #['apple','orange','banana'] リストを追加した時の挙動 append_example2.py fruits = ['apple','orange'] fruits.append(['banana','kiwi']) print(fruits) #['apple','orange',['banana','kiwi']] ■ list.count(x) →リストでのxの出現回数を返す count_example.py number = [2, 0, 1, 1, 8, 1, 9, 3, 6, 7, 5, 1] x = number.count(1) print(x) # 4 ■ list.extend(x) →全ての要素を対象のリストの末尾に追加 extend_example.py number = [1, 3, 5] number.extend([7,9,11]) print(number) #[1,3,5,7,9,11] ■ list.index(x[, start[,end]]) →リストの要素を検索する index_example.py country = ['Japan', 'America', 'Australia', 'Korea'] country.index('Japan') # 0 index_example2.py number = [0,1,2,4,1,1,0,2] number.index(0, 2, 7) # 6 ■ list.insert(i,x) →指定した位置(i)に要素(x)を挿入する insert_example.py color = ['Blue','Green','Red'] color.insert(2,'Yellow') print(color) #['Blue', 'Green', 'Yellow', 'Red'] ■ list.pop(i) →リストの中の指定された位置(i)にある要素をリストから削除する pop_example.py sushi = ['maguro', 'ika', 'sa-mon', 'ikura'] x = sushi.pop(0) print(x) #['ika','sa-mon','ikura'] ■ list.remove(x) →リストの中からxと同じ要素を取り除く remove_example.py animals = ['dog', 'cat', 'rabbit'] animals.remove('dog') print(animals) #['cat', 'rabbit'] 複数削除はしてくれないみたい remove_example2.py animals = ['dog', 'cat', 'rabbit', 'dog'] animals.remove('dog') print(animals) #['cat', 'rabbit', 'dog'] ■ list.reverse() →リストの並び順を反転する reverse_example.py alphabet = ['a','b','c','d','e'] alphabet.reverse() print(alphabet) # ['e','d','c','b','a']
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む