- 投稿日:2022-01-19T23:08:52+09:00
Google Cloud FunctionsでSeleniumがうまく動かなかったので、Phantom JS Cloudで代用した
Google Cloud Functionsでスクレイピングを自動化 ざっくり背景 もともとFXの自動取引で使用していたお名前VPSにPythonをインストールして、Windowsのタスクスケジューラでスクレイピングを自動化していましたが、 月々1,300円払ってスクレイピングするのも馬鹿らしくなったので、Google Cloud Functionsで動かすことにしました。 色々つまずきながらも、ほぼすべてのプログラムをGoogle Cloud Functionsに移管でき、ついに最後のプログラム。 最後のものが一番の難関でした・・・Selenium使っとる なぜSeleniumは面倒なのか Javascriptで生成された動的なページの場合、BeautifulSoupではソースコードをうまく取得することができないため、部分的にSeleniumを使っていました。 で、SeleniumでスクレイピングするためにはWebDriverなるものを動かさないといけない。 そのためにはWebDriverをCloud Functionsにデプロイしなければいけない。 デプロイしても権限?の問題でなぜか動かせない。 「Google Cloud Functions Selenium」 「Google Cloud Functions webdriver」 「GCF chromedriver」 とかとか、とにかく検索しまくって海外サイトも調べ漁っても答えが見つかりませんでした。 で、色々考えていたらPhantomJSのことを思い出しました。 PhantomJSCloud使えば一発だった サンプルコード 要はデプロイして手元で動かさずに、クラウドサービスに動かしてもらえばええやんって話。 sample.py payload = {'url':'https://google.com/','renderType':'HTML','outputAsJson':'true'} payload = json.dumps(payload) payload = urllib.parse.quote(payload, safe = '') phantomjs_key = 'API-KEY' url = 'https://phantomjscloud.com/api/browser/v2/' + phantomjs_key + '/?request=' + payload pj_res = requests.get(url) pj_res = pj_res.json() html = pj_res["content"]["data"] soup = BeautifulSoup(html, 'html.parser') PhantomJSCloudは無料プランだと1日500リクエストまで使えるので、基本はBeautifulSoupでスクレイピングして、JSで作られた動的なページだけPhantomJSCloudに任せればOK。 おまけ Google Cloud Functions × Seleniumに関して5時間くらい調べても動かすことができなかったのですが、PhantomJSCloudという方法に気づいてからは一瞬でした。 同じ悩みを抱えている人はPhantomJSCloud使いましょうw Selenium以外のCloud Functionsのつまずきは私のブログにまとめてます。
- 投稿日:2022-01-19T23:04:00+09:00
【非エンジニア向け】マイクラの自動釣りができないなら外部ツールをつくればいいじゃない
プログラマでなくても怠惰であれ 怠惰、短気、傲慢。これらはあまり聞こえのいい言葉ではありませんが、プログラマにおける3大美徳とされています。じゃあ非プログラマにとってはそうではないのでしょうか?人間は機械の力を借りて、楽に移動したり、モノを大量に作ったり、とにかく自動化して楽をしてきた生き物です。やらない理由はないでしょう! 今回の課題 マインクラフトをマルチでプレイしていたら自動釣りでお宝が釣れなくなっていたことがことの始まりです。 参考:https://minecraft.mixjuice.info/2020/08/23/fishing_open_water/ 簡単に言うと、上記リンクのこの画像の広さの水がないと宝が釣れないようにアプデされてしまったのです。 古いバージョンで動作していた釣りを行うための装置を設置すると、条件を満たさなくなりゴミばかり釣れてしまいます。今回はマイクラ内で行うことは諦め、画像認識の力でなんとかすることに決めました。 そもそも釣りってどういう仕様だっけ 右クリックで糸を垂らし、もう一度右クリックすると巻き上げます。魚が来ると浮きが沈むので、タイミングよく巻き上げる必要があります。(猶予は0.5秒?) 糸を垂らした状態 魚が来て浮きが沈んだ状態 完成したもの opencv-pythonのトラックバーを乱用し、最低限のパラメータ調整で浮きの赤色を固定位置で監視できるようにしています。 浮きが沈み、左側の赤色を抽出した画面で浮きが見えなくなるとpyautoguiが釣り竿を再度使用してくれます。(on/offを1にしているときのみ動作します。) 完成したコード 考えながら書いたので...が混ざっていますがいい味出しているのでそのまま掲載します。 import cv2 from PIL import ImageGrab import pyautogui as pag import numpy as np import time import threading class App: def __init__(self) -> None: self.resolution = pag.size() self.pos_x = 0 self.pos_y = 0 self.rectsize = 100 self.thresh = 128 self.running = 0 self.waiting = False ... def set_pos_y(self,e): self.pos_y = e def set_pos_x(self,e): self.pos_x = e def set_thresh(self,e): self.thresh = e def set_rectsize(self,e): self.rectsize = e if self.rectsize < 1: self.rectsize = 1 def set_running(self,e): self.running = e def run(self): cv2.namedWindow("window") cv2.createTrackbar("y", "window", 0, self.resolution[1], self.set_pos_y) cv2.createTrackbar("x", "window", 0, self.resolution[0], self.set_pos_x) cv2.createTrackbar("size", "window", 0, 255, self.set_rectsize) cv2.createTrackbar("threth", "window", 0, 255, self.set_thresh) cv2.createTrackbar("on/off", "window", 0, 1, self.set_running) while True: im = ImageGrab.grab() im = im.crop((self.pos_x-self.rectsize, self.pos_y-self.rectsize,self.pos_x+self.rectsize,self.pos_y+self.rectsize)) arr = np.asarray(im) arr = cv2.cvtColor(arr, cv2.COLOR_BGR2RGB) arr = cv2.resize(arr,(300,300)) mask = cv2.inRange(arr,np.array([0,0,128]),np.array([self.thresh,self.thresh,255])) red = cv2.bitwise_and(arr,arr,mask=mask) if red.sum()//255 == 0 and self.running and not self.waiting: def click(): self.waiting = True pag.rightClick() time.sleep(1) pag.rightClick() time.sleep(2) self.waiting = False th =threading.Thread(target=click) th.start() res = np.hstack([red, arr]) cv2.imshow("window",res) cv2.waitKey(100) app = App() app.run() 手抜きポイントその1 hstack res = np.hstack([red, arr]) しきい値を調整した結果が正しいか確認するために、処理後のredと処理前のarrを両方表示する必要がありました。しかし、windowを2つにすることは面倒極まりなかったので横に結合し解決しました。 手抜きポイントその2 トラックバー乱用 まぁ公式がボタンとして使えると言っているから多少はね... http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_gui/py_trackbar/py_trackbar.html#trackbar ポンポン置けるのでちょっとした自作ツールでは重宝します。ドラッグで浮きの位置を設定できるとベストなのですがドラッグ判定が地味に面倒なので自作ツールではバーを好んで使用しています。 まとめ 物体検出といった高度な技術を使用しなくても自動化はできる!ということが伝えたかったです。それだけです。
- 投稿日:2022-01-19T22:44:36+09:00
#1. pythonと機械学習 ~ 最小二乗法
はじめに こんにちは、この記事の著者はPC初心者がプログラミングの勉強のために書いたものとなっています。ご了承ください。今回は、単回帰分析において用いられている、最小二乗法について、pythonを用いて解説します。 線形単回帰 機械学習の最も基本的なモデルである「単回帰」では、1つの数値x(説明変数)から、1つの数値y(目的変数)を予測します。このとき、xの値からyの値を求めるには、 y = ax + b の式に代入すれば、yの値が算出できそうです。aとbの値を求めるために使うものが、「学習用データセット」となります。ここでは、 xが1のとき、yは10 xが3のとき、yは14 xが6のとき、yは20 を「学習用データセット」とします。xが7のときのyの値を予測します。まず、学習用データセットのxとyの値で散布図を描いてみます。 コード1 import matplotlib.pyplot as plt import numpy as np xdata = np.array([1, 3, 6]) ydata = np.array([10, 14, 20]) plt.scatter(xdata,ydata) plt.xlabel("x") plt.ylabel("y") plt.show() プロットした点と点は線で結べそうです。 結ぶことが出来ました。連立方程式を使えば、y = ax + bのaとbの値が算出できそうです。実際に求めてみるとa = 2, b = 8となります。これで、yの値の予測式がy = 2x + 8となることが分かりました。この式にxの値を代入すれば、yの値は22ということが分かりました。 上記のパターンでは、「学習用データセット」によってプロットした点で線を引くと、全ての点を通りましたが、現実はこう上手くはいきません。たとえばコード1のxdata, ydata部分を コード2 xdata = np.array([16, 76, 39, 53, 98]) ydata = np.array([58, 126, 90, 100, 139]) に、変えた場合で、散布図を描くと、 先ほどのように、全ての点を通るような一次関数は無さそうです。しかし、5つの点と近似するような一次関数の式は描けそうです。 最小二乗法 では、5つの点と近似するような一次関数の式を求めるにはどうすれば良いのか?答えは一次関数の式と点の距離が最小となるような式を探せばいいのです。予測式を y = ax + b 「学習用データセット」を下記のように文字で表します。 xdata = x_1, x_2, \cdots x_{i-1}, x_i\\ ydata = y_1, y_2, \cdots y_{i-1}, y_i 一番目の要素と予測式のy軸方向の距離は (ax_1 + b) - y_1 で求めることが出来ます。この値は負の値をとることもあるので、2乗して正の値にします。 (ax_1 + b - y_1)^2 残りの点でも同じことをすれば、データ点と予測式の距離を求めることが出来ます。今回は、全ての点と近似するような一次関数の式を求めることが目的なので、データ点と予測式の距離が最小値になれば良いです。 (ax_1 + b - y_1)^2 + (ax_2 + b - y_2)^2 + \cdots(ax_i + b - y_i)^2 上記の関数をL(a,b)として、総和記号Σを使って(nは要素数)、 L(a,b) = \sum_{i=1}^{n} (ax_i + b - y_i)^2\\ と、表すことができます。微分して値が0となる点では傾きが0なので、極値になります。L(a,b)のa^2とb^2の係数は必ず正の値になるので、L(a,b)のグラフは常に「下に凸」となります。よって、関数L(a,b)が最小値をとるようなa,bを探すには、(L(a,b)を微分した値) = 0 になれば良いわけです。関数L(a,b)をaまたはbで偏微分します。 \frac{∂L(a,b)}{∂a} = \sum_{i=1}^{n} (2x_i^2a + 2x_ib - 2x_iy_i) = 0 \cdots (1)\\ \frac{∂L(a,b)}{∂b} = \sum_{i=1}^{n} (2b + 2x_ia - 2y_i) = 0\cdots (2)\\ が、成り立てばよいです。次に、二つの式を変形します。まず(2)の式から、 \frac{∂L(a,b)}{∂b} = \sum_{i=1}^{n} (2b + 2x_ia - 2y_i) = 0\\ \sum_{i=1}^{n} (b + x_ia - y_i) = 0\\ \ -\sum_{i=1}^{n} y_i + a\sum_{i=1}^{n} x_i + bn = 0\\ \ -\frac{1}{n}\sum_{i=1}^{n} y_i + a\frac{1}{n}\sum_{i=1}^{n} x_i + b = 0\\ \ b = \frac{1}{n}\sum_{i=1}^{n} y_i - a\frac{1}{n}\sum_{i=1}^{n} x_i xdataとydataの平均値を \bar{x} = \frac{1}{n}\sum_{i=1}^{n} x_i\\ \bar{y} = \frac{1}{n}\sum_{i=1}^{n} y_i\\ とすると、 b = \bar{y} - a\bar{x} \cdots (3)\\ 次に(1)の式を変形します。 \frac{∂L(a,b)}{∂a} = \sum_{i=1}^{n} (2x_i^2a + 2x_ib - 2x_iy_i) = 0\\ \sum_{i=1}^{n} (x_i^2a + x_ib - x_iy_i) = 0\\ a\sum_{i=1}^{n} x_i^2 + b\sum_{i=1}^{n} x_i - \sum_{i=1}^{n} x_iy_i = 0 (3)の式を変形した式を代入します。 a\sum_{i=1}^{n} x_i^2 + (\bar{y} - a\bar{x})\sum_{i=1}^{n} x_i - \sum_{i=1}^{n} x_iy_i = 0\\ a\frac{1}{n}\sum_{i=1}^{n} x_i^2 + \bar{y}\frac{1}{n}\sum_{i=1}^{n} x_i - a\bar{x}\frac{1}{n}\sum_{i=1}^{n} x_i - \frac{1}{n}\sum_{i=1}^{n} x_iy_i = 0\\ a\frac{1}{n}\sum_{i=1}^{n} x_i^2 + \bar{x}\bar{y} - a\bar{x}^2 - \frac{1}{n}\sum_{i=1}^{n} x_iy_i = 0\\ a(\frac{1}{n}\sum_{i=1}^{n}x_i^2 - \bar{x}^2) = \frac{1}{n}\sum_{i=1}^{n} x_iy_i - \bar{x}\bar{y}\\ a(\frac{1}{n}\sum_{i=1}^{n}x_i^2 - 2\bar{x}^2 + \bar{x}^2) = \frac{1}{n}\sum_{i=1}^{n} x_iy_i - \bar{x}\bar{y}\\ a(\frac{1}{n}\sum_{i=1}^{n}x_i^2 -2a\bar{x}\frac{1}{n}\sum_{i=1}^{n} x_i + \bar{x}^2) = \frac{1}{n}\sum_{i=1}^{n} x_iy_i -2\bar{x}\bar{y} + \bar{x}\bar{y}\\ a\frac{1}{n}\sum_{i=1}^{n}(x_i - \bar{x})^2 = \frac{1}{n}\sum_{i=1}^{n}((x_i - \bar{x})(y_i - \bar{y}))\\ a = \frac{\frac{1}{n}\sum_{i=1}^{n}((x_i - \bar{x})(y_i - \bar{y}))}{\frac{1}{n}\sum_{i=1}^{n}(x_i - \bar{x})^2}\\ 回帰係数aは(x,yの共分散)/(xの分散)で算出できるようです。切片bは、(3)の式に回帰係数aを代入すれば、算出することができます。これで、a, bの値が分かったので、予測式から未知の値を予測することができます。 実際に使ってみる コード2のデータにおいて、x = 70のときのyの値をpythonで計算して、予測してみます。(numpyの関数を使用します) コード3 import matplotlib.pyplot as plt import numpy as np from sklearn import datasets xdata = np.array([16, 76, 39, 53, 98]) ydata = np.array([58, 126, 90, 100, 139]) x_mean = np.mean(xdata) y_mean = np.mean(ydata) #np.mean 平均値を求める x_var = np.var(xdata) #np.var 分散を求める xy_cov = np.cov(xdata, ydata, rowvar=1)[0][1] #np.cov xdataとydataの共分散を求める a = round(x_var/xy_cov, 1) #回帰係数 b = round(y_mean - a*x_mean, 1) #切片 print("回帰係数", a) print("切片", b) print("予測式 y = {}x + {}".format(a, b)) #計算結果の表示 出力 回帰係数 0.8 切片 57.5 予測式 y = 0.8x + 57.5 回帰係数、切片、予測式を求めることができました。予測式をデータの散布図に当てはめてみます。 以下のコードを書き足します。 コード x = np.linspace(0, 100, 1000) y = a*x + b plt.plot(x, y) plt.text(50, 120, "y = {}x + {}".format(a, b)) plt.scatter(xdata, ydata) plt.xlabel("x") plt.ylabel("y") plt.show() 5つの点と近似するような一次関数の式が書けました。予測式に代入すると、x = 70のときの y の値は113.5 と予測できました。 参考までに… 最小二乗法の公式の導出 https://analytics-notty.tech/derivation-of-least-squares-equation/ python version 3.9.2
- 投稿日:2022-01-19T22:37:45+09:00
MacのPythonで音声データを扱う:オーディオデバイスの情報を取得
PythonでAudioデータを使ったアプリを作っていきます。 手始めに、オーディオデバイスの情報を取得します 開発環境 ・Mac(Mac Book Air 2013) ・MacOS Big Sur(11.5) ・Python 3.9.1 ・PortAudio 19.7.0 ・PyAudio 0.2.11 参考 PyAudioのAPI仕様を参考にします 手順 1.オーディオデバイスの数を取得 2.全オーディオデバイスに対して情報を取得 import pyaudio p = pyaudio.PyAudio() for i in range(0,p.get_device_count()): print(p.get_device_info_by_index(i)) 私のMacでは以下のような出力が得られました。 {'index': 0, 'structVersion': 2, 'name': 'Built-in Microphone', 'hostApi': 0, 'maxInputChannels': 2, 'maxOutputChannels': 0, 'defaultLowInputLatency': 0.0029478458049886623, 'defaultLowOutputLatency': 0.01, 'defaultHighInputLatency': 0.01310657596371882, 'defaultHighOutputLatency': 0.1, 'defaultSampleRate': 44100.0} {'index': 1, 'structVersion': 2, 'name': 'Built-in Output', 'hostApi': 0, 'maxInputChannels': 0, 'maxOutputChannels': 2, 'defaultLowInputLatency': 0.01, 'defaultLowOutputLatency': 0.007551020408163266, 'defaultHighInputLatency': 0.1, 'defaultHighOutputLatency': 0.017709750566893424, 'defaultSampleRate': 44100.0} オーディオ端子のマイク入力がindexの0番として認識されていますね。 ここで得た情報をもとに、オーディオアプリを作っていけそうです。 今回はここまで。
- 投稿日:2022-01-19T22:31:44+09:00
easy_patents 応用例
初めに easy_patentsの応用例を紹介する。 概要 他社特許の監視を行う際に、エクセルで監視対象の案件の出願番号を管理している局面を想定する。 本記事では、このエクセルファイルの更新を行うスクリプトを紹介する。 INPUTファイル 以下のような、出願番号(app_number)の列に監視対象の出願番号を並べたエクセルファイルをインプットとする。 なお、本例では、特許情報取得APIのサンプルでよく出てくる特願2020-8423と、その出願番号をインクリメントした7件の合計8件を監視対象案件とした。 サンプルコード sample_update_monitor_excel.py from easy_patents.get_info import app_progress from easy_patents.errors import NoDocumentError, TooManyAccessError, ParameterError, UnknownError import pandas as pd from datetime import datetime def update_monitor_excel(excel_path, sheet_name="Sheet1"): ''' 特許情報監視エクセルを更新する。 Parameters ---------- excel_path: str 更新対象のエクセルのパス sheet_name: str or int シート名またはシート番号 ''' # 指定されたエクセルを読み出す df = pd.read_excel(excel_path, sheet_name=sheet_name) # インストールしたpandasのバージョンによっては、以下のようにencodingを指定する必要がある。 # df = pd.read_excel(excel_path, # sheet_name=sheet_name, encoding="utf-8_sig") for index, row in df.iterrows(): # 読み込んだエクセルから出願番号を取得 app_number = row['app_number'] # 特許経過情報の取得 try: # reget_date=0とすることで、 # コンピュータにファイルが保存されているかにかかわらず、 # APIから最新の情報を取得する progress_info_all = app_progress(app_number, reget_date=0) except NoDocumentError: # 未公開情報などでドキュメントが取得できない場合にはスキップ continue except ParameterError as e: # パラメーターエラーの場合(出願番号の記載に誤りがあるような場合)には、 # メッセージを出してスキップ print(e) continue except (TooManyAccessError, UnknownError) as e: # アクセス数超過、原因不明エラーの場合には # エラーメッセージを出力して終了 print(e) break # 重要なデータのみ取り出し progress_info = progress_info_all['result']['data'] # 発明の名称を設定 row['invention_title'] = progress_info['inventionTitle'] # bibliographyInformationの中には、 # 複数のリストがあって処理がしずらいので、 # 強引に1つにまとめる document_lists = [] for binfo in progress_info['bibliographyInformation']: document_lists += binfo['documentList'] # 最後の処理が最新の処理と推定して、最後の処理を取ってくる。 last_progress = document_lists[-1] # 日付が8桁の整数であらわされているので、日付型に変換 row['last_process_date'] = datetime.strptime( last_progress['legalDate'], "%Y%m%d") row['last_process'] = last_progress['documentDescription'] # データフレームの対象行を取得した情報でアップデート df.loc[index] = row # エクセルに反映 df.to_excel(excel_path, sheet_name=sheet_name, index=False) # インストールしたpandasのバージョンによっては、以下のようにencodingを指定する必要がある。 # df.to_excel(excel_path, # sheet_name=sheet_name, index=False, encoding="utf-8_sig") if __name__ == "__main__": import sys excel_path = sys.argv[1] sheet_name = sys.argv[2] update_monitor_excel(excel_path, sheet_name) 実行例(コマンドプロンプト) ※複数シート以上あると、上記実装では他のシートが削除されるので注意。 $ cd <スクリプトがあるディレクトリ> $ python sample_update_monitor_excel.py <エクセルファイルのパス> <シート名> OUTPUTファイル(更新後のエクセルファイル)
- 投稿日:2022-01-19T21:52:19+09:00
Linux(Ubuntu)に非rootユーザがPython環境構築する方法
目的 Linuxマシンにroot権限がないユーザがPython環境を構築する方法。 滅多にない場面だが、共有GPUサーバーを利用する際に、root権限がないユーザを発行される & pipすら入っていない、という状況に遭遇した。 sudo apt install python3-pip とするとエラーが出たので、他の方法を試す。 前提 curl、もしくはwgetがインストールされているものとする。 方法 Anacondaをインストールする。具体的には以下の通り。 https://repo.anaconda.com/archive/ を訪れ、自分が使いたいバージョンのダウンロードリンクを確認する。例えば、記事執筆時(21/01/19)時点の最新バージョンだと https://repo.anaconda.com/archive/Anaconda3-2021.11-Linux-x86_64.sh である。 以下のコマンドを順に打ち込む。まずシェルスクリプトをダウンロード。 terminal curl -O ${先ほど確認したリンク} 例えば、 terminal curl -O https://repo.anaconda.com/archive/Anaconda3-2021.11-Linux-x86_64.sh curlがインストールされていない場合はwgetを使ってください。 Anacondaをインストール terminal sh ${先ほどダウンロードしたファイル} -b -p 例えば、 terminal sh Anaconda3-2021.11-Linux-x86_64.sh -b -p もうシェルスクリプトはいらないので捨ててください。 terminal rm ${先ほどダウンロードしたファイル} 環境変数を設定 terminal echo ". $HOME/anaconda3/etc/profile.d/conda.sh" >> ~/.bashrc echo "conda activate base" >> ~/.bashrc 適宜自分のシェルに合わせて読み替えてください。 再起動するか、 source ~/.bashrc anacondaを最新版に(任意) terminal conda update conda conda install anaconda これでPython環境が出来上がりました。 which python3 として出てきたパスがホームディレクトリ以下の anaconda3/bin/python3であれば正しいです。 終わりに sudo必要なかったですね。condaの使い方は他の記事読んでください。 これとか。 https://qiita.com/naz_/items/84634fbd134fbcd25296
- 投稿日:2022-01-19T21:41:29+09:00
特許情報の簡易取得パッケージ easy_patents
初めに 特許庁の特許情報取得APIを簡単に使えるようにするためのpythonパッケージeasy_patentsを作成したので、本記事では、それを紹介する。 easy_patents の特徴 (1)アクセストークンの取得を意識せずにAPIへのアクセスが可能 (2)簡易な指定が可能 (3)データをキャッシュするので、不必要な通信を避けることができる (1)アクセストークンの取得を意識せずにAPIへのアクセスが可能 アクセストークンの取得、更新は自動で行われる。最初にユーザ名とパスワードを設定することで、あとはアクセストークンの存在を意識せずにeasy_patentsを利用できる。 (2)簡易な指定が可能 easy_patentsでは、 progress_info = app_progress("特願2020-8423") のように、API名(app_progress)と指定情報("特願2020-8423")だけを書けば、目的のjsonデータや実体審査ファイルなどが取得できる。 なお、出願番号などは、柔軟な指定が可能であり、全角と半角を混ぜたり、数字のみで指定したり、ハイフンの代わりにスラッシュを使ったりしてもよい。 例えば、全角で「特願2020/8423号」と指定してもよいし、2020008423と指定してもよい。 ただし、20208423のように、ハイフンやスラッシュ、ゼロのすべてを省略することはできない。これは、ハイフンやスラッシュを基準として、右側の数字のゼロ埋め処理を行っているためである。 (3)データをキャッシュするので、不必要な通信を避けることができる 特許情報は、頻繁に更新されるものではないため、同じ情報取得のため、短期間のうちに何度もAPIへアクセスする必要はない。 この点に鑑みて、easy_patentsでは、すでに取得された情報については、一定期間中(defaultでは30日)は再取得をせずに、取得済みのファイル(キャッシュ)を返すようにした。ファイルの更新期間は、引数reget_dateで指定可能である。 progress_info = app_progress("特願2020-8423", reget_date=15) 上記の場合、既存のデータが15日以内のデータであれば再取得はせずに既存のデータが返される。 なお、reget_date=0とすることで、即時取得が可能である。 使い方 前提条件 python3.6以上 特許庁にAPI利用申請をし、usernameとpasswordを知らされていること。 インストール pip install easy-patents 認証情報設定 コマンドプロンプトやWindows Power Shellなどを立ち上げて、pythonと打ち込むことで、pythonのシェルが起動する。 そして、以下の通り実行すると、username, passwordの入力プロンプトが出るので、特許庁から送付されたusername, passwordを入力する。 $ python >>> from easy_patents.update_authinfo import update_authinfo >>> update_authinfo() username: password: ※username, passwordについては、特許庁から送付されたそのままの値を入力すること。(urlencodeなどはプログラム内部で行われる) なお、入力中のパスワードを表示させたい場合には、以下のようにすればよい。 >>> update_authinfo(display_password=True) サンプル実行 $ python >>> from easy_patents.get_info import app_progress >>> progress_info = app_progress("特願2020-8423") >>> progress_info['result']['data']['inventionTitle'] '管理システム及び管理方法' なお、実行時に末尾に以下のように表示された場合には、usernameかpasswordが間違っているため、update_autuinfo()を再実行する必要がある。 KeyError: 'access_token' また、実行時に以下のように表示された場合には、Visual C++ 再頒布可能パッケージ のインストールと再起動を行う必要がある。 ImportError: DLL load failed: 指定されたモジュールが見つかりません。 使用可能な関数 使用可能な関数名を、引数と共に表示する。 app_progress(case_number, reget_date=30) app_progress_simple(case_number, reget_date=30) divisional_app_info(case_number, reget_date=30) priority_right_app_info(case_number, reget_date=30) applicant_attorney_cd(code, reget_date=30) applicant_attorney(name, reget_date=30) application_reference(case_number, reget_date=30) publication_reference(case_number, reget_date=30) registration_reference(case_number, reget_date=30) app_doc_cont_opinion_amendment(case_number, reget_date=30) app_doc_cont_refusal_reason_decision(case_number, reget_date=30) app_doc_cont_refusal_reason(case_number, reget_date=30) cite_doc_info(case_number, reget_date=30) registration_info(case_number, reget_date=30) いずれも、以下のようにしてインポートして使用することができる。 from easy_patents.get_info import registration_info info = registration_info("特願2020-8423", reget_date=10) なお、application_reference, publication_reference, registration_reference以外の関数については、同名のAPIに対してアクセスする関数である。APIの詳細については、APIの仕様書を参照のこと。 application_reference, publication_reference, registration_reference以外の関数におけるcase_numberは出願番号、codeは申請人番号、nameは申請人名である。 regit_dateは再取得までの日数(=キャッシュの有効期間)である。 application_referenceは、case_number_referenceで種別をapplicationとしたもの。case_numberは出願番号。 publication_referenceは、case_number_referenceで種別をpublicationとしたもの。case_numberは公開・公表番号。 registration_referenceは、case_number_referenceで種別をregistrationとしたもの。case_numberは登録番号。 また、出願番号等を半角や特定の漢字無視をして変換する関数として、以下を使用できる。 easy_patents.get_info.convert_key(key) このほかの関数については、将来的に廃止や変更を行う可能性があるため、使わない方が良い。 戻り値 いずれも関数名に対応するAPIの情報を取得する。 jsonデータの場合には、jsonデータが返答される。 なお、このjsonデータには、easy_patents内部で使用するep_dataが付加されている。 zipデータの場合には、zip解凍先のディレクトリのパスが返される。 例外 APIのステータスコードに応じていくつかの例外が発生する。 NoDocumentError statusCodeが107, 108, 111の時に発生するエラー ParameterError statusCodeが204, 208, 212, 301, 400の時に発生するエラー TooManyAccessError statusCodeが203の時に発生するエラー TemporaryError statusCodeが210, 302, 303の時に発生するエラー UnknownError statusCodeが999かその他定義されていないコードの時に発生するエラー なお、ステータスコード210, 302, 303については、例外をすぐには発生させずに、最大3回まで1秒スリープでリトライする。 いずれの例外も、以下のようにしてインポートすることができる。 from easy_patents.errors import UnknownError キャッシュクリア キャッシュはコマンドプロンプトなどで以下のコマンドで削除できる。 <easy_patentsがインストールされているディレクトリ>/delete_caches.py <削除対象のキャッシュの経過日数> もしくは、pythonシェルやスクリプトで以下のようにして削除してもよい。 >>> from easy_patents.delete_caches import delete_caches >>> delete_caches(delete_before=<削除対象のキャッシュの経過日数>) なお、全てのキャッシュを削除したい場合には、easy_patentsがインストールされているディレクトリ配下のdataディレクトリを削除すればよい。 免責事項 easy_patentsは、とある弁理士が開発したものである。利用者はeasy_patentsを自己の責任で使うこと。easy_patentsの使用によって発生したいかなる損害についてもeasy_patentsの開発者は責任を負わない。 問合せ先 tiwtter: @EasyPatents39 email: easypatents39@gmail.com
- 投稿日:2022-01-19T21:12:09+09:00
【Python3】abcモジュールで抽象クラスを実装する
はじめに Pythonの標準モジュールであるabc(abstract base class)を使うと抽象クラスを利用することができます。 環境 Ubuntu20.04 Python3.8.10 使い方 抽象クラスを定義 animal.py # 1. モジュールをインポート from abc import ABC, abstractmethod # 2. ABCクラスを継承 class Animal(ABC): # 3. デコレータで抽象メソッドであることを宣言 @abstractmethod def cry(self): pass まずは冒頭でabcモジュールからABCクラス・abstractmethodデコレータをインポートします。 クラスの定義時にABCクラスを継承することで抽象クラスとして定義できます。 class Animal(ABC): 以下のような書き方も可能です。 class Animal(metaclass=ABCMeta): @abstractデコレータをメソッドにつけると抽象メソッドであることが示されます。 抽象メソッドにも具体的な処理を記述することはできますが、抽象メソッドであることを明示するためには何も書かないのが望ましいでしょう。 継承先クラスを実装 先ほど定義したAnimalクラスを継承したクラスを実装します。 cat.py # 抽象クラスをインポート from animal import Animal # 抽象クラスを継承 class Cat(Animal): def __init__(self, name: str): self.name: str = name # 抽象メソッドを実装 def cry(self): print('にゃーん') # Animal型のオブジェクトとしてcatを宣言 cat: Animal = Cat("ハチワレ") cat.cry() cat.pyを実行します。 $ python3 cat.py にゃーん 正常に実行することができました。 Animalクラスで定義されているcryメソッドを実装しない場合はどうなるでしょうか。 dog.py # 抽象クラスをインポート from animal import Animal # 抽象クラスを継承 class Dog(Animal): def __init__(self, name: str) ->None: self.name: str = name # 抽象メソッドを定義しない dog: Dog = Dog("ポチ") このdog.pyを実行してみます。 $ python3 dog.py Traceback (most recent call last): File "dog.py", line 9, in <module> dog: Animal = Dog("ポチ") TypeError: Can't instantiate abstract class Dog with abstract methods cry Animalクラスで定義したcryメソッドを実装していないとエラーになります。 このように、抽象クラスを使えば継承先のクラスにメソッドを実装するように強制することができます。実装漏れや同じようなクラスで振る舞いが異なるという事態を事前に回避するのに役立ちます。 参考資料 abc --- 抽象基底クラス
- 投稿日:2022-01-19T18:54:12+09:00
MediaPipeのFACE_CONTOURSでトラブった話
トラブル内容 こちらのサンプルでフェイスメッシュして遊ぼうとしたら、 エラー吐いて落ちる現象。 AttributeError: module 'mediapipe.python.solutions.face_mesh' has no attribute 'FACE_CONTOURS' 環境 Windows 10 64bit Python 3.9.1 解決方法 海外サイトを漁っていたら見つけた情報。 「Update: FACEMESH_CONTOURS is the correct new name.」 サンプルのfacemesh.pyの35行目あたり、 FACE_CONTOURSをFACEMESH_CONTOURSに書き換えたらすんなり動いた。 connections=mp_face_mesh.FACEMESH_CONTOURS ,
- 投稿日:2022-01-19T15:56:37+09:00
【備忘録】pytorchで空の配列にtensorをappendするには
モチベーション 備忘録です。 for文を使って処理した値を空のリストに入れるというような操作をpytorchでやりたかったのですが、pytorchで空の配列を作ろうとすると処理後のtensorの次元にあったものを作って代入する必要があるっぽい。しかしパラメータによって処理後のtensorの次元が異なっていてそれに合わせた空のtensorを作るのはあまりにも面倒くさいと頭を抱えていたところようやくやり方を見つけました。 参考URL : Appending to a tensor 僕が頭を抱えていたところ ここは僕個人の問題なので読み飛ばしていただいて構いません。 1次元畳み込みtorch.Conv1dをつかってチャネルごとの特徴量を抽出した後、それらを結合したかった。 やり方 一般的なやり方通り空の配列[]を作る forループの中で処理したtensorを空の配列にappendする forループ終了後はtorch.catでリストの中身のtensorをくっつけたい次元を指定してconcatenate. model.py features = [] for i in range(x.shape[1]): x_conved = cnn1d(x[ :, i, :]) features.append(x_conved) features = torch.cat(features, dim = 1) なんでこんな簡単なこと思いつかなかったんだろう・・・
- 投稿日:2022-01-19T14:58:20+09:00
matplotlib による 等高線 の 白抜き
Reference : [Pythonによる科学・技術計算] 2次元(カラー)等高線等の描画,可視化,matplotlib by @sci_Haru on Qiita Patheffect Demo by matplotlib Implementation : import numpy as np import matplotlib.pyplot as plt x = np.arange(0, 10, 0.05) y = np.arange(0, 10, 0.05) X, Y = np.meshgrid(x, y) Z = np.sin(X) + np.cos(Y) plt.setp(cntr.collections, path_effects=[ patheffects.withStroke(linewidth=3, foreground="w")]) cont=plt.contour(X,Y,Z, 5, vmin=-1,vmax=1, colors=['black']) plt.setp(cont.collections, path_effects=[ patheffects.withStroke(linewidth=3, foreground="w")]) cl=cont.clabel(fmt='%1.1f', fontsize=14) plt.setp(cl, path_effects=[ patheffects.withStroke(linewidth=3, foreground="w")]) plt.xlabel('X', fontsize=24) plt.ylabel('Y', fontsize=24) plt.pcolormesh(X,Y,Z, cmap='cool') pp=plt.colorbar (orientation="vertical") pp.set_label("Label", fontsize=24) plt.show()
- 投稿日:2022-01-19T14:07:21+09:00
簡単に作れる?! 「にじ」で「さんじ」が検索できるword2vecコーパス作成への道
はじめに どうも、お久しぶりです。 検索エンジンが気になったので、調べたけど、挫折した人です。 この記事では、そんな検索エンジンを調べたときに知った 「word2vecを使った類義語検索」 について紹介します。 この記事では、wikipediaの文章を使ってコーパスを作っていきます。 開発環境 Windows11 python: 3.10 gensim: 4.1.2 Gensimのバージョンを確認してね 見ていた資料とバージョンが違って、挫折しかけたから、気をつけて wikiページの取得 まず、wikipediaのページ全てのデータを取得していきます。 これには、面倒なスクレイピングを使うのではなく、便利な圧縮ファイルがあるので、それをダウンロードします。 ふつうにwebサイトからダウンロードしてもいいのですが、なんかカッコイイので、curlでダウンロードします。 curl https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pages-articles.xml.bz2 -o jawiki-latest-pages-articles.xml.bz2 記事執筆時には3.8GBくらいあって、自分のPC/ネット環境だと、2時間くらいかかりました。 wiki圧縮ファイルの解凍 ダウンロードしたファイルは、圧縮ファイルです。 これは、普通にCLIコマンドで解凍してもいいのですが、それじゃつまらないので、「WikiExtractor」という、そこそこ有名な解凍ライブラリを使用します。 pip install wikiextractor これでインストール完了 圧縮ファイルをダウンロードしたディレクトリで以下のコマンドを叩くと、解凍が始まります。 python -m wikiextractor.WikiExtractor jawiki-latest-pages-articles.xml.bz2 > python -m wikiextractor.WikiExtractor jawiki-latest-pages-articles.xml.bz2 INFO: Preprocessing 'jawiki-latest-pages-articles.xml.bz2' to collect template definitions: this may take some time. INFO: Preprocessed 100000 pages INFO: Preprocessed 200000 pages INFO: Preprocessed 300000 pages INFO: Preprocessed 400000 pages INFO: Preprocessed 500000 pages INFO: Preprocessed 600000 pages INFO: Preprocessed 700000 pages INFO: Preprocessed 800000 pages INFO: Preprocessed 900000 pages INFO: Preprocessed 1000000 pages INFO: Preprocessed 1100000 pages INFO: Preprocessed 1200000 pages INFO: Preprocessed 1300000 pages INFO: Preprocessed 1400000 pages INFO: Preprocessed 1500000 pages INFO: Preprocessed 1600000 pages INFO: Preprocessed 1700000 pages INFO: Preprocessed 1800000 pages INFO: Preprocessed 1900000 pages INFO: Preprocessed 2000000 pages INFO: Preprocessed 2100000 pages INFO: Preprocessed 2200000 pages INFO: Preprocessed 2300000 pages INFO: Preprocessed 2400000 pages INFO: Preprocessed 2500000 pages INFO: Preprocessed 2600000 pages INFO: Loaded 94413 templates in 765.2s INFO: Starting page extraction from jawiki-latest-pages-articles.xml.bz2. こんなログが出たとなと思った次の瞬間、、、 Traceback (most recent call last): File "C:\Python310\lib\runpy.py", line 196, in _run_module_as_main return _run_code(code, main_globals, None, File "C:\Python310\lib\runpy.py", line 86, in _run_code exec(code, run_globals) File "C:\Python310\lib\site-packages\wikiextractor\WikiExtractor.py", line 645, in <module> main() File "C:\Python310\lib\site-packages\wikiextractor\WikiExtractor.py", line 640, in main process_dump(input_file, args.templates, output_path, file_size, File "C:\Python310\lib\site-packages\wikiextractor\WikiExtractor.py", line 359, in process_dump Process = get_context("fork").Process File "C:\Python310\lib\multiprocessing\context.py", line 239, in get_context return super().get_context(method) File "C:\Python310\lib\multiprocessing\context.py", line 193, in get_context raise ValueError('cannot find context for %r' % method) from None ValueError: cannot find context for 'fork' はい、エラーが来ました。 出てきたものを見る感じ、ライブラリエラーなので、wikiextractorを最新版にします。 pip install git+https://github.com/prokotg/wikiextractor Githubから直接インストールすると、最新版がインストールできます。 これで、インストールができたので、もう一度解凍コマンドを実行します。 python -m wikiextractor.WikiExtractor jawiki-latest-pages-articles.xml.bz2 今回はうまくいきました。 多分、このコマンドを実行したディレクトリにtextフォルダーができて、中に大量のwikiデータが入ってると思います。 中身を見てみたい場合は、ファイルをメモ帳やエディターで開くと、中身が見れます。 wikiをまとめる これには、コマンドが複数ありますが、今回は一番単純は"cat"を使ったコマンドを使用します。 cat text/*/* > wiki.txt これで、ファイルを一つにまとめることができました。 ネタバレ:こいつが原因だった。 wiki文章の整形 wikiファイルたちの中身を見た人は気づいたかもしれませんが、wikiファイルは、 <doc></doc> で囲まれています。 これは、wikiコーパスの精度を落とす可能性があるので、消します。 あと、wiki特有の、読み仮名をカッコで囲むやつも、精度を落とす可能性があるので、消します。 sed -e 's/<[^>]*>//g' ./wiki.txt > ./wiki_notag.txt sed -i -e 's/(/(/g' ./wiki_notag.txt && sed -i -e 's/)/)/g' ./wiki_notag.txt sed -i -e 's/([^)]*)//g' ./wiki_notag.txt sed -i -e 's/ //g' ./wiki_notag.txt && sed -i -e '/^$/d' ./wiki_notag.txt これら、四つのコマンドを使うことで、いらないものが削除されました。 ネタバレ:こいつも原因だった。 MeCabでWakati word2vecのコーパスを作る上で、分かち書きは必ず(?)行わなければなりません。 日本語の分かち書きに使えるライブラリで一番有名で人気があるのがMeCabです。 MeCabインストール MeCabは、以下のurlからインストールできます。 インストーラーを起動して、ポチポチすると、このような画面になったと思います。 ここでは、SHIFT-JISを選択します。 理由は簡単で、SHIFT-JISだと、コンソール上で文字化けしないからです。 詳しい理由はggってくれ UTF-8 すもももももももものうち すもももももももものうち 險伜捷,荳闊ャ,*,*,*,*,* EOS 今日は晴天なり 今日は晴 險伜捷,荳闊ャ,*,*,*,*,* V 蜷崎ゥ・蝗コ譛牙錐隧・邨・ケ・*,*,*,* 險伜捷,荳闊ャ,*,*,*,*,* ネ 蜷崎ゥ・蝗コ譛牙錐隧・邨・ケ・*,*,*,* 險伜捷,荳闊ャ,*,*,*,*,* EOS SHIFT-JIS すもももももももものうち すもも 名詞,一般,*,*,*,*,すもも,スモモ,スモモ も 助詞,係助詞,*,*,*,*,も,モ,モ もも 名詞,一般,*,*,*,*,もも,モモ,モモ も 助詞,係助詞,*,*,*,*,も,モ,モ もも 名詞,一般,*,*,*,*,もも,モモ,モモ の 助詞,連体化,*,*,*,*,の,ノ,ノ うち 名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ EOS 今日は晴天なり 今日 名詞,副詞可能,*,*,*,*,今日,キョウ,キョー は 助詞,係助詞,*,*,*,*,は,ハ,ワ 晴天 名詞,一般,*,*,*,*,晴天,セイテン,セイテン なり 助動詞,*,*,*,文語・ナリ,基本形,なり,ナリ,ナリ EOS MeCabで分かち書きをする mecab -Owakati wiki_notag.txt -o wiki_wakati.txt このコマンドを叩くだけで、分かち書きができます。 途中、こんな文字が出てきた人がいるかもしれません。 開発に影響を与えるタイプでは無いので、大丈夫です。(多分...) input-buffer overflow. The line is split. use -b #SIZE option. Gensim君 Gensimというのは、超簡単に言うと、AIを作る用のライブラリ郡です。 正直、よく理解してない インストールしていきます。 Gensimのインストール 超簡単、pip installすればいいだけ! pip install -U gensim まぁ、ここでエラーが出てくるんすけどね(笑) "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\bin\HostX86\x64\cl.exe" /c /nologo /O2 /W3 /GL /DNDEBUG /MD -IC:\Python310\include -IC:\Python310\Include -IC:\Python310\lib\site-packages\numpy\core\include "-IC:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\include" "-IC:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\include\um" /Tcgensim/models/word2vec_inner.c /Fobuild\temp.win-amd64-3.10\Release\gensim/models/word2vec_inner.obj word2vec_inner.c C:\Python310\include\pyconfig.h(59): fatal error C1083: include ファイルを開けません。'io.h':No such file or directory error: command 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.30.30705\\bin\\HostX86\\x64\\cl.exe' failed with exit code 2 ---------------------------------------- ERROR: Command errored out with exit status 1: 'C:\Python310\python.exe' -u -c 'import io, os, sys, setuptools, tokenize; sys.argv[0] = '"'"'C:\\Users\\Zect\\AppData\\Local\\Temp\\pip-install-hkqqlyre\\gensim_915fa383964a4ff896ebe797b6f105c2\\setup.py'"'"'; __file__='"'"'C:\\Users\\Zect\\AppData\\Local\\Temp\\pip-install-hkqqlyre\\gensim_915fa383964a4ff896ebe797b6f105c2\\setup.py'"'"';f = getattr(tokenize, '"'"'open'"'"', open)(__file__) if os.path.exists(__file__) else io.StringIO('"'"'from setuptools import setup; setup()'"'"');code = f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record 'C:\Users\Zect\AppData\Local\Temp\pip-record-6v3t2lvi\install-record.txt' --single-version-externally-managed --user --prefix= --compile --install-headers 'C:\Users\Zect\AppData\Roaming\Python\Python310\Include\gensim' Check the logs for full command output. ログが超長いので、最後の数行だけ載せました。 これを見るに、Visual StudioのBuild Toolsである、MSVC Ver.14.30.30705を開こうとしているみたいです。 なので、Visual StudioのBuild ToolsからMSVC Ver.14をインストールします。 画像を持ってくる ベクトルの作成 makeVector.py from gensim.models import word2vec import logging logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO) sentences = word2vec.Text8Corpus('./wiki_wakati.txt') model = word2vec.Word2Vec(sentences, vector_size=200, min_count=20, window=15) model.wv.save_word2vec_format("./wiki.model", binary=True) こんな感じ このコードを実行してあげることで、wikiコーパス(モデル)の作成が行われます。 vector_size: ベクトルの次元数 min_count: 出てきた回数がn回以下の場合は使用しない window: 単語間の最大距離 引数に関しては、ここを参照 モデルを使った単語の類似検索 よし、じゃぁ、モデルを試しに使ってみよう! testModel.py from gensim.models import KeyedVectors wv = KeyedVectors.load_word2vec_format('wiki.model', binary=True) results = wv.most_similar(positive='日本') for result in results: print(result) 検索する文字は、もちろん日本!!! Traceback (most recent call last): File "C:\wikivectest.py", line 4, in <module> results = wv.most_similar(positive='日本') File "C:\gensim\keyedvectors.py", line 773, in most_similar mean.append(weight * self.get_vector(key, norm=True)) File "C:\gensim\keyedvectors.py", line 438, in get_vector index = self.get_index(key) File "C:\gensim\keyedvectors.py", line 412, in get_index raise KeyError(f"Key '{key}' not present") KeyError: "Key '日本' not present" あれ??? '日本'という言葉がモデル内に存在しないと言われてしまいました、、 なぜだ? いろいろ試してみたところ、アルファベットでの検索には対応しているみたいです。 てことで、'Japan'で検索しました testModel.py from gensim.models import KeyedVectors wv = KeyedVectors.load_word2vec_format('wiki.model', binary=True) - results = wv.most_similar(positive='日本') + results = wv.most_similar(positive='Japan') for result in results: print(result) ('etc', 0.5311076641082764) ('や', 0.4949520230293274) ('縲舌', 0.4680085778236389) ('縺娯', 0.46634429693222046) ('YuyaKiuchi', 0.4642036259174347) ('笳', 0.4613495171070099) ('笳上', 0.460459440946579) ('縲職', 0.4527381658554077) ('..', 0.45070236921310425) ('nGene', 0.45022666454315186) おやおや やはり、バイナリ文字が混じってたみたいですね。 この文字は、CLIで作業するとき、win上でよく出てくる文字です。 文字コードの処理をしなかったり、CLIコマンドでファイルを処理したりすると、よく出てきます。 (個人差があります。) こいつ、どうやって解消しよう、、、 CLIコマンドの作業をPythonで書く それらしき文献が一切なかったので、最初から実行し、出力されるファイルを途中で無理やり開いて中身を見た結果、 wikiデータを一つにまとめるコマンド cat text/*/* > wiki.txt が悪さをしていたみたいです。 なので、ファイルをまとめるコードをPythonで記述します。 gatherWiki.py import glob import logging logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO) logger = logging.getLogger(__name__) AllData = '' Folders = glob.glob('text\*') for FolderName in Folders: files = glob.glob(f"{FolderName}\*") for file in files: f = open(file, 'r', encoding='UTF-8') data = f.read() AllData += data + '\n' logger.info(file) wf = open(f'data/wiki_data_{FolderName[5:7]}.txt', 'w', encoding='UTF-8') wf.write(AllData) wf.close wf = open('wiki_data.txt', 'w', encoding='UTF-8') wf.write(AllData) wf.close() これで、ファイルを一つにまとめて、「wiki_data.txt」というファイルで書き出すことができるようになりました。 解説をすると、 glob.glob('text\*') で、textディレクトリ内のフォルダを取得します。 「フォルダを取得」といっても、wikiextractorから出力されたものからなにか編集していた場合は若干不具合が生じるかもしれません。 このデータはlist型で取得できるので、forで回しました。 そして、回ってきたフォルダの中身を検索します。 files = glob.glob(f"{FolderName}\*") これで、「text/${FolderName}/*」が取得できました。 ファイルの中身を読む方法は、 f = open("<ファイル名>", 'r', encoding='UTF-8') f.read() これでできます。 open()のパラメーターの 1つ目は、ファイル名 2つ目は、モード;ここでは、読み込むだけなので、'r'を選択;書き込むとき/ファイル新規作成は'w'にする 3つ目は、文字コードの指定;UTF-8じゃないとバイナリが交じるので、UTF-8にします。 で、ここで読み込んだデータを最初に用意した変数に「+=」しました。 その次に、途中で処理が中断して、やりくりしたデータが全部飛んだときの対策に、1フォルダ読み込み終わったら、それまでのデータを書き出すことをします。 wf = open(f'data/wiki_data_{FolderName[5:7]}.txt', 'w', encoding='UTF-8') wf.write(AllData) このコードを実行する前に、dataフォルダが存在することを確認してください。 もし、dataディレクトリが存在しない場合は、エラーを吐きます。 ここではファイルの新規作成を行うので、2つ目のパラメーターは'w'にします。 これで解説は以上 なんかわからないところがあったら、ggるか、コメ欄に書いといて PythonのMeCabでWakati さて、wiki文章を一つのファイルにまとめることができたから、ここからは分かち書きをしていくよ! mecab -Owakati wiki_notag.txt -o wiki_wakati.txt ここでは、このコマンドは使いません これもPythonで書いていきます。 wakatiWiki.py import glob import MeCab import logging logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO) logger = logging.getLogger(__name__) tagger = MeCab.Tagger("-Owakati") file = 'wiki_data.txt' f = open(file, 'r', encoding='UTF-8') data = f.read() wakatiData = tagger.parse(data) wf = open('wiki_data_wakati.txt', 'w', encoding='UTF-8') wf.write(AllData) wf.close() これでできました。 解説 tagger = MeCab.Tagger("-Owakati") こいつで、「私、ワカチ宣言」します。 このtaggerというのは、こういう風にしてあげると、watati書きしてくれます。 tagger.parse('すもももももももものうち') # すもも も もも も もも の うち さっきのコードと違うのは、処理がアホほど遅いことです。 ここでは「wiki_data.txt」を読み込んでるので、しょうがないです。 実行したら、待ちます。 ......一時間後 [実行終了] エクスプローラーを見る 「あれ??ファイルがない。。。」 もう一度実行 ......一時間後 [実行終了] 「やっぱ、ファイルが無い!」 データが大きすぎると、wakatiできない問題 もう、こいつはどうしようもない。 ここでは、wiki文章をまとめるときに使ったコードの途中にwakati書きすることにして、対応した。 コードはこんな感じ gatherWiki.py import glob import MeCab import logging logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO) logger = logging.getLogger(__name__) tagger = MeCab.Tagger("-Owakati") AllData = '' Folders = glob.glob('text\*') for FolderName in Folders: files = glob.glob(f"{FolderName}\*") for file in files: f = open(file, 'r', encoding='UTF-8') data = f.read() - AllData += data + '\n' + wakatiData = tagger.parse(data) + AllData += wakatiData + '\n' logger.info(file) - wf = open(f'data/wiki_data_{FolderName[5:7]}.txt', 'w', encoding='UTF-8') + wf = open(f'wakati/wiki_data_{FolderName[5:7]}.txt', 'w', encoding='UTF-8') wf.write(AllData) wf.close wf = open('wiki_data_wakati.txt', 'w', encoding='UTF-8') wf.write(AllData) wf.close() 解説は、、、 いらないかな 全部、前に説明したことだし 2つのコードを一つにまとめた、みたいなところあるから、解説いらないよね この記事の主役は我々だ! やっとword2vecの出番です。 from gensim.models import word2vec import logging logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO) sentences = word2vec.Text8Corpus('./wiki_wakati_w.txt') model = word2vec.Word2Vec(sentences, vector_size=200, min_count=20, window=15, workers=10) model.wv.save_word2vec_format("./wiki.model", binary=True) はい。 出番終了です() 解説 上で使ったコードに加え、一つ引数を足しました。 "workers" ですね。 これは、処理を早くすることができる引数です。 ご利用は計画的に ちなみに、ここでこの引数が出てきたのは、ちょうどこの時期にハイエンドが届いて、処理が早くなったから(自慢) 単語検索 これで、モデルの作成が終了したので、遊んでいきましょう! vecTest.py from gensim.models import KeyedVectors wv = KeyedVectors.load_word2vec_format('wiki_data.model', binary=True) results = wv.most_similar(positive=input('単語を入力\n>>> ')) for result in results: print(result) 解説の必要は無いですかね。 オタクたるもの、まずは推しから検索すべし! > python wikivectest.py 単語を入力 >>> 初音 ('ミク', 0.814029335975647) ('VOCALOID', 0.6854016184806824) ('KAITO', 0.6704154014587402) ('ボーカロイド', 0.6387607455253601) ('MEIKO', 0.6119569540023804) ('クリプトン・フューチャー・メディア', 0.5945071578025818) ('Megpoid', 0.57939213514328) ('TUNES', 0.5519722104072571) ('GUMI', 0.5508669018745422) ('EXIT', 0.5456638932228088) すげーーーーーーー!!! まさか、ここでCryptonが出てくるとは思いませんでした。 それも、コサイン類似度0.594! 結構近いですよ 一番上に「ミク」がいるので、「ミク」を検索してみましょう > python wikivectest.py 単語を入力 >>> ミク ('初音', 0.8140293955802917) ('VOCALOID', 0.7328931093215942) ('KAITO', 0.6994694471359253) ('ボーカロイド', 0.688727617263794) ('MEIKO', 0.6449904441833496) ('Megpoid', 0.6289090514183044) ('クリプトン・フューチャー・メディア', 0.6288353204727173) ('はつね', 0.6032723784446716) ('TUNES', 0.5871540904045105) ('GUMI', 0.5743457674980164) こんどは、ひらがな「はつね」がいますね > python wikivectest.py 単語を入力 >>> はつね ('フィーチャリング・', 0.6516125798225403) ('ミク', 0.6032723188400269) ('エグジット・チューンズ・プレゼンツ', 0.5987372398376465) ('初音', 0.5109699964523315) ('にじ', 0.500720202922821) ('はなみ', 0.46432074904441833) ('ぎざか', 0.4537661075592041) ('シーディー', 0.45156562328338623) ('フォーティーシックス', 0.4501747488975525) ('こいする', 0.4432184100151062) まさかの、「こいする」 知ってる人は知ってると思いますが、知らない人のために解説すると、 「恋するVoc@loid」という楽曲がありまして、その曲のタイトルの一部が出てくるとは思ってもいませんでした。 おや? 「にじ」 がある。。。 やっぱねぇ、 「にじ」 と言ったら、アレしか無いでしょ そう! > python wikivectest.py 単語を入力 >>> にじ ('さんじ', 0.7964274287223816) ('きゅうじ', 0.7316185235977173) ('よじ', 0.7172978520393372) ('かんじ', 0.7107529044151306) ('かせん', 0.7078416347503662) ('とうせん', 0.7055351138114929) ('てんし', 0.7030456066131592) ('じじ', 0.7001359462738037) ('げんじ', 0.695505678653717) ('えんか', 0.6890977621078491) にじ さんじ 文字同士の関連度を調べる 竹島って、どこの国のものなんでしょう() このモデルを使って調べてみましょう!(炎上案件) from gensim.models import KeyedVectors wv = KeyedVectors.load_word2vec_format('wiki_data.model', binary=True) kr = wv.similarity('竹島', '韓国') jp = wv.similarity('竹島', '日本') nk = wv.similarity('竹島', '北朝鮮') cn = wv.similarity('竹島', '中国') print(f'''\ 韓国 と竹島の類似度 : {kr} 日本 と竹島の類似度 : {jp} 北朝鮮と竹島の類似度 : {nk} 中国 と竹島の類似度 : {cn}\ ''') さぁ、どの国が一番数値が高かったと思いますか? 韓国 と竹島の類似度 : 0.41579458117485046 日本 と竹島の類似度 : 0.18454104661941528 北朝鮮と竹島の類似度 : 0.3764462172985077 中国 と竹島の類似度 : 0.25470954179763794 おや、誰か来たようだ なんか、途中で問題がおきたら、コメントか、TwitterのDMで教えてくれ じゃぁ、次の記事で会おう! ノシ
- 投稿日:2022-01-19T14:01:06+09:00
自作モジュールをPyPIに登録する
目的 自作モジュールをPyPIへ登録してpip installでインストールします。今回は簡単なコードを実装して使います。 方法 基本的に公式のチュートリアルに従います。個人的なこだわりで、wslを使ったりgithubに上げたり、仮想環境をpyvenvで作ったりしてますが、今回はそういう事が無くても出来るようにしているつもりです。何回かコマンドを使う必要がある為、そこで引っかかるかもしれません。そこは他の記事にお任せします。または後日、更新します。 言葉の定義 ライブラリ、パッケージ、モジュールの区別が難しいので、これを参考に大まかに区別をしておきます。 Pythonのモジュール、パッケージ、ライブラリ徹底解説! 必要なディレクトリやファイルを作る 公式のチュートリアルのままです。これを任意の場所に作ります。僕はデスクトップにPyPIフォルダを作ってその中で作業しています。 setup.cfgの編集 前はsetup.pyを作るのが公式だったようですが、今はsetup.cfgとpyproject.tomlを使うようです。中身は公式にお任せしますが、変更点についてだけ、お話します。赤枠で囲まれている所を変更します。 赤枠で囲まれたところは下の様に書き直しました。 name:自分の付けたいモジュール名 author:PyPIで登録した名前を入れてます author_email:PyPIで登録したメールアドレスを入れてます url:自分のgithubのurlを使っています。そのままでも動きました。 project_urls:自分のgithubのurlを使っています。そのままでも動きました。 buildする 必要なディレクトリやファイルが出来たらbuildしてPyPIへデプロイするする準備をします。ご自身のターミナル(僕の場合はwsl)で次のコマンドを実行して下さい。 コマンドの更新 buildコマンドを更新します。 python3 -m pip install --upgrade build buildしてPyPIへ上げるファイルとディレクトリを作ります。 python3 -m build 上のコードを実行するとdistファイルが出来るはずです。中に.whlファイルと.tar.gzファイルが出来ていれば成功です。具体的には下図の様になります。 PyPIへの登録 テスト環境へアップロード python3 -m twine upload --repository-url https://test.pypi.org/legacy/ dist/* テスト環境に上げたモジュールを使うために、自分の環境にインストールします。 python3 -m pip install --index-url https://test.pypi.org/simple/ --no-deps packaging_tutorial 本番環境へアップロード テストで上手く出来たら本番環境にアップロードします。 python3 -m twine upload dist/* この後はいつものpip install <package_name>でインストール出来るようになります。 感想 今回は、まずPyPIへアップロードするためだけに、公式チュートリアルに従って簡単に説明しました。説明が簡略化され過ぎて分からない所があると思いますので、その際は質問して下さると嬉しいです。可能な限り、ご説明させて頂きます。
- 投稿日:2022-01-19T12:57:09+09:00
【Project Euler】Problem 41: パンデジタル素数
本記事はProjectEulerの「100番以下の問題の説明は記載可能」という規定に基づいて回答のヒントが書かれていますので、自分である程度考えてみてから読まれることをお勧めします。 問題 41. パンデジタル素数 原文 Problem 41: Pandigital prime 問題の要約:n桁のパンデジタル数で素数のものの最大値を求めよ パンデジタル数はもう何度も出てきていますが、今回は素数。例として素数2143が4桁パンデジタル数として挙げられています。これも最大値を求めよなので大きい順に探していきます。 import itertools import sympy digits9 = "123456789" found = False for n in range(9,0,-1): digits = digits9[n-1::-1] # "987654321", "87654321",,,"21","1" for pstr in itertools.permutations(digits): # pandigital number in decending order ps = ''.join(pstr) if sympy.isprime(int(ps)): found = True break if found: break print(f"Answer: {ps}") (開発環境:Google Colab)
- 投稿日:2022-01-19T12:54:43+09:00
【Project Euler】Problem 40: チャンパーノウン定数
本記事はProjectEulerの「100番以下の問題の説明は記載可能」という規定に基づいて回答のヒントが書かれていますので、自分である程度考えてみてから読まれることをお勧めします。 問題 40. チャンパーノウン定数 原文 Problem 40: Champernowne's constant 問題の要約:以下のように小数点以下のに自然数を 1 から小さい順に並べた少数点以下n桁目の数を$d_n$としたときの$d_1 × d_{10}× d_{100} × d_{1000} × d_{10000} × d_{100000} × d_{1000000} $を求めよ 0.12345678910112131415161718192021... \\ チャンパーノウン定数(Wikipedia) に詳しい説明があります。プログラム的にはかなりシンプルです。 import numpy as np def Cham(maxlen): # generate Champernowne's constant as long as maxlen ret = "" for n in range(1,N+1): ret += str(n) if len(ret)>maxlen: break return ret print(f"Answer is {np.prod([int(Cham(10**6)[10**i-1]) for i in range(NP+1)])}") (開発環境:Google Colab)
- 投稿日:2022-01-19T12:54:42+09:00
pipでインストールしたパッケージのコマンドが使えなかった話(python -m pip)
pipでインストールしたパッケージをコマンドで実行できない問題が発生したので問題状況と解決までを書きたいと思います 各種情報 プラットフォーム:MacBook Pro(intel-2019) OS: macOS Monterey 12.0.1 Python: Python3.9.7 pip3 : pip 21.3.1 from /usr/local/lib/python3.9/site-packages/pip (python 3.9) 問題状況 ESP32の開発環境整備のためcliからwebreplを使えるようにしたかった そのためにupydevというパッケージを見つけたのでインストールして使えるようにしたいと思い下記を実行 $ pip3 install upydev 問題なくインストールは完了した. ....がしかし $ upydev zsh: command not found: upydev なぜかコマンドが見つからないと言われてしまって困った 原因を調べていると, 過去のPythonのバージョンが残っていて、pipは過去のPythonで実行されている というこちらから見つけた. 解決 ということで解決方法ですが, 直接pipコマンドは使わず,pythonコマンドを介してpipモジュールを使う ということになりそうです. $ python3 -m pip install <package-name> とすることで実行できる. 実際にやってみた $ python3 -m pip install upydev [...] Successfully installed upydev-0.3.7 $ upydev -v upydev: 0.3.7 無事実行できた! 今度からpip3はpython3 -m pipを使っていきたいと思います
- 投稿日:2022-01-19T12:52:31+09:00
【Project Euler】Problem 39: 整数の直角三角形
本記事はProjectEulerの「100番以下の問題の説明は記載可能」という規定に基づいて回答のヒントが書かれていますので、自分である程度考えてみてから読まれることをお勧めします。 問題 39. 整数の直角三角形 原文 Problem 39: Integer right triangles 問題の要約:整数の直角三角形の周囲の和pとするとp<1000で最も多いpを求めよ 整数の直角三角形すなわちピタゴラス数の生成は「Problem 9: 特別なピタゴラス数」で作ったpythを使います。生成された基本ピタゴラス数の周囲の和pの1000以下の倍数をすべて配列に入れ、これをCounterに入力して数えて最も多いものをmost_commonメソッドで取り出します。ただし要注意なのはpythは必ずしも周囲の和pの小さい順には生成しないのでp=1000で止めると、その後に1000より小さいものが出てくる可能性があるということです。 from collections import Counter N, p, peris = 1000, 0, [] pgen = pyth() while p > N*2: # need up to twice of the bound p = sum(next(pgen)) for i in range(1,(N//p)+1): # Multiples up to N peris.append(p*i) print(Counter(peris).most_common()[0]) # extract most common number そこでpをいくつまで生成すればいいか考えてみました。 pythでpを$10^6$まで生成して、p以前のの最大値pmaxとの比率p/pmaxの最小値rを求めます。数が大きくなっていくと0.5に収束していくようです。したがって1000以下のpをすべて生成するためには2倍の2000になるまでという条件で良さそうです。たぶん証明すれば出来るのでしょうが、誰か出来たら教えてください。 pgen = pyth() p, pmax, r = 0, 1, 1 while p < 10**6: p = sum(next(pgen)) if p>pmax: pmax=p if (p/pmax) < r: r = (p/pmax) print(f"Minimum s/smax ratio = {r}") #Minimum s/smax ratio = 0.5035145516710452 (開発環境:Google Colab)
- 投稿日:2022-01-19T11:17:09+09:00
Jupyter Labで不要なショートカットキーを無効化する
経緯 Jupyter Labをvimのキーバインドで操作するため「@axlair/jupyterlab_vim」を利用していたが、ある時からEscキーを押すとセルの移動モード(Commandモード)に切り替わるようになってしまったため、Vimでの操作に支障が出るようになった。 概要 Jupyter Labデフォルトの「EscキーでCommandモードに切り替え」というショートカットを無効化し、jupyterlab_vimのEscキーに割り当てられた以下のショートカットを有効化する。 Esc:ノーマルモードへの切り替え Shift + Esc: Jupyter LabのCommandモードへの切り替え 設定方法 Jupyter Labのメニューから「Settings > Advanced Settings Editor」を開く。 「Keyboard Shortcuts」タブを開き、「User Preferences」に以下の通りデフォルトのEscキーに割り当てられたショートカットを無効化するための設定を入力する。入力後は右上の保存ボタンを押す。 { "shortcuts": [ { "command": "notebook:enter-command-mode", "keys": [ "Escape" ], "selector": ".jp-Notebook.jp-mod-editMode", "disabled": true }, ] } 3. 編集画面をリロードし、jupyter_vim側のショートカットキーが有効化されていることを確認する。
- 投稿日:2022-01-19T11:13:03+09:00
【Django】SNS app作成手順03-ログインページの作成
—————login.html作成---------- {% extends 'base.html' %} {%block content%} {{context}} <main class="form-signin"> <form method='POST'>{% csrf_token %} <h1 class="h3 mb-3 fw-normal">Please login</h1> <div class="form-floating"> <input type="text" class="form-control" id="floatingInput" name='username' placeholder="name@example.com"> <label for="floatingInput">username</label> </div> <div class="form-floating"> <input type="password" class="form-control" id="floatingPassword" name='password' placeholder="Password"> <label for="floatingPassword">Password</label> </div> <button class="w-100 btn btn-lg btn-primary" type="submit">login</button> <p class="mt-5 mb-3 text-muted">© 2017–2021</p> </form> </main> {%endblock content%} —————views.pyへ追記---------- def loginfanc(request): if request.method == 'POST': username = request.POST['username'] password = request.POST['password'] user = authenticate(request,username = username,password = password) if user is not None: login(request,user) return render(request,'login.html',{'context':'login'}) else: return render(request,'login.html',{'context':'notlogin'}) return render(request,'login.html',{'context':'get method’}) —————urls.pyへ追記---------- path('login/', loginfanc, name = 'login'),
- 投稿日:2022-01-19T10:57:22+09:00
[py2rb] 辞書の値が関数
はじめに 移植やってます。 ( from python 3.7 to ruby 2.7 ) 辞書の値が関数 (Python) def a(k, v): print(k, v) def b(k, v): print(k, v) h = {'func a': a, 'func b': b} h['func a']('A', 1) h['func b']('B', 2) # A 1 # B 2 辞書の値に関数を入れておいて、引数を渡します。 動的プログラミングですねえ。 失敗 (Ruby) def a(k, v) p [k, v] end h = {'func a' => a} main.rb:1:in `a': wrong number of arguments (given 0, expected 2) (ArgumentError) ハッシュにメソッドを入れた時点でエラーになります。 成功 lambda (Ruby) a = lambda { |k, v| p [k, v] } b = lambda { |k, v| p [k, v] } h = {'func a' => a, 'func b' => b} h['func a'].call('A', 1) h['func b'].call('B', 2) # ["A", 1] # ["B", 2] lambdaだと成功します。 成功 method (Ruby) def a(k, v) p [k, v] end def b(k, v) p [k, v] end h = {'func a' => 'a', 'func b' => 'b'} method(h['func a']).call('A', 1) method(h['func b']).call('B', 2) # ["A", 1] # ["B", 2] defのままで行いたい場合は、methodを使用します。 メモ Python の 辞書の値が関数 を学習した 百里を行く者は九十里を半ばとす
- 投稿日:2022-01-19T09:44:13+09:00
[py2rb] lambda
はじめに 移植やってます。 ( from python 3.7 to ruby 2.7 ) lambda (Python) _identity = lambda x, **kw: x いわゆる無名関数ですね。 lambda (Ruby) _identity = ->(x, **kw) { x } # or _identity = lambda { |x, **kw| x } lambdaとProcの違いの一つに引数の数が違っている場合があげられますが、可変長引数を使用するとエラーにならない様です。 identity2 = ->(x, **kw) { x } puts identity2.call(2) # 2 identity1 = ->(x, kw) { x } puts identity1.call(1) main.rb:5:in `block in <main>': wrong number of arguments (given 1, expected 2) (ArgumentError) メモ Python の lambda を学習した 道のりは遠そう
- 投稿日:2022-01-19T01:54:36+09:00
簡単な対話bot作ってみた
はじめに この記事は、SLP KBIT AdventCalendar2021 21日目の記事です。(物凄く遅れました…) Python学習のチュートリアルとして簡単な対話botを作りました。 (といっても会話が支離滅裂なので期待しているようなbotではありません) 目次 ・環境 ・コードの解説 ・実行結果 ・おわりに 環境 Python 3.9.7 Spyder コードの解説 responder.pyはユーザーの入力した文字列に対して反応を示すためのプログラムです。あらかじめユーザーに返すフレーズをリストにまとめて置き、それをランダムに出力することで会話をしているように見せています。 responder.py import random class Responder(object): def __init__(self, name): self.name = name def response(self, input): return '' class RepeatResponder(Responder): # オウム返しのためのサブクラス def response(self, input): return '{}ってなに?'.format(input) class RandomResponder(Responder): #ランダムな応答のためのサブクラス def __init__(self, name): super().__init__(name) self.responses = ['おなかすいた', 'ねむい', 'ご飯作って', '元気だねー', 'いい天気だね', 'お疲れ様ー'] def response(self, input): return (random.choice(self.responses)) pityna.py import responder class Pityna(object): #本体クラス def __init__(self, name): self.name = name self.responder = responder.RandomResponder('Random') def dialogue(self, input): #response()を呼び出して入力した文字列を取得 return self.responder.response(input) def get_responder_name(self): # responderに格納されているオブジェクト名を戻り値にする return self.responder.name def get_name(self): return self.name 実行結果 実行してみると下の様な会話をすることができました。しかし、会話をしているように見えてもリストからランダムにフレーズを取り出しているだけなのでぎこちないですね。 おわりに 作業中にエラーが大量発生したことでなかなか進みませんでした… 今回はbotとの会話が少しぎこちない状態で終わりましたが、この後も本を読み進めながら学習をして会話がちゃんと成立するぐらいのbotを作りたいと思います。 最後まで記事を見ていただきありがとうございました。 参考にした本 ・金城 俊哉『Pythonプログラミングパーフェクトマスター』(秀和システム、2020)
