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

Python_デコレーター

デコレーターの書き方 from functools import wraps def デコレーター名(func): @wraps(func) # なくても動くがある方よい def wrapper(*args, **kwargs): # どんな引数をとってもいいようにすると良い # デコレーターとしての機能を記述 return func(*args, **kwargs) # funcの実行結果を返す return wrapper デコレーターの使い方 @デコレーター名 def デコレーターを使用する関数(): 処理を書く # 実行 デコレーターを使用する関数() 実行時間を計測するデコレーター例 import time from functools import wraps def stop_watch(func): @wraps(func) def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) print(f"実行時間(sec):{time.time()-start:2f}") return result return wrapper @stop_watch def my_sleep(sec): time.sleep(sec) print(f"{sec}秒待ちました") my_sleep(10) 10秒待ちました 実行時間(sec):10.016692
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

仮想環境でDjangoを動かすンゴ

この記事で書くこと 使用するPythonバージョンを切り分ける(pyenv) Pythonをインストールする 仮想環境を作成する(venv) Djangoをインストールする 使用環境 OS:macOS Big Sur ver11.0.1 使用するシェル:bash pyenv:2.2.2 python:3.10.0 Django:3.2.9 ※homebrewを使用しますが、ここでは解説しないので良い感じにアップデート推奨です。 1. 使用するPythonバージョンを切り分ける(pyenv) pyenvを使って、使いたいPythonバージョンを用意します。 今使ってるpythonバージョンで問題ない場合は不要です。 pyenvをインストールする brew install pyenv インストール後、もろもろの設定をします。 自分の場合、Mac ✖️ bashを使用しているため、以下の設定を行いました。 echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile echo 'eval "$(pyenv init --path)"' >> ~/.bash_profile echo 'if [ -n "$PS1" -a -n "$BASH_VERSION" ]; then source ~/.bashrc; fi' >> ~/.bash_profile echo 'eval "$(pyenv init -)"' >> ~/.bashrc 上記の設定については詳しくはこちらに記載されています。 https://github.com/pyenv/pyenv#basic-github-checkout 設定したらbash_profileを更新します source ~/.bash_profile 2. Pythonをインストールする pyenvで使用できるpythonバージョン一覧を確認します pyenv install -l バージョンを指定しインストールを行います。 pyenv install 3.10.0 終わったら、インストールされたpythonバージョン一覧を確認します pyenv versions 使用したいpythonバージョンを設定します(ローカル設定もできるけどここではグローバル設定で) pyenv global 3.10.0 python --verison で指定バージョンに切り替わっていればOKです。 切り替わらない場合はpyenvインストール後の設定が何かおかしいかもしれません。 (※最近のMacOSだとデフォルトのシェルがbashからzshになってたり。) 3. 仮想環境を作成する(venv) 任意の場所に仮想環境を作ります。 「env1」の部分は今回作る仮装環境の名前です。 python -m venv <任意の場所>/env1 作成した仮想環境を有効化します。 source <任意の場所>/env1/bin/activate ※ユーザ名の前に(env1) がついたら仮想環境が有効になった状態です。 4. Djangoをインストールする Djangoをインストールします pip install pip install Django==3.2.9 インストールされたかを確認します python >>> import django >>> print(django.get_version()) インストールしたDjangoのバージョンが表示されていればOKです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

自分用 備忘録

right(engineer)です。 今回は、自分用の備忘録をまとめる。 JupyterLabの起動方法 ターミナルに以下コマンドを入力する。 $ jupyter lab
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】2つのPDFファイルを交互に結合する

はじめに 最近,A3,B4をスキャンできるスキャナを購入したが, 両面同時スキャン対応していなかった (´;ω;`) 両面プリントの束をスキャンするときに, 面倒だが片面ずつスキャンしてPDFファイルをマージする必要がある 10枚の両面プリントをスキャンしたとき(A:表面,B:裏面,数字は枚目) 1回目のスキャンでできたPDF:1A,2A,3A,・・・,10A 2回目のスキャンでできたPDF:1B,2B,3B,・・・,10B これを, マージしたPDF:1A,1B,2A,2B,・・・,10A,10B とする必要がある プログラム設計 書くべきほどのことではないが,簡単に書くとこんな感じであろう 引数は,結合する2つのファイル名と出力するファイル名 結合する2つのファイルの存在チェック 結合する2つのページ数が同じかチェック 1ページごと交互に新しいファイルに書いていく PyPDF2 python で PDF ファイルを扱うなら PyPDF2 がよさそうだ インストールは,conda なら conda install PyPDF2 PDFファイルのページ数を得る PdfFileReader の getNumPages を使えばよい PdfFileReader の生成にはファイル名を渡す PdfFileReader(stream, strict=True, warndest=None, overwriteWarnings=True) ## stream: Could also be a string representing a path to a PDF file.(抜粋) getNumPages はただ呼ぶだけでよい getNumPages() つまり,これだけでページ数が取得できる import PyPDF2 reader = PyPDF2.PdfFileReader('src.pdf') page_num = reader.getNumPages() PDFファイルの1ページを得る PdfFileReader の getPage を使えばよい getPage(pageNumber) pageNumber は 0始まりだ つまり,これだけで全ページが取得できる import PyPDF2 reader = PyPDF2.PdfFileReader('src.pdf') page_num = reader.getNumPages() for page_number in range(page_num): page = reader.getPage(page_number) PDFファイルに出力する PdfFileWriter を生成し,addPage でページを追加,write で PDF へ書き込む addPage,write は次のようで addPage(page) write(stream) addPage の page には PdfFileReader.getPage で取得した page を渡す write の stream はファイルオブジェクトを渡す 以下のソースで,src.pdf が dest.pdf にコピーされる copy_pdf.py import PyPDF2 reader = PyPDF2.PdfFileReader('src.pdf') page_num = reader.getNumPages() writer = PyPDF2.PdfFileWriter() for page_number in range(page_num): page = reader.getPage(page_number) writer.addPage(page) with open('dest.pdf', 'wb') as f: writer.write(f) 2つのPDFファイルを交互に結合する two_file_merge.py import os import argparse import PyPDF2 as pp # 引数の解析 parser = argparse.ArgumentParser() parser.add_argument('file1', help='元ファイル1', type=str) parser.add_argument('file2', help='元ファイル2', type=str) parser.add_argument('--output', '-o', help='出力ファイル名', type=str, default='output.pdf') arg = parser.parse_args() # 引数 file1 = arg.file1 file2 = arg.file2 output = arg.output # 引数のチェック if not os.path.exists(file1): print(f'{file1}が存在しません') quit() if not os.path.exists(file2): print(f'{file2}が存在しません') quit() reader1 = pp.PdfFileReader(file1) reader2 = pp.PdfFileReader(file2) if reader1.getNumPages() != reader2.getNumPages(): print('ページ数が異なります') quit() # 1ページずつ交互に新しいファイルに書き込む writer = pp.PdfFileWriter() for page in range(reader1.getNumPages()): writer.addPage(reader1.getPage(page)) writer.addPage(reader2.getPage(page)) with open(output, 'wb') as f: writer.write(f) おわりに 便利だな,PyPDF2 PDFファイルを扱うことが多いので,これから活用していきたい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonで書かれたコードの実行時に、別途用意したファイルを標準入力から入力する

n版煎じですみません。 pythonで書かれたコードの実行時に、標準入力から別途用意したファイルを入力する方法メモ。 以下のようなコードがある場合 test.py #!/bin/python3 line1 = input() line2 = input() print(line1) print(line2) 何もせずに実行すると以下のようにCUIからの標準入力待ちになる。 $ python3 test.py this_is_line1 ★入力する this_is_line2 ★入力する this_is_line1 ★出力される this_is_line2 ★出力される 以下のようにすると別途用意したファイル(input.txt)を標準入力から入力できる。 $ python3 test.py < input.txt this_is_line1 this_is_line2 input.txt this_is_line1 this_is_line2 追記 (コメントでアドバイス頂いたように) catの出力をパイプで渡した方が覚えやすい気がしています。 $ cat input.txt | python3 test.py this_is_line1 this_is_line2 input.txt this_is_line1 this_is_line2 参考 Pythonでcatみたいに標準入力またはファイル名指定でテキストを読み込む方法
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Mac で Spleeter を使ってみた

はじめに なんとなく、Pythonを使った何かを体験したかったところで、この Spleeter の情報を見かけて、実際に使ってみることにしました。 Spleeterって? Spleeterは、フランスの音楽配信サイトである Deezerが開発した、AI を利用して、音楽をパート別を分離するアプリケーションソフトです。 任意の楽曲をこのアプリに読み込ませるとボーカル、ベース、ドラムなどのパートに分離した音声ファイルを出力することができます。 最近はご無沙汰なのですが、DTMの打ち込みをやるにあたり、耳コピは非常につらいものがありますので、これが少しでも低減できるのは嬉しいものです。 Spleeterの導入手順 SpleeterはPythonで動作するアプリケーションですので、Pythonの環境を構築してからSpleeterをインストールします。 ここでは、以下のようなmacOSの環境で構築、インストールしました。 項目 OS macOS Big Sur 11.5.2 ハード MacBook Pro (13-inch, 2017, Four Thunderbolt 3 Ports) CPU 3.1GHz デュアルコア Intel Core i5 メモリ 8GByte Homebrewのインストール まず、Homebrewをインストールします。すでにインストールされて場合はこれをスキップしてください。 ①ターミナルを起動します。 ②Homebrewのページに行き、ここに表示されるコマンドをコピー&ペーストします。 % /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" ==> Checking for `sudo` access (which may request your password)... Password: ==> This script will install: /usr/local/bin/brew /usr/local/share/doc/homebrew /usr/local/share/man/man1/brew.1 /usr/local/share/zsh/site-functions/_brew /usr/local/etc/bash_completion.d/brew /usr/local/Homebrew ==> The following new directories will be created: /usr/local/Cellar /usr/local/Caskroom Press RETURN to continue or any other key to abort: ※Press RETURN to continue or any other key to abort: では Retern(Enter)キーを押下します。 ==> /usr/bin/sudo /bin/mkdir -p /usr/local/Cellar /usr/local/Caskroom ==> /usr/bin/sudo /bin/chmod ug=rwx /usr/local/Cellar /usr/local/Caskroom ==> /usr/bin/sudo /usr/sbin/chown katz /usr/local/Cellar /usr/local/Caskroom ~~~ 中略 ~~~ Error: homebrew-core is a shallow clone. homebrew-cask is a shallow clone. To `brew update`, first run: git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core fetch --unshallow git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask fetch --unshallow These commands may take a few minutes to run due to the large size of the repositories. This restriction has been made on GitHub's request because updating shallow clones is an extremely expensive operation due to the tree layout and traffic of Homebrew/homebrew-core and Homebrew/homebrew-cask. We don't do this for you automatically to avoid repeatedly performing an expensive unshallow operation in CI systems (which should instead be fixed to not use shallow clones). Sorry for the inconvenience! Failed during: /usr/local/bin/brew update --force --quiet ③エラーが出てしまいました。エラ〜メッセージにしたがってコマンドを実施します。 % git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core fetch --unshallow remote: Enumerating objects: 563045, done. remote: Counting objects: 100% (563035/563035), done. remote: Compressing objects: 100% (199938/199938), done. remote: Total 554214 (delta 357072), reused 548563 (delta 351428), pack-reused 0 Receiving objects: 100% (554214/554214), 201.13 MiB | 8.05 MiB/s, done. Resolving deltas: 100% (357072/357072), completed with 4197 local objects. From https://github.com/Homebrew/homebrew-core 7d8991e9851..c17ee8e4b64 master -> origin/master % git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask fetch --unshallow remote: Enumerating objects: 351943, done. remote: Counting objects: 100% (351936/351936), done. remote: Compressing objects: 100% (103184/103184), done. remote: Total 345455 (delta 246585), reused 340583 (delta 241717), pack-reused 0 Receiving objects: 100% (345455/345455), 137.32 MiB | 7.88 MiB/s, done. Resolving deltas: 100% (246585/246585), completed with 3252 local objects. From https://github.com/Homebrew/homebrew-cask 56c5e308ba..531e6ba041 master -> origin/master ④②を再実行します。 % /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" ==> Checking for `sudo` access (which may request your password)... Password: ==> This script will install: /usr/local/bin/brew /usr/local/share/doc/homebrew /usr/local/share/man/man1/brew.1 /usr/local/share/zsh/site-functions/_brew /usr/local/etc/bash_completion.d/brew /usr/local/Homebrew Press RETURN to continue or any other key to abort: ==> /usr/bin/sudo /usr/sbin/chown -R katz:admin /usr/local/Homebrew ==> Downloading and installing Homebrew... Updating files: 100% (2716/2716), done. HEAD is now at 4c3f32534 Merge pull request #12482 from Bo98/zero_ar_date Updated 2 taps (homebrew/core and homebrew/cask). ==> Installation successful! ==> Homebrew has enabled anonymous aggregate formulae and cask analytics. Read the analytics documentation (and how to opt-out) here: https://docs.brew.sh/Analytics No analytics data has been sent yet (nor will any be during this install run). ==> Homebrew is run entirely by unpaid volunteers. Please consider donating: https://github.com/Homebrew/brew#donations ==> Next steps: - Run brew help to get started - Further documentation: https://docs.brew.sh ⑤Homebrewのバージョン確認によりインストール完了を確認します。 % brew -v Homebrew 3.3.5 Homebrew/homebrew-core (git revision 48bd7e9937c; last commit 2021-11-28) Homebrew/homebrew-cask (git revision 531e6ba041; last commit 2021-11-28) pyenvのインストール pyenv は、システム上に複数バージョンのPythonを同居させて使い分けられるようにするものです。Pythonを扱う際にインストールしておくと何かと便利なようです。 ① Homebrew で pyenv をインストールします。 % brew install pyenv Updating Homebrew... ==> Auto-updated Homebrew! Updated 1 tap (homebrew/core). ==> Updated Formulae Updated 2 formulae. Error: The following directories are not writable by your user: /usr/local/share/zsh /usr/local/share/zsh/site-functions You should change the ownership of these directories to your user. sudo chown -R $(whoami) /usr/local/share/zsh /usr/local/share/zsh/site-functions And make sure that your user has write permission. chmod u+w /usr/local/share/zsh /usr/local/share/zsh/site-functions エラーが出てしまいました。 権限などが不足しているようです。 エラーメッセージにしたがって追加します % sudo chown -R $(whoami) /usr/local/share/zsh /usr/local/share/zsh/site-functions Password: % chmod u+w /usr/local/share/zsh /usr/local/share/zsh/site-functions ③あらためて①を実行します。 % brew install pyenv ==> Downloading https://ghcr.io/v2/homebrew/core/m4/manifests/1.4.19 ######################################################################## 100.0% ~~~ 中略 ~~~ ==> Installing pyenv ==> Pouring pyenv--2.2.2.big_sur.bottle.tar.gz ? /usr/local/Cellar/pyenv/2.2.2: 834 files, 2.8MB ④pyenvがインストールできたか確認します。 % pyenv -v pyenv 2.2.2 ⑤シェルの環境変数などの定義を追加します。 % echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc % echo 'export PATH="$PYENV_ROOT/shims:$PATH"' >> ~/.zshrc % echo 'eval "$(pyenv init -)"' >> ~/.zshrc % source ~/.zshrc ※export PATH="$PYENV_ROOT/shims:$PATH" の部分ですが、古いPythonバージョンでは export PATH="$PYENV_ROOT/bin:$PATH" のようです。この後に手順にある Pythonバージョンの切り替えがうまくいかない場合はこの部分を見直してみてください。 Pythonのインストール Pythonをインストールします。Spleeterは、2021年11月28日現在、Python3.8.xまでにしか対応していないようなので、Python3.8の最新版をインストールします。 ①Pythonをインストールします。 % pyenv install 3.8.12 python-build: use openssl@1.1 from homebrew python-build: use readline from homebrew Downloading Python-3.8.12.tar.xz... ~~~ 中略 ~~~ python-build: use zlib from xcode sdk Installed Python-3.8.12 to /Users/katz/.pyenv/versions/3.8.12 ②pythonコマンドで使用するPythonバージョンを、インストールしたバージョンに設定します、 % pyenv global 3.8.12 % python -V Python 3.8.12 ffmpegのインストール ffmpegは、動画や音声を加工するのに強力で万能なツールアプリケーションです。 ①Homebrewを使って ffmpeg をインストールします。 % brew install ffmpeg Updating Homebrew... ==> Auto-updated Homebrew! Updated 1 tap (homebrew/core). ==> Updated Formulae Updated 5 formulae. ==> Downloading https://ghcr.io/v2/homebrew/core/brotli/manifests/1.0.9 ######################################################################## 100.0% ==> Downloading https://ghcr.io/v2/homebrew/core/brotli/blobs/sha256:9d3009fd246d0f6cf9fd11d0a3bd388f6c043c75fa302decf0dd935163fb0f4b ~~~ 中略 ~~~ ==> Installing ffmpeg ==> Pouring ffmpeg--4.4.1_3.big_sur.bottle.tar.gz ? /usr/local/Cellar/ffmpeg/4.4.1_3: 276 files, 51MB Spleeterのインストール ここまででようやく Spleeter インストールの準備ができましたので、Spleeterをインストールします。 ①pip を利用して Spleeter をインストールします。 % pip install spleeter Collecting spleeter Downloading spleeter-2.3.0-py3-none-any.whl (51 kB) |████████████████████████████████| 51 kB 3.7 MB/s Collecting llvmlite<0.37.0,>=0.36.0 Downloading llvmlite-0.36.0-cp38-cp38-macosx_10_9_x86_64.whl (18.5 MB) |████████████████████████████████| 18.5 MB 3.6 MB/s ~~~ 中略 ~~~ Successfully installed absl-py-0.15.0 anyio-3.4.0 appdirs-1.4.4 astunparse-1.6.3 audioread-2.1.9 cachetools-4.2.4 certifi-2021.10.8 cffi-1.15.0 charset-normalizer-2.0.8 click-7.1.2 decorator-5.1.0 ffmpeg-python-0.2.0 flatbuffers-1.12 future-0.18.2 gast-0.4.0 google-auth-2.3.3 google-auth-oauthlib-0.4.6 google-pasta-0.2.0 grpcio-1.34.1 h11-0.12.0 h2-4.1.0 h5py-3.1.0 hpack-4.0.0 httpcore-0.13.7 httpx-0.19.0 hyperframe-6.0.1 idna-3.3 importlib-metadata-4.8.2 joblib-1.1.0 keras-nightly-2.5.0.dev2021032900 keras-preprocessing-1.1.2 librosa-0.8.0 llvmlite-0.36.0 markdown-3.3.6 norbert-0.2.1 numba-0.53.1 numpy-1.19.5 oauthlib-3.1.1 opt-einsum-3.3.0 packaging-21.3 pandas-1.3.4 pooch-1.5.2 protobuf-3.19.1 pyasn1-0.4.8 pyasn1-modules-0.2.8 pycparser-2.21 pyparsing-3.0.6 python-dateutil-2.8.2 pytz-2021.3 requests-2.26.0 requests-oauthlib-1.3.0 resampy-0.2.2 rfc3986-1.5.0 rsa-4.8 scikit-learn-1.0.1 scipy-1.7.3 six-1.15.0 sniffio-1.2.0 soundfile-0.10.3.post1 spleeter-2.3.0 tensorboard-2.7.0 tensorboard-data-server-0.6.1 tensorboard-plugin-wit-1.8.0 tensorflow-2.5.0 tensorflow-estimator-2.5.0 termcolor-1.1.0 threadpoolctl-3.0.0 typer-0.3.2 typing-extensions-3.7.4.3 urllib3-1.26.7 werkzeug-2.0.2 wheel-0.37.0 wrapt-1.12.1 zipp-3.6.0 WARNING: You are using pip version 21.1.1; however, version 21.3.1 is available. You should consider upgrading via the '/Users/katz/.pyenv/versions/3.8.12/bin/python3.8 -m pip install --upgrade pip' command. ②WARNING: You are using pip version 21.1.1; however, version 21.3.1 is available. と pip のバージョンアップをうながされましたので、実施しておきます。 % pip install --upgrade pip Requirement already satisfied: pip in ./.pyenv/versions/3.8.12/lib/python3.8/site-packages (21.1.1) Collecting pip Using cached pip-21.3.1-py3-none-any.whl (1.7 MB) Installing collected packages: pip Attempting uninstall: pip Found existing installation: pip 21.1.1 Uninstalling pip-21.1.1: Successfully uninstalled pip-21.1.1 Successfully installed pip-21.3.1 ③Spleeter がインストールできたか確認します。 % spleeter --version Spleeter Version: 2.3.0 Spleeterを使ってみる Spleeter はコマンドラインを使ってパート分離を行います。 基本的なコマンドオプションは Spleeter にのっておりますのでこれを参照してください。 分離元となる音楽ファイルのフォーマットは特に問わないようです。MP3,AAC,ALAC(Apple Lossless)で分離ができることは確認できました。 ざっくりとしては、下記で分離を行うことができます。 spleeter separate -o 【出力先ディレクトリ】 -p 【分離方法】 【分離を行う音楽ファイル名】 分離方法は下記から選択します。 オプション名 分離方法 spleeter:2stems ボーカル、伴奏に分離 (-p オプションを指定しない場合のデフォルト) spleeter:4stems ボーカル、ベース、ドラム、その他に分離 spleeter:5stems ボーカル、ピアノ、ベース、ドラム、その他に分離 【spleeter:5stemsでの実行例】 % spleeter separate -o ~/output -p spleeter:5stems ~/TestMusic/AloneAgain.m4a INFO:spleeter:Downloading model archive https://github.com/deezer/spleeter/releases/download/v1.4.0/5stems.tar.gz INFO:spleeter:Validating archive checksum INFO:spleeter:Extracting downloaded 5stems archive INFO:spleeter:5stems model file(s) extracted INFO:spleeter:File /Users/katz/output/AloneAgain/piano.wav written succesfully INFO:spleeter:File /Users/katz/output/AloneAgain/vocals.wav written succesfully INFO:spleeter:File /Users/katz/output/AloneAgain/bass.wav written succesfully INFO:spleeter:File /Users/katz/output/AloneAgain/drums.wav written succesfully INFO:spleeter:File /Users/katz/output/AloneAgain/other.wav written succesfully % ls -l ~/output/AloneAgain total 381440 -rw-r--r--@ 1 katz staff 38780750 11 28 20:49 bass.wav -rw-r--r--@ 1 katz staff 38780750 11 28 20:49 drums.wav -rw-r--r--@ 1 katz staff 38780750 11 28 20:49 other.wav -rw-r--r--@ 1 katz staff 38780750 11 28 20:49 piano.wav -rw-r--r--@ 1 katz staff 38780750 11 28 20:49 vocals.wav ※分離方式の初回実行時にはモデルのアーカイブファイルをダウンロードするため少し時間がかかります。 どこまで分離できる? 手持ちの色々なジャンル(J-POP,Rock,Jazz,Fusion,Eurobeat,Dance,)の曲でspleeter:5stemsとspleeter:2stemsを試してみました。 細かい評価は省略しますが、総合的には以下の傾向でした。 ボーカルはかなり明瞭に分離できる。 spleeter:2stemsの伴奏部分(=カラオケ状態)は、ラジカセ等のボーカルカット機能よりもかなり明瞭にボーカルカットが可能。 ドラムもかなり明瞭に分離できる。 ベースは総じて聞き取りづらい。 ピアノはソロパートは聞き取りやすいがバッキングはかなり聞き取りづらい。たまにその他のパートに入ることがある。 あとがき ゆっくり検証する時間がとれないのですが、もう少しいろいろ検証してみたいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SWIGで C++ の opencv コードのPythonラッパーを作成

SWIGインストール Archlinuxの場合 sudo pacman -S swig ビルド 参考 swig-and-c-shared-library ラッパーファイル生成 OpenCVをインクルードしてるmylib.hに対してmylib.iを用意 <string>など標準ライブラリをインクルードしてるならその対応の.iも入れておく (string-arguments-are-not-recognized-by-swig) %module mylib // Make mylib_wrap.cxx include this header: %{ #include "mylib.h" %} %include <std_string.i> %include "mylib.h" .iをコンパイルして swig -c++ -python mylib.i mylib_wrap.cxx,mylib.pyが生成される コンパイルとリンク 生成された.cxxファイルをコンパイルして Python と Opencv をリンクする mylib.pyはimport _mylibでネイティブを呼び出すのでオブジェクトファイルの名前は _mylib.so にする g++ -shared -fPIC -L. mylib_wrap.cxx -o _mylib.so `pkg-config --libs --cflags python3` `pkg-config --libs --cflags opencv4` 試しにpythonで呼び出す import mylib a=mylib.SomeClass()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ラズパイで子供の寝相監視をした時のログと、参考にした記事のまとめ【前編】

はじめに もうすぐ2歳になる子供が家に滞在していたので、せっかくなので寝相をとって動画にして、みんなで楽しむことにした。 そのときに行ったことのまとめ。 使用機器 項目 内容 ラズパイ Rapsberry Pi4 4GBモデル カメラ よくある暗視カメラ SDカード容量 32GB 初期設定 ラズパイを買ったのは相当昔で、初期設定自体は何を参考にしていたかは忘れてしまった。 特筆するようなことはしていないので、このページのような記事を参考にしておけば良さそうである。 方針 今回は次のような方針で動画を作成することにした。 Pythonでカメラを制御し、一定の間隔で画像を撮影するコードを作る 遠隔制御し、1のコードを起動する 撮影した動画をメインの環境(Windows)にコピーする メイン環境にて動画化 みんなで楽しむ 記事が長くなりそうだったので、2までで区切ることにする カメラ制御方法 Pythonでカメラを制御する時のサンプルは公式が一番わかり易いと思う。 Pythonコードでカメラモジュールをコントロールする方法を参考にして、まずはカメラの動作確認をしたところ、かなりぼやけた内容の画像が取れていた。 今回のカメラモジュールの場合、レンズの部分がネジ式になっていて、手動で焦点距離を変えられるようだった。 ピントが合う部分を把握してから、レンズの部分を回転させてピントの調整を行おう。 ピント調整なども含めたコードは下記のとおりになった。 camera.py from picamera import PiCamera from time import sleep import datetime DEBUG = False TIMESPAN = 2 WAIT = TIMESPAN / 5 with PiCamera() as camera: if DEBUG: camera.start_preview() _ = input() camera.stop_preview() check_time = datetime.datetime.now() count = 0 while check_time.hour >= 20 or check_time.hour <= 6: while datetime.datetime.now().second != (check_time.second + TIMESPAN) % 60: sleep(WAIT) check_time = datetime.datetime.now() camera.capture(f"{check_time:%d%H%M%S}.jpg") count += 1 print(count) if DEBUG and count > 100: break 保存ファイル名を日付を含めている(check_time:%d%H%M%Sとしている)のは、 時間までだとファイル名をソートしたときに24時のファイルが一番最初に来てしまうから。 月をまたぐことはなかったのと、時間を簡単に確認できるようにしておく方が 動画を見ながら話すときにやりやすいとおもってUNIX時間の採用はしなかった。 理論上、一度の撮影に1秒以上かかると1分待たされる可能性が出てくるが、事前検証(GUI上で一晩動かして見てのテスト)では問題なかったためこのまま使用した。 なお、事前検証では撮影間隔を1秒にしていたが、32GBのSDでは容量が足りなかった。 これの対策について色々検証したが最終的に撮影間隔を2秒にする対応に落ち着いた。 検証記録は別途記事にまとめることにする。 遠隔操作によるコード起動 遠隔操作によるコードの起動方法はいくつかあるが、TeratermでSSHでラズパイに接続するのが一番手軽そうだった。 とりあえずTeratermを落としても実行し続ける方法を確認してから、ソースコードのあるフォルダに移動して、sudo nohup python camera.pyをすると…何が起きたかはよくわからないが一瞬で終了した。 (※後で知ったが、最後に&をつけるとバックグラウンドで実行できるようだった。 今回はコードを実行することだけが目的だったことと異常終了の検知ができたことからフォアグラウンド実行でも問題は出なかった。) cat nohup.logすると、次のような表記。 File "camera.py", line 25 camera.capture(f"{check_time:%d%H%M%S}.jpg") ^ SyntaxError: invalid syntax IDEで作業していたPythonでは対応していた新しい記法が使えないPythonを使おうとしていたようだ。 試しにpythonしてみると…? Python 2.7.16 (default, Oct 10 2019, 22:02:15) [GCC 8.3.0] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> 今後pythonを使う場合はPython3系しか使わないと思うので、対応方法の記事を参考にして、pythonをpython3に紐づけておいた。 もう一度実行して翌朝、ls | wc -lでファイル数を調べることによって、期待通りのファイルが出力されていることを確認した。 記事が長くなりそうなため、一旦ここで区切ることにする。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pyhon line-bot-sdkによるLine Bot備忘録

Pyhon line-bot-sdkによるLine Bot備忘録 Line Bot応答の概要  Lineユーザーが公式Lineアカウントへメッセージを送り、Messaging APIがそれを受け取る。Messaging APIに登録しているWebhook URL(Bot server)へHTTPS POSTリクエストを送る。受け取ったリクエストの"Header"の著名と"Body"をサーバーに登録している秘密鍵によってエンコードされた著名が一致するかどうか検証する。承認されればBotが応答し、その結果をMessaging APIへ返す。Messaging APIから公式Lineアカウントへ返す。 事前登録 Line APIを使うため、Python Developpersに登録 Bot severを使うため、Herokuに登録 Herokuにオリジナルプログラムを連携させるため、git hubに登録 Herokuに連携したgit hubレポジトリに以下のファイルを置く。 Line_Bot.py from flask import Flask, request, abort from linebot import ( LineBotApi, WebhookHandler ) from linebot.exceptions import ( InvalidSignatureError ) from linebot.models import ( MessageEvent, TextMessage, TextSendMessage, ) app = Flask(__name__) line_bot_api = LineBotApi('YOUR_CHANNEL_ACCESS_TOKEN') handler = WebhookHandler('YOUR_CHANNEL_SECRET') @app.route("/") def check(): return "successfully" @app.route("/callback", methods=['POST']) def callback(): # get X-Line-Signature header value signature = request.headers['X-Line-Signature'] # get request body as text body = request.get_data(as_text=True) app.logger.info("Request body: " + body) # handle webhook body try: handler.handle(body, signature) except InvalidSignatureError: print("Invalid signature. Please check your channel access token/channel secret.") abort(400) return 'OK' @handler.add(MessageEvent, message=TextMessage) def handle_message(event): line_bot_api.reply_message( event.reply_token, TextSendMessage(text=event.message.text)) if __name__ == "__main__": app.run() Procfile web: python Line_Bot.py requirements.txt Flask==2.0.2 line-bot-sdk==2.0.1 runtime.txt python-3.9.9 HerokuでGit hubレポジトリと連携し、Deploy Branchをする 正常にDeployされた場合は、Viewでweb pageを開くと"successfully"と表示される。 Line DevelopersにWebhookを紐づける Line DevelopersでWebhook URLに以下を登録し、Webhookを有効にする。 https://*****.herokuapp.com/callback ※*****はHerokuのアプリ名 完成 送ったLineメッセージがオウム返しされたら、成功。 以下の箇所を変更することで、応答の仕方を変更することができる。今回は受け取ったメッセージテキストをそのまま返す処理が書かれている。 @handler.add(MessageEvent, message=TextMessage) def handle_message(event): line_bot_api.reply_message( event.reply_token, TextSendMessage(text=event.message.text)) 自動会話AIを使って、自然な会話をすることもできる。以下はリクルートのAIを使ったコードである。 https://a3rt.recruit.co.jp/product/talkAPI/ TALKAPI_KEY = 'YOUR_API_KEY' def talkapi(text): url = 'https://api.a3rt.recruit.co.jp/talk/v1/smalltalk' req = requests.post(url, {'apikey':TALKAPI_KEY,'query':text}, timeout=5) data = req.json() if data['status'] != 0: return data['message'] rep = data['results'][0]['reply'] return rep @handler.add(MessageEvent, message=TextMessage) def handle_message(event): tmp_text = event.message.text rep = talkapi(tmp_text) line_bot_api.reply_message( event.reply_token, TextSendMessage(text=rep)) 参考Link
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS+ServerlessFrameworkを使ってPythonバックエンドを構築する

ここしばらく個人でスマホアプリを作ってみたいと思い色々とバックエンド側の構築方法を探っていました。Amplifyを使おうかと思っていたのですが、Flutterではdatastore周りが少し安定していない感がありまして、噂に聞いていたServerlessFrameworkを使ってみることにしました Flutter的にはバックエンドはFirebaseがメジャーだと思いますが、私のスキルセット的にAWS、LambdaもPythonで構築することにしました この記事でできること ServerlessFramework、AWSを使ったバックエンドの構築 APIGateway + Lambda + S3 + DynamoDBの構成 APIKeyを使ったREST APIのアクセス管理 S3やDynamoDBに対する、アクセスロールの設定 ルートディレクトリに集まりがちなfunctionを関数ごとにディレクトリ分け と、バックエンドを構築するときの一通りのことができるのではないかと思います。また、ServerlessFrameworkそのものの導入方法は割愛します。すでに詳しく解説されている記事がありますので 環境 Framework Core: 2.57.0 Plugin: 5.4.4 SDK: 4.3.0 Components: 3.16.0 初期化 % serverless create --template aws-python3 --name hello-world <<< Serverlessサービスを作る ServerlessFrameworkはサービスという単位で環境を作るそうです。上記のコマンドひとつで、Lambda1つを含む基本的なテンプレートが生成されます serverless.yml service: hello-world frameworkVersion: '2' provider: name: aws runtime: python3.8 lambdaHashingVersion: 20201221 functions: << これ以下を消すと、作ったLambdaを削除することもできる hello: handler: handler.hello <<< hander.pyとの紐付け handler.py import json def hello(event, context): body = { "message": "Go Serverless v1.0! Your function executed successfully!", "input": event } response = { "statusCode": 200, "body": json.dumps(body) } return response このテンプレートをベースにガリガリ触っていきます。ちなみに デフォルトだとバージニア北部リージョンにできる Lambdaは[サービス名]-[環境変数]-[serverless.ymlで指定した名前]でできる 実行ログのCloudWatch吐き出しもデフォルトで設定できるようである Amplifyと比べて、余計なファイルもできないし、デプロイもほぼ待ち時間はありません。全体的にシンプルで良いですね APIGateway > Lambda > DynamoDBの流れを作る serverless.yml service: hello-world frameworkVersion: '2' provider: name: aws runtime: python3.8 lambdaHashingVersion: 20201221 # ///////// ↓DynamoDBへのアクセス権限を追加↓ ///////////# iam: role: statements: - Effect: 'Allow' Action: - 'dynamodb:*' Resource: - "arn:aws:dynamodb:us-east-1:xxxxxxxxxx:table/*" # xxxxxxxxにはAWSのアカウント番号が入る # ///////// ↑DynamoDBへのアクセス権限を追加↑ ///////////# functions: hello: handler: handler.hello # ////////// ↓ここから下でAPIGatewayとDynamoDBを追加↓ ///////// # events: - httpApi: path: /users/create method: get resources: Resources: DynamoDbTable: Type: 'AWS::DynamoDB::Table' Properties: AttributeDefinitions: - AttributeName: id AttributeType: S - AttributeName: name AttributeType: S KeySchema: - AttributeName: id KeyType: HASH - AttributeName: name KeyType: RANGE ProvisionedThroughput: ReadCapacityUnits: 1 WriteCapacityUnits: 1 TableName: hello-dynamodb この時、Lambdaからはboto3を使ってDynamoDBにアクセスします handler.py def hello(event, context): dynamodb = boto3.resource('dynamodb') result = dynamodb.Table('hello-dynamodb').put_item( Item = { 'id': "hoge_id", 'name': 'hoge_name' } ) response = { "statusCode": 200, "body": json.dumps('success!') } return response デプロイをして再度実行すると、DynamoDBにも無事レコードが追加されました。Amplifyでは自動でcreated_at, updated_at, versionなどが付加されていましたが、今回は定義したもの以外は全くなく、非常にシンプルでした。 生成されたURLをブラウザで開いてみると、「success!」とだけ表示されました。成功ですね APIKeyを使ったREST APIのアクセス管理 このままだとAPIは全世界に公開されていて、URLを知ってさえいれば誰でもアクセスできてしまいます。これはこれで良いのですが、コール数を管理したり制限するときにはAPI Key(AWS的には使用量プラン)をつけます serverless.yml # 関連しないところは省略しています service: hello-world frameworkVersion: '2' provider: name: aws runtime: python3.8 lambdaHashingVersion: 20201221 stage: dev # api-keyがstageできりかわるようにするために追加 # ////////// ↓全function共通で使用するAPIKeyを追加↓ ////////// # apiGateway: apiKeys: # API Keyの設定 - mobileApp: - name: ${self:provider.stage}-app-key value: # お好みのAPI Key usagePlan: # 使用量プラン - mobileApp: quota: limit: 1000 offset: 0 period: DAY throttle: rateLimit: 100 burstLimit: 100 # ////////// ↑全function共通で使用するAPIKeyを追加↑ ////////// # functions: hello: handler: handler.hello events: - http: # httpApi -> http path: /users/create method: get private: true # ApiKeyをつけたいものにprivateをつける これで画像のようにAPI KeyをHeaderにつけてAPIコールしないと動かなくなります。(API Key無しだとForbidden) S3の作成と、functionごとへにアクセスロールの設定 serverless.yml custom: backetName: hello-serverless-bucket-${self:provider.stage} resources: Bucket: Type: AWS::S3::Bucket Properties: BucketName: ${self:custom.backetName} S3AccessRole: Type: AWS::IAM::Role DependsOn: Bucket Properties: Path: / RoleName: hello-serverless-lambda-role-with-s3 AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: hello-serverless-lambda-policy-with-s3 PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - 'Fn::Join': - ':' - - 'arn:aws:logs' - Ref: 'AWS::Region' - Ref: 'AWS::AccountId' - 'log-group:/aws/lambda/*:*:*' - Effect: "Allow" Action: - "s3:PutObject" - "s3:GetObject" - "s3:ListBucket" Resource: - 'Fn::Join': - '' - - "arn:aws:s3:::" - ${self:custom.backetName} - 'Fn::Join': - '' - - "arn:aws:s3:::" - ${self:custom.backetName} - "/*" LambdaごとにAWSの各リソースに対するアクセス権限を付与しています Bucket名は定数化して環境ごとに切り替わるようにしています hendler.py def hello(event, context): filepath = '/tmp/data.csv' with open(filepath, 'w') as f: writer = csv.writer(f) writer.writerow(["a", "b", "c"]) s3 = boto3.resource('s3') s3.meta.client.upload_file(filepath, 'hello-serverless-bucket-dev', 'hgoe.csv') response = { "statusCode": 200, "body": json.dumps('success!') } return response Lambdaのコードは上記のようになります functionのディレクトリ分けと、外部ライブラリのアップロード functionは数十個になりうるのですが、このままだとServerlessのルートディレクトリに全て並べないといけなくなります。また、Lambdaがデフォルトでサポートしていない外部ライブラリを使う場合にアップロードする方法に迷いました これらを解決してくれるのが、ServerlessFrameworkのライブラリserverless-python-requirementsです。導入方法は公式を参照していただくのが良いかと思います。ちなみにDockerが必要です ディレクトリ構成 / ├── serverless.yml ├── functions/ │ ├── hello/ │ │ ├── handler.py │ │ └── requirement.txt こんな感じでfunctionごとにディレクトリを切ることができます。ちなみに、各ディレクトリ中のrequirement.txtには利用したい外部ライブラリを列挙するのですが、外部ライブラリを使わない場合でも必要です Serverless定義 serverless.yml service: hello-world frameworkVersion: '2' provider: name: aws runtime: python3.8 lambdaHashingVersion: 20201221 stage: dev plugins: # 追加 - serverless-python-requirements # 追加 custom: backetName: hello-serverless-bucket-${self:provider.stage} pythonRequirements: # 追加 dockerizePip: true # 追加 package: # 追加 individually: true # 追加 functions: hello: role: S3AccessRole module: functions/hello # 追加 handler: handler.hello events: - http: path: /users/create method: get private: true まとめ ServerlessFrameworkはやや情報が少ないのか、一度ハマると解決するのが結構大変でした(requirement.txtが必須とか)。今回の記事で、ServerlessFrameworkで、AWSを使って、Pythonを使う場合の基本的な内容はカバーできた気がします
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonを学ぶ際に出てくるAnaconda、Jupyter Notebook、Google Colablatoryなどの違い

はじめに Pythonを学ぶ際に、以下のようなものが最初に出てきます。 Anaconda Jupyter Notebook Google Colablatory IDLE Spyder Visiual Studio Visiual Studio Code 本によって使うものが違ったりいろいろな機能があったりするため、結局何が何なのか、どれを使えば良いのか分からなくなってしまいますよね。 この記事では、それぞれの違いと、どんな特徴があるのかをまとめました。初学者の方の頭の整理になれば幸いです。 分類・特徴 それぞれ分類と特徴はこのようになっています。 名称 分類 特徴 Anaconda Python環境構築サポートソフト 機械学習やデータサイエンス等のライブラリが初めから入っている。プロジェクトごとの環境設定ができる。 Jupyter Notebook IDE(統合開発環境) ブラウザ上で使うことが出来る。Python以外の言語にも対応している。 Google Colablatory IDE ブラウザ上で使うことが出来る。Pythonのみに対応。 IDLE IDE Python用のIDE。Pythonをインストールしたときに付いてくるデフォルトのもの。 Spyder IDE Python用のIDE。Jupyter Notebookほどの機能は無いがシンプル。 Visiual Studio IDE Microsoftが提供するIDE。Python以外の言語にも対応している。 Visiual Studio Code エディタ Microsoftが提供するエディタ。Python以外の言語にも対応している。 Anacondaは少し特殊なため、先にIDEとエディタについて説明します。 IDE(統合開発環境)とは 一言で言えば、これ一つでプログラムを書いて動かすことが出来るソフトのことです。 少し詳しく説明すると、コーディング、コンパイル、デバッグ、テストなどのプログラミングに必要な機能が全てひとまとまりになっているものを言います。 そのため、基本的にはIDEを一つ用意してしまえばプログラムを書き始められるものとなっており、本などでは一番最初に用意を勧められます。 エディタとは 一言で言えば、プログラムを書くことが出来るソフトのことです。 つまり、コーディングの機能のみを持ったものを指します。そのため、エディタは動作が軽く、ストレスなくプログラムを書くことが出来ます。 また、ソフトによっては、後からコンパイル、デバッグ、テストなどの機能を追加することも出来るため、IDEとの明確な区別が出来ないものもあります。 最初に紹介したVSCode(Visiual Studio Code)の他にもエディタはたくさんあります。 Atom vim nano メモ帳(Windows) サクラエディタ 秀丸エディタ いろいろ使ってみて自分のお気に入りのエディタを見つけても良いかもしれません。 それぞれの紹介 Anaconda Anacondaは他のものとは違い、IDEやエディタではありません。Pythonの環境構築をサポートしてくれるソフトです。 具体的には、 Python環境の構築 よく使うパッケージ、ライブラリのインストール プロジェクトごとの環境設定 が出来ます。 また、この中で最も使える機能がプロジェクトごとの環境設定です。 通常、pipなどでパッケージやライブラリをインストールした場合には、他プロジェクトでインストールしたもの同士で依存関係がぐちゃぐちゃになってしまい、プログラムが上手く動かないことがあります。これはプロジェクトが増えるごとに起きる確率が高くなる重大な問題です。 しかし、AnacondaにはCondaというパッケージ管理ツールが内包されており、プロジェクトごとに分離した環境を作り出すことが出来ます。そのため、依存関係に悩むことを減らすことが出来るのです。 また、Condaに似た機能を持つソフトに、venvというものもあります。これはPythonをインストールした際に付いてくる、最も手軽にプロジェクトごとの環境設定が出来るソフトです。Anacondaのインストールが難しい人はこちらを使ってみるのも良いかと思います。 Jupyter Notebook 分類:IDE 使用できる言語:Python、Julia、R、C++、Ruby、Kotlinなど 難易度:低 使用環境:インストール版とブラウザ版のどちらも可能 使用者が多く、文献も多いため、まだプログラミングに慣れていないような初心者の方にもおすすめのIDEです。ビジュアルも良いため、分かりやすさの面でもおすすめです。 Anacondaをインストールした場合には自動でJupyter Notebookも付いてくるため、Anacondaを入れてしまうのもありです。 Google Colablatory 分類:IDE 使用できる言語:Python 難易度:中 使用環境:ブラウザ版のみ ブラウザで簡単にPythonのプログラムが書けるGoogleのサービスです。手軽にPythonを書けるのが良いですが、現状ではJupyter Notebookなどに比べると日本語の資料が少ないため、独学かつ調べることが得意ではない人は他のIDEを使うことを検討しても良いかもしれません。 また、GoogleがGPUという演算処理装置を無料で提供しているため、機械学習を学びたい方にも向いています。ただし、無料で使える範囲には限りがあるため注意が必要です。 IDLE 分類:IDE 使用できる言語:Python 難易度:低 使用環境:インストール版のみ Pythonをインストールしたときに自動でインストールされる、公式のIDEです。最低限の機能しかなく、利便性はあまりないですが、プログラミングをし始めるまでの壁がほとんどないため、初めてプログラミングをする人に向いています。 Spyder 分類:IDE 使用できる言語:Python 難易度:中 使用環境:インストール版のみ グラフの描画やデータ解析に強く、計算や解析をするためにプログラミングをする人におすすめのIDEです。 Jupyter Notebookほどの機能はありませんがシンプルでかっこいいです。 Anacondaをインストールした場合には自動でJupyter Notebookも付いてくるため、Anacondaを入れてしまうのもありです。 Visiual Studio 分類:IDE 使用できる言語:だいたい何でも 難易度:中 使用環境:インストール版のみ Microsoftが提供する、だいたい何でも出来るIDEです。ゲーム開発やアプリ開発からWeb開発など多種多様な用途に対応しています。ただ、その分カスタマイズ出来る部分が多すぎて、使いこなすのには時間がかかる部分もあるため注意が必要です。 Visiual Studio Code 分類:エディタ 使用できる言語:だいたい何でも 難易度:中 使用環境:インストール版とブラウザ版のどちらも可能 筆者が最も愛するエディタです。Visiual Studioのエディタ部分の機能だけ取り出したものです。 比較的動作が軽く、カスタマイズ性が良く、さらにかっこいいため、ストレスなくプログラミングができます。また、カスタマイズをすることで、エディタの機能だけでなく、コンパイルやデバッグ、テストまで出来るので、IDEのように使うことも可能です。ただし、カスタマイズされていない状態だとほとんど何も出来ないので、少しだけ難易度が高いです。 最後に 様々なソフト、サービスについてまとめました。少しでも頭の整理に役立てたのであれば嬉しいです。 また、筆者も学習中の身のため、もし間違えがあればご指摘いただけるとありがたいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

FCMのPythonSDKでの通知メッセージ・データメッセージ・APNsの利用

通知メッセージとデータメッセージとAPNs FCMのメッセージ配信は、通知メッセージとデータメッセージが存在します。通知メッセージはFCMが用意したフォーマットで配信することで、PUSH通知を送ることができます。PUSHメッセージはPUSH通知で利用するデータを送信し端末のアプリ側で処理を行います。またiOSの場合はAPNsを利用した配信も可能です。 PythonSDKでのメッセージ配信 PythonSDKのリファレンスは以下になります。 通知メッセージでの配信 以下のようにnotificationを指定することで通知メッセージでの配信ができます。 notification = messaging.Notification( title='プッシュ通知', body='プッシュ通知が来たよ!', image='hoge://yyyyyyyy' ) message = messaging.Message(token = "dummy", notification = notification) messaging.send(message) データメッセージでの配信 以下のように Message メソッドにdataをdict形式で渡すことでデータメッセージができます。 data = { "title": 'プッシュ通知', "body": 'プッシュ通知が来たよ!', "url": 'hoge://yyyyyyyy', } message = messaging.Message(token = "dummy", data = data) messaging.send(message) APNsの利用 次にAPNsを利用する方法を紹介します。APNsを利用する場合基本的には通知メッセージまたはデータメッセージと組み合わせることが一般的です。基本的には、APNsに対応するように引数が存在しますが、存在しないものは以下のように custom_data を指定します。これにより、どのようなAPNsのパラメータも利用することができます。 data = { "url": 'hoge://yyyyyyyy', } apns = messaging.APNSConfig( payload = messaging.APNSPayload( aps = messaging.Aps( alert = messaging.ApsAlert( body = 'プッシュ通知が来たよ!' ), sound = 'default', badge = 1, custom_data = { 'interruption-level': 'time-sensitive' }, ) ) ) message = messaging.Message(token = "dummy", data = data, apns = apns) messaging.send(message) APNsのドキュメントは以下になります その他 以上で紹介したものの他にWebプッシュ用のパラメータや、Andoroidだけで利用するためのパラメータ等も存在します。詳しくは以下のリファレンスをご参照ください。 デバッグ 続いてPythonSDKで生成されたメッセージがどのようなJSONになるのかのデバッグ方法を紹介します。以下のように文字列変換してやることでJSONに変換可能です。ただし、 Message は token または topic のどちらかが必須となるためデータだけをデバッグしたい場合はダミーで値を登録してやる必要があります。 以下がデバッグの例です。 data = { "url": 'hoge://yyyyyyyy', } apns = messaging.APNSConfig( payload = messaging.APNSPayload( aps = messaging.Aps( alert = messaging.ApsAlert( body = 'PUSH!!!!' ), sound = 'default', badge = 1, custom_data = { 'interruption-level': 'time-sensitive' }, ) ) ) debug_message = messaging.Message(token = "dummy", data = data, apns = apns) print(str(debug_message)) 上記の結果が以下になります。 { "apns": { "payload": { "aps": { "alert": { "body": "PUSH!!!!" }, "badge": 1, "interruption-level": "time-sensitive", "sound": "default" } } }, "data": { "url": "hoge://yyyyyyyy" }, "token": "dummy" } APNsで指定した custom_data の interruption-level がちゃんと aps 直下に含まれていることが確認できます。一点注意点ですが、日本語が入っている場合はutf8エンコードされてしまう場合があります。これについては、「json dump python 日本語」等でググってください。 補足 MulticastMessage 同一のメッセージを渡したtokenのリストに配信するものもありますがこちらはJSONへの変換ができませんでした。 また、公式のドキュメントにはこのデバッグ方法は書かれていなく、ソースコードを確認してJSONに変換できることを確認できたため、アナウンスなくJSON変換できなくなる可能性があります。 まとめ 本記事では、FCMのPythonSDKでの配信処理について紹介しました。また、メッセージのデバッグ方法について紹介しました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

気象学における流線関数・速度ポテンシャルとPythonでの計算方法

はじめに 季節〜気候スケールの大気の水平の流れ場を表現する場合には、流線関数(stream function)や速度ポテンシャル(velocity potential)を用いることがある。 これらの量を東西・南北風速から計算するには、後述するようにPoisson方程式を解く必要があり、localな差分計算のみからは求まらない。このため、例えばMetPyのmetpy.calcには関数が用意されておらず、別のパッケージを用いる必要がある。 ここでは、xarrayで読み込んだデータに対して、windsphermというPythonパッケージを用いて計算する方法を説明する。windsphermは、全球の風速データに対して球面調和関数展開を適用し、流線関数・速度ポテンシャルなどの風に関わる諸量を計算することができる。 なお、詳しくない読者のために流線関数や速度ポテンシャルについての数学的基礎や気象学的応用について記したが、必要のない読者は読み飛ばしてもらって構わない。 数学的基礎 まず、流線関数・速度ポテンシャルに関する数学的な基礎を説明する。気象データを扱う場合は球面上での計算になるが、簡単のためにデカルト座標での計算を考える。 Helmholtz分解定理 水平風ベクトル ${\bf V}$ は、東西風 $u$ と南北風 $v$ を用いて ${\bf V} \equiv (u, v, 0)^\mathrm{T}$ と成分表示される。 気象学でよく使われる2次元のHelmholtz分解定理は、任意のベクトル場(ここでは風の場)を発散なし成分 $\mathbf{V_ψ}$ と渦なし成分 $\mathbf{V_χ}$ に一意に分解されることを示す。 \mathbf{V} = \mathbf{V}_ψ + \mathbf{V}_χ \\ ∇ \cdot \mathbf{V}_ψ = 0\\ ∇ \times \mathbf{V}_χ = \mathbf{0} 流線関数・速度ポテンシャル 水平発散なしの流れ場に対しては、一般に以下のような流線関数が導入できる。 \mathbf{V}_ψ = (u_ψ, v_ψ, 0)^\mathrm{T} = \left(-\frac{∂ψ}{∂y}, \frac{∂ψ}{∂x}, 0 \right)^\mathrm{T} 流線関数の等値線は、その名の通り流れのベクトル $\mathbf{V_ψ}$ に平行な流線になる。 水平渦なしの流れ場に対しては、一般に以下のような速度ポテンシャルが導入できる。 \mathbf{V}_χ = (u_χ, v_χ, 0)^\mathrm{T} = \left(\frac{∂χ}{∂x}, \frac{∂χ}{∂y}, 0 \right)^\mathrm{T} 速度ポテンシャルは、流れのベクトル $\mathbf{V_χ}$ に対して直交する。 なお、流線関数と速度ポテンシャルいずれも定数項の任意性があるが、通常は領域平均が0になるように定数項を決めることが多いと思われる。 以上より、水平風は(発散なし成分の)流線関数 $ψ$ と(渦なし成分の)速度ポテンシャル $χ$ の2つのスカラー量で表現できることが分かる。 鉛直渦度・水平発散との関係 鉛直渦度 $ζ$ は流線関数 $ψ$ のLaplacianである。 ζ \equiv \mathbf{k} \cdot ∇ \times \mathbf{V} = \mathbf{k} \cdot ∇ \times \mathbf{V}_ψ = \frac{∂^2 ψ}{∂x^2} + \frac{∂^2 ψ}{∂y^2} = \nabla^2 ψ ここで $\mathbf{k}$ は鉛直($z$)方向の単位ベクトルである。 水平発散 $δ$ は速度ポテンシャル $χ$ のLaplacianである。 δ \equiv ∇ \cdot \mathbf{V} = ∇ \cdot \mathbf{V}_χ = \frac{∂^2 χ}{∂x^2} + \frac{∂^2 χ}{∂y^2} = \nabla^2 χ このことから、水平風 $(u,v)$ から、流線関数・速度ポテンシャル $(ψ,χ)$ を求めるためには、まず渦度・発散 $(ζ,δ)$ を求めた後、2つのPoisson方程式 \nabla^2 ψ = ζ \\ \nabla^2 χ = δ を解く必要があることが分かる。 気象学的応用 流線関数や速度ポテンシャルが季節予報で好まれる理由 流線関数と鉛直渦度はどちらも渦を表す量であるが、鉛直渦度のほうが水平スケールの細かい構造を表現する。というのも微分演算子Laplacianはハイパスフィルタとして機能するからである。例えば、$ψ = \sin(kx)$ と東西波数 $k$ の正弦波で表現される場合、$ζ = -k^2 \sin(kx)$ となるため、より高波数(短波長)成分が強調されるためである。 このような性質から、短期予報では細かい擾乱を把握するために鉛直渦度や水平発散に着目することが多いのに対して、季節予報ではより大きな循環場を把握するために流線関数や速度ポテンシャルを用いることが多い。 速度ポテンシャルと対流活発域 速度ポテンシャルは、熱帯の対流活動等に伴う大規模な発散・収束等を監視するのに利用される。熱帯域では通常下層850hPaで収束(発散)、上層200hPaで発散(収束)、その間の高度で上昇流(下降流)というパターンになることが多いため、200hPaの速度ポテンシャルが小さい発散域では対流活動が活発で上昇流域になっていると解釈される。 なお、前述のように水平発散と速度ポテンシャルは表現する空間スケールが異なり、水平発散は個々の対流(あるいは降水)のパターンに対応するのに対し、速度ポテンシャルはMJOのようなある程度の広さを持った対流活発域と対応する。 流線関数と水平風の流線 流線関数 $ψ$ は発散なし成分 $\mathbf{V_ψ}$ に対する流線関数であるが、気象学ではあたかも流線関数の等値線が元の水平風 $\mathbf{V}$ の流線であるかのように解釈される。つまり値が大きいほうを右手に向いて風が吹き、流線関数の等値線の間隔が狭いほど風が強いと解釈される。 この背景には、発散なし成分と渦なし成分の大きさについて |\mathbf{V}_χ| \ll |\mathbf{V}_ψ| という関係が経験的に成り立つことに基づいている。この関係が成り立つ理由については、筆者の推測だが、水平収束は連続の式より(浮力が必要な)上昇流を伴うのに対し、渦を作る場合は上昇流を伴う必要がないことが関係していると思われる。 流線関数を使うメリットの一つは、熱帯域も含めて等値線を描くだけで風速・風向を把握できる点にある。中高緯度では地衡風平衡が概ね成り立つため、等ジオポテンシャル高度線から風を把握できる。しかし、熱帯域では地衡風平衡が成り立たないため、等ジオポテンシャル高度線から風を推測することができない(そもそもジオポテンシャル高度勾配はほとんど存在しない)。流線関数はジオポテンシャル高度と比べて、熱帯域の風の把握に使えるという利点がある。 Pythonでの計算 windspharmのインストールはcondaを用いて行うことができる。このあとxarrayを使うため、インストールしていない場合はあらかじめインストールしておく。 conda install -c conda-forge windspharm windspharmは入力に対する様々なインターフェースが用意されている。 本パッケージは、numpy と pyspharm が利用可能であることを最低限必要とし、インストールには setuptools が必要です。windspharm.iris インターフェースは、iris パッケージが利用可能な場合にのみ使用できます (iris のドキュメントを参照)。windspharm.cdms インターフェースは、cdms2 モジュールが利用可能な場合にのみ使用できます。このモジュールは、UVCDATプロジェクトの一部として配布されています。windspharm.xarray インターフェースは、xarray パッケージが利用可能な場合にのみ使用できます(xarray のドキュメントを参照)。 ここではxarrayを用いた方法を説明する。xarrayインターフェースについての公式ドキュメントは以下のページにある。 次に示すサンプルコードは、風速の適当なGRIBファイル(6か月アンサンブル数値予報モデル統計GPV(全球域)3か月統計値)から、流線関数と速度ポテンシャルを計算する。 import matplotlib.pyplot as plt import xarray as xr from windspharm.xarray import VectorWind # ファイル読み込み file_u = "grib/Z__C_RJTD_20211122000000_EPSC_GPV_Rgl_Gll2p5deg_Lp200_Pwu_E3em_grib2.bin" file_v = "grib/Z__C_RJTD_20211122000000_EPSC_GPV_Rgl_Gll2p5deg_Lp200_Pwv_E3em_grib2.bin" ds_u = xr.open_dataset(file_u, engine="cfgrib") ds_v = xr.open_dataset(file_v, engine="cfgrib") # 時刻0の2次元配列を変数に代入 u = ds_u["u"][0] v = ds_v["v"][0] # 流線関数と速度ポテンシャルを計算 # 流線関数のみの場合は w.streamfunction() # 速度ポテンシャルのみの場合は w.velocitypotential() w = VectorWind(u, v) sf, vp = w.sfvp() # 描画 sf.plot.contour(levels=20, colors="black") # vp.plot.contour(levels=20, colors="black") plt.show() plt.clf() plt.close() 実行すると、以下のように流線関数や速度ポテンシャルが描画される。 流線関数 速度ポテンシャル
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PyQt5のチュートリアルを動かす ① Hello World

はじめに 以下のサイトを参考にPyQt5のチュートリアルを動かしてみました。 サンプル1 最初のサンプルです。 sample1.py import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * def window(): app = QApplication(sys.argv) w = QWidget() b = QLabel(w) b.setText("Hello World!") w.setGeometry(100,100,200,50) b.move(50,20) w.setWindowTitle("PyQt5") w.show() sys.exit(app.exec_()) if __name__ == '__main__': window() 実行結果 === プログラムの説明 === このプログラムは以下の流れで処理を行っています。 1.PyQtに含まれるQtCore,QtGui,QtWidgetsモジュールをインポート 2.アプリケーションオブジェクトのQApplicationクラスを生成 3.QWidgetで最上位のウィンドウを作成し,QLableオブジェクトを設置 4.ラベルに"hello world"という文字を記載 5.setGeometry()メソッドでウィンドウのサイズと位置を定義 6.app.exec_()メソッドでアプリケーションのメインループに入る サンプル2 また、先程のコードをオブジェクト志向の様に作成することも可能です。 その場合のコードは以下のようになります。 sample2.py import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * class window(QWidget): def __init__(self, parent = None): super(window, self).__init__(parent) self.resize(200,50) self.setWindowTitle("PyQt5") self.label = QLabel(self) self.label.setText("Hello World") font = QFont() font.setFamily("Arial") font.setPointSize(16) self.label.setFont(font) self.label.move(50,20) def main(): app = QApplication(sys.argv) ex = window() ex.show() sys.exit(app.exec_()) if __name__ == '__main__': main() 実行結果です。 === プログラム説明 === 先程のプログラムと違う点についてはQWidgetクラスを元にしたwindowクラスを宣言しているところです。これによって,1つ1つのwidgetを独立して作成していき、それを組み合わせることで大きなGUIプログラムを作成していくものと予想できます。 さいごに QApplicationというクラスがPyQtのアプリを内部で管理しているのでしょうか? 今後もサンプルを動作させていき理解を深めて行ければなと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PyQt5のチュートリアルを動かす ①

はじめに 以下のサイトを参考にPyQt5のチュートリアルを動かしてみました。 サンプル1 最初のサンプルです。 sample1.py import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * def window(): app = QApplication(sys.argv) w = QWidget() b = QLabel(w) b.setText("Hello World!") w.setGeometry(100,100,200,50) b.move(50,20) w.setWindowTitle("PyQt5") w.show() sys.exit(app.exec_()) if __name__ == '__main__': window() 実行結果 === プログラムの説明 === このプログラムは以下の流れで処理を行っています。 1.PyQtに含まれるQtCore,QtGui,QtWidgetsモジュールをインポート 2.アプリケーションオブジェクトのQApplicationクラスを生成 3.QWidgetで最上位のウィンドウを作成し,QLableオブジェクトを設置 4.ラベルに"hello world"という文字を記載 5.setGeometry()メソッドでウィンドウのサイズと位置を定義 6.app.exec_()メソッドでアプリケーションのメインループに入る サンプル2 また、先程のコードをオブジェクト志向の様に作成することも可能です。 その場合のコードは以下のようになります。 sample2.py import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * class window(QWidget): def __init__(self, parent = None): super(window, self).__init__(parent) self.resize(200,50) self.setWindowTitle("PyQt5") self.label = QLabel(self) self.label.setText("Hello World") font = QFont() font.setFamily("Arial") font.setPointSize(16) self.label.setFont(font) self.label.move(50,20) def main(): app = QApplication(sys.argv) ex = window() ex.show() sys.exit(app.exec_()) if __name__ == '__main__': main() 実行結果です。 === プログラム説明 === 先程のプログラムと違う点についてはQWidgetクラスを元にしたwindowクラスを宣言しているところです。これによって,1つ1つのwidgetを独立して作成していき、それを組み合わせることで大きなGUIプログラムを作成していくものと予想できます。 さいごに QApplicationというクラスがPyQtのアプリを内部で管理しているのでしょうか? 今後もサンプルを動作させていき理解を深めて行ければなと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AtCoder解説】PythonでABC229のA,B,C,D,E問題を制する!

ABC229のA,B,C,D,E問題を、Python3でなるべく丁寧に解説していきます。 ただ解けるだけの方法ではなく、次の3つのポイントを満たす解法を解説することを目指しています。 シンプル:余計なことを考えずに済む 実装が楽:ミスやバグが減ってうれしい 時間がかからない:パフォが上がって、後の問題に残せる時間が増える ご質問・ご指摘はコメントかツイッター、マシュマロ、Discordサーバーまでお気軽にどうぞ! Twitter: u2dayo マシュマロ: https://marshmallow-qa.com/u2dayo ほしいものリスト: https://www.amazon.jp/hz/wishlist/ls/2T9IQ8IK9ID19?ref_=wl_share Discordサーバー(質問や記事の感想・リクエストなどどうぞ!) : https://discord.gg/jZ8pkPRRMT よかったらLGTMや拡散していただけると喜びます! 目次 ABC229 まとめ A問題『First Grid』 B問題『Hard Calculation』 C問題『Cheese』 D問題『Longest X』 E問題『Graph Destruction』 アプリ AtCoderFacts を開発しています コンテストの統計データを見られるアプリ『AtCoderFacts』を作りました。 現在のところ、次の3つのデータを見ることができます。 レート別問題正解率 パフォーマンス目安 早解きで上昇するパフォーマンス 今後も機能を追加していく予定です。使ってくれると喜びます。 ABC229 まとめ 全提出人数: 6070人 パフォーマンス パフォ AC 点数 時間 順位(Rated内) 200 AB------ 300 13分 4697(4439)位 400 ABC----- 600 54分 3916(3661)位 600 ABC----- 600 31分 3271(3017)位 800 ABCD---- 1000 95分 2603(2353)位 1000 ABCD---- 1000 31分 1987(1739)位 1200 ABCDE--- 1500 79分 1465(1225)位 1400 ABCDE--- 1500 51分 1057(822)位 1600 ABCDE--- 1500 33分 745(517)位 1800 ABCDEF-- 2000 106分 494(305)位 2000 ABCDEF-- 2000 60分 326(164)位 2200 ABCDE-G- 2100 85分 211(81)位 2400 ABCDEFG- 2600 95分 131(38)位 色別の正解率 色 人数 A B C D E F G H 灰 2347 92.5 % 84.9 % 53.3 % 11.0 % 3.0 % 0.1 % 0.3 % 0.0 % 茶 1156 98.4 % 98.6 % 93.5 % 41.6 % 13.5 % 0.6 % 0.6 % 0.0 % 緑 927 98.4 % 98.7 % 97.6 % 76.4 % 55.1 % 2.2 % 1.4 % 0.0 % 水 584 99.1 % 99.3 % 98.8 % 93.2 % 94.0 % 13.7 % 5.0 % 0.0 % 青 356 98.9 % 98.9 % 98.9 % 98.0 % 98.0 % 39.9 % 20.8 % 0.0 % 黄 191 92.2 % 92.7 % 92.7 % 92.7 % 94.2 % 63.9 % 44.5 % 0.5 % 橙 41 92.7 % 92.7 % 92.7 % 92.7 % 95.1 % 80.5 % 87.8 % 0.0 % 赤 30 93.3 % 93.3 % 96.7 % 96.7 % 96.7 % 93.3 % 90.0 % 30.0 % ※表示レート、灰に初参加者は含めず A問題『First Grid』 問題ページ:A - First Grid 灰コーダー正解率:92.5 % 茶コーダー正解率:98.4 % 緑コーダー正解率:98.4 % 入力 $S_1, S_2$ : #または.からなる $2$ 文字の文字列 考察 「$2$ つの異なる黒マス同士が辺で接している時、またその時に限りそれら $2$ つの黒マスは直接行き来できます。」とは、ある#のマスから上下左右にある別の#に移動できるという意味です。斜め移動はできないです。(入出力例を見るとわかりやすいです) 問題文より、#のマスは $2$ つ以上です。# が $2,3,4$ 個のそれぞれについて、どのようなパターンがYesかNoになるかを考察します。 4 個のパターン ## ## すべて#なので、Yesです。 3個のパターン ## .# L字型を回転させた(.の位置が変わっただけ) $4$ パターンしかないので、Yesです。 2文字のパターン #. #. ## .. 同じ行か列に#が並んでいる $4$ パターンはYesです。($2$ パターン省略しています) #. .# .# #. #が対角線上に並んだ、この $2$ つのパターンだけが No です。 ダメなパターンは2つしかない Noになるパターンは $2$ つしかないので、Noになるパターンを直接書いて判定するといいです。 コード S = input() + input() # 書きやすくするために、2行まとめてしまいます """\は無視されるので、T1は"#..#"で、T2は".##."という1つの文字列です """ T1 = "#." \ ".#" T2 = ".#" \ "#." if S == T1 or S == T2: print("No") else: print("Yes") B問題『Hard Calculation』 問題ページ:B - Hard Calculation 灰コーダー正解率:84.9 % 茶コーダー正解率:98.6 % 緑コーダー正解率:98.7 % 入力 $A,\ B$ : $19$ 桁以下の正の整数 考察 $A$ と $B$ の同じ桁どうしを足し合わせたとき、どの桁も $10$ 以上にならないときは繰り上がりが生じません。$1$ つでも $10$ 以上になる桁があるなら、繰り上がりが生じます。 $A$ と $B$ の桁数が違う場合があるので、実装に注意が必要です。 実装 $A$ と $B$ の桁数が違う場合、$0$ があることにすると良いです。例えば、$A=39, B=543$なら、$A=039, B=543$ ということにして、$0+5, 3+4, 9+3$を判定します。 これは、$A, B$ を文字列で受け取って、X = A.zfill(20)、Y = B.zfill(20)として、$20$ 桁の $0$ 埋めされた文字列を作ると楽です。 コード A.zfill(20) でどのような文字列ができるかよくわからなければ、試しにprintしてみるといいです。 たとえばA = "229" なら、X == "00000000000000000229"になります。 def judge(): A, B = input().split() X, Y = A.zfill(20), B.zfill(20) # 0000...229のような、左から0埋めされた長さ20の文字列ができます for x, y in zip(X, Y): if int(x) + int(y) >= 10: return "Hard" return "Easy" print(judge()) C問題『Cheese』 問題ページ:C - Cheese 灰コーダー正解率:53.3 % 茶コーダー正解率:93.5 % 緑コーダー正解率:97.6 % 入力 $N$ : チーズの種類数 $W$ : 乗せていいチーズの重さの上限 $A_i$ : チーズ $i$ は、$1$ グラムあたりのおいしさが $A_i$ $B_i$ : チーズ $i$ は、$B_i$ グラム在庫がある 考察 好きなチーズを自由に使うことができるので、価値が高い($A_i$ が大きい)チーズから貪欲に使えばいいです。($1$ グラムあたり $X$ の価値がある粉を容量 $W$ の瓶に詰めて、価値の合計を最大にすると考えましょう) つまり、$A_i$ が大きい順にソートして、順番に使えるだけ使えば答えがわかります。 なお、$W, B_i$ のどちらも整数なので、各チーズを使う量は必ず整数になります。 $A_i$ も整数ですから、答えも整数になります。 コード from operator import itemgetter def solve(): N, W = map(int, input().split()) AB = [list(map(int, input().split())) for _ in range(N)] AB.sort(reverse=True, key=itemgetter(0)) # Bの順序はどうでもいいので、Aの順だけでソートしたほうが速いです(keyを指定せずにソートしてもいいです) ans = 0 rem = W for a, b in AB: t = min(rem, b) # bかremの小さい方 ans += t * a rem -= t return ans print(solve()) おまけ 勉強している人は、こういう問題はナップサック問題だから、動的計画法で解けると思ってしまうかもしれません。それは罠です。制約の $N,W,A_i$ のいずれも大きく、特殊な制約もないので、動的計画法を使って $2$ 秒で解くことはできません。 動的計画法で解ける制約は、例えば以下の問題のようなものです。 ABC032 D - ナップサック問題 ABC060 D - Simple Knapsack(この問題と共通する部分があります) D問題『Longest X』 問題ページ:D - Longest X 灰コーダー正解率:11.0 % 茶コーダー正解率:41.6 % 緑コーダー正解率:76.4 % 入力 $S$ : Xまたは.からなる、長さが $200000$ 以下の文字列 $K$ : .をXに置き換える操作を行える回数の上限 考察 $S$ の $r$ 文字目を右端として固定すると、$K$ 回以下の書き換えで、どこまでXが連続する区間を左に伸ばせるかわかります。 $S$ の $r+1$ 文字目を新しい右端にすることを考えます。$S_{r+1}$ が #なら、先ほどの答えに $1$ を足せばいいです。.で、書き換えた回数が$K$ を上回るなら、書き換えた回数が$K$ 回以下になるまで、左端を右にずらす必要があります。 区間の右端を右に進めたとき、区間の左端はそのままか、右に進むのどちらかで、左に戻ることはありません。このような場合、尺取法というアルゴリズムを使うことができ、計算量 $O(|S|)$ でこの問題を解くことができます。 なお、.の数の累積和を求めたあと、右端を固定したとき左端をどこまで伸ばせるか二分探索することで $O(|S|\ log\ |S|)$ で解くこともできます。 実装 尺取法はdequeを使ったキューで実装すると楽です。 こちらの方の記事が参考になります:しゃくとり法のDequeを使ったバグりにくい実装 コード from collections import deque def solve(): S = input() K = int(input()) que = deque() rem = K ans, score = 0, 0 for char in S: score += 1 if char == ".": que.append(1) rem -= 1 else: que.append(0) while rem < 0: rem += que.popleft() score -= 1 ans = max(ans, score) return ans print(solve()) E問題『Graph Destruction』 問題ページ:E - Graph Destruction 灰コーダー正解率:3.0 % 茶コーダー正解率:13.5 % 緑コーダー正解率:55.1 % 入力 $N$ : 頂点の数 $M$ : 辺の数 $A_i,\ B_i$ : 辺 $i$ は頂点 $A_i$ と $B_i$ をつないでいる($A_i\lt{B_i}$) 考察 高速に連結成分を管理できるデータ構造にUnionFindがありますが、普通のUnionFindで頂点や辺を削除することはできません。 そこで、頂点 $N$ まですべての頂点が消えている状態からスタートして、頂点 $N,N-1,\dots, 2,1$ の順に頂点が出現すると逆再生して考えます。頂点 $i$ が出現する時点では、$i$ より小さい頂点は存在しませんから、$i$ から $i$ より小さい頂点につながる辺も存在しないとみなします。 すると、頂点や辺は追加されるだけになりますから、UnionFindを使って管理することができます。 実装 UnionFindを少しいじって、連結成分の数を $O(1)$ で求められるようにすると楽です。 コード from typing import List class UnionFind: """0-indexed""" def __init__(self, n): self.n = n self.parent = [-1] * n self.__group_count = n # 辺がないとき、連結成分はn個あります def unite(self, x, y): """xとyをマージ""" x = self.root(x) y = self.root(y) if x == y: return 0 self.__group_count -= 1 # 木と木が合体するので、連結成分数が1減ります if self.parent[x] > self.parent[y]: x, y = y, x self.parent[x] += self.parent[y] self.parent[y] = x return self.parent[x] def is_same(self, x, y): """xとyが同じ連結成分か判定""" return self.root(x) == self.root(y) def root(self, x): """xの根を取得""" if self.parent[x] < 0: return x else: self.parent[x] = self.root(self.parent[x]) return self.parent[x] def size(self, x): """xが属する連結成分のサイズを取得""" return -self.parent[self.root(x)] def all_sizes(self) -> List[int]: """全連結成分のサイズのリストを取得 O(N) """ sizes = [] for i in range(self.n): size = self.parent[i] if size < 0: sizes.append(-size) return sizes def groups(self) -> List[List[int]]: """全連結成分の内容のリストを取得 O(N・α(N))""" groups = dict() for i in range(self.n): p = self.root(i) if not groups.get(p): groups[p] = [] groups[p].append(i) return list(groups.values()) def group_count(self) -> int: """連結成分の数を取得 O(1)""" return self.__group_count # 変数を返すだけなので、O(1)です def main(): """入力の頂点番号から1引いて、頂点0からはじまるようにしています(UnionFindをN+1頂点で作ると都合が悪いため)""" N, M = map(int, input().split()) G = [[] for _ in range(N)] for _ in range(M): a, b = map(int, input().split()) G[a - 1].append(b - 1) # 制約より、a < b が保証されています uf = UnionFind(N) # 連結成分数を管理して、O(1)で取得できるようにしたUnionFindです ans = [0] * N for u in reversed(range(N)): ans[u] = uf.group_count() - (u + 1) # u が出現する直前 = uまで消えているときの答え for v in G[u]: uf.unite(u, v) for x in ans: print(x) if __name__ == '__main__': main()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

condaとpipとTensorFlow【Python】

はじめに Anaconda環境でTensorFlow2.4.1を用いてコードを書いていたのですが、TensorFlow2.6以降に追加された関数を利用したかったので、下記のようにcodnaインストールできるバージョンを探してみました。 conda search tenosrflow-gpu すると、「tensorflow-gpu 2.4.1」(Anacondaのバージョンによっては「tensorflow-gpu 2.5.0」)が最新のバージョンとなっており、望む結果ではありませんでした。 conda install tensorflow と tensorflow-gpu TensorFlow2.x系は基本的にGPUを用いるので、「tensorflow」と「tensorflow-gpu」のどちらをインストールしてもGPUを用いることができるらしい(先に結論:condaではできない)ので、インストール可能な「TensorFlow」のバージョンを調べてみました。 conda search tensorflow すると、「tensorflow 2.6.0」が最新のバージョンとなっていました。試しに(以下、失敗しますが)「tensorflow 2.6.0」を念のため仮想環境内でインストールしてみます。 conda create -n tf260 tensorflow=2.6.0 これで、TensorFlow2.6.0をインストールすることができたので、Pythonを起動してGPUを認識できているかを確認します。 GPUを認識しているかどうかを確認するコード from tensorflow.python.client import device_lib device_lib.list_local_devices() 結果、GPUを認識していなことが分かります。ここから、conda installでGPUを利用したい場合は「tensorflow-gpu」をインストールする必要があるということが分かりました。 解決策はpip Anacondaを利用する際には基本的にconda installを利用するように心掛けていますが、今回は仕方ないのでpip installを用いることにします。念のため仮想環境内で作業します。 conda create -n tf260pip pip conda activate tf260pip pip install tensorflow==2.6.0 これで正常にtensorflow2.6.0をインストールすることができました!では、GPUを認識しているかどうかを確認してみると(先ほどのコードを再利用)、しっかりとGPUを認識していることが分かります! まとめ 知っている方も多いと思いますが、condaとpipでインストールする際の参照先は異なります。condaの場合でGPU環境を利用したいなら「tensorflow-gpu」をインストールする必要がありますが、pipの場合は「tensorflow」をインストールすることでGPUを認識することができるようです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

自然言語処理100本ノック2020を解いてみた(第二章)

はじめに 自然言語処理100本ノック2020の第二章を解いてみました。 第一章はこちら。 動かしてみたい場合は、こちらのNotebook(Google Colaboratory)から、簡単に実行できます。 事前準備 popular-names.txtをダウンロードします。 !wget https://nlp100.github.io/data/popular-names.txt 10. 行数のカウント 行数をカウントせよ.確認にはwcコマンドを用いよ. python import pandas as pd df = pd.read_table("./popular-names.txt", header=None, names=["名前", "性別", "人数", "年"]) print(len(df)) output 2780 command !wc -l ./popular-names.txt output 2780 ./popular-names.txt 11. タブをスペースに置換 タブ1文字につきスペース1文字に置換せよ.確認にはsedコマンド,trコマンド,もしくはexpandコマンドを用いよ. python df.to_csv("./problem11_python_ans.txt", sep=' ', index=False, header=None) !head -n 3 ./problem11_python_ans.txt output Mary F 7065 1880 Anna F 2604 1880 Emma F 2003 1880 command !sed "s/\t/ /g" ./popular-names.txt | head -n 3 output Mary F 7065 1880 Anna F 2604 1880 Emma F 2003 1880 12. 1列目をcol1.txtに,2列目をcol2.txtに保存 各行の1列目だけを抜き出したものをcol1.txtに,2列目だけを抜き出したものをcol2.txtとしてファイルに保存せよ.確認にはcutコマンドを用いよ. python df.iloc[:, 0].to_csv("./col1_python.txt", index=False, header=None) df.iloc[:, 1].to_csv("./col2_python.txt", index=False, header=None) !head -n 3 ./col1_python.txt !head -n 3 ./col2_python.txt output Mary Anna Emma F F F command !cut -f 1 ./popular-names.txt > ./col1.txt !cut -f 2 ./popular-names.txt > ./col2.txt !head -n 3 ./col1.txt !head -n 3 ./col2.txt output Mary Anna Emma F F F 13. col1.txtとcol2.txtをマージ 12で作ったcol1.txtとcol2.txtを結合し,元のファイルの1列目と2列目をタブ区切りで並べたテキストファイルを作成せよ.確認にはpasteコマンドを用いよ. python col1_df = pd.read_table("./col1.txt", header=None, names=["名前"]) col2_df = pd.read_table("./col2.txt", header=None, names=["性別"]) merge_df = pd.concat([col1_df, col2_df], axis=1) merge_df.to_csv("./problem13_python_ans.txt", sep="\t", index=False, header=None) !head -n 3 ./problem13_python_ans.txt output Mary F Anna F Emma F command !paste ./col1.txt ./col2.txt > ./problem13_unix_ans.txt !head -n 3 ./problem13_unix_ans.txt output Mary F Anna F Emma F 14. 先頭からN行を出力 自然数Nをコマンドライン引数などの手段で受け取り,入力のうち先頭のN行だけを表示せよ.確認にはheadコマンドを用いよ. python df.head(3) output 名前 性別 人数 年 0 Mary F 7065 1880 1 Anna F 2604 1880 2 Emma F 2003 1880 command !head -n 3 ./popular-names.txt output Mary F 7065 1880 Anna F 2604 1880 Emma F 2003 1880 15. 末尾のN行を出力 自然数Nをコマンドライン引数などの手段で受け取り,入力のうち末尾のN行だけを表示せよ.確認にはtailコマンドを用いよ. python df.tail(3) output 名前 性別 人数 年 2777 Lucas M 12585 2018 2778 Mason M 12435 2018 2779 Logan M 12352 2018 command !tail -n 3 ./popular-names.txt output Lucas M 12585 2018 Mason M 12435 2018 Logan M 12352 2018 16. ファイルをN分割する 自然数Nをコマンドライン引数などの手段で受け取り,入力のファイルを行単位でN分割せよ.同様の処理をsplitコマンドで実現せよ. python df_splits = [df.loc[(len(df)//3)*i:(len(df)//3)*(i+1)] for i in range(3)] df_splits[0].head(3) output 名前 性別 人数 年 0 Mary F 7065 1880 1 Anna F 2604 1880 2 Emma F 2003 1880 command !split -n 3 ./popular-names.txt split !head -n 3 ./splitaa output Mary F 7065 1880 Anna F 2604 1880 Emma F 2003 1880 17. 1列目の文字列の異なり 1列目の文字列の種類(異なる文字列の集合)を求めよ.確認にはcut, sort, uniqコマンドを用いよ. python ans = df.iloc[:, 0].unique() print(len(ans)) output 136 command !cut -f 1 ./popular-names.txt | sort -u | wc -l output 136 18. 各行を3コラム目の数値の降順にソート 各行を3コラム目の数値の逆順で整列せよ(注意: 各行の内容は変更せずに並び替えよ).確認にはsortコマンドを用いよ(この問題はコマンドで実行した時の結果と合わなくてもよい). python ans = df.sort_values(df.columns[2], ascending=False) ans.head(3) output 名前 性別 人数 年 1340 Linda F 99689 1947 1360 Linda F 96211 1948 1350 James M 94757 1947 command !sort -nrsk 3 ./popular-names.txt | head -n 3 output Linda F 99689 1947 Linda F 96211 1948 James M 94757 1947 19. 各行の1コラム目の文字列の出現頻度を求め,出現頻度の高い順に並べる 各行の1列目の文字列の出現頻度を求め,その高い順に並べて表示せよ.確認にはcut, uniq, sortコマンドを用いよ. python ans = df.iloc[:, 0].value_counts() ans.head(3) output James 118 William 111 John 108 Name: 名前, dtype: int64 command !cut -f 1 ./popular-names.txt | sort | uniq -c | sort -rsnk 1 | head -n 3 output 118 James 111 William 108 John
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】陽解法を用いた1次元拡散方程式の数値計算

はじめに  今回は、非定常1次元拡散方程式を陽解法を用いて数値計算する手法を記録する。  Python3でのシミュレーションを行っている。 問題  厚さを無視できる長さ1.0mmの薄い板を考える。初め、板は21℃で一定温度であった。片側の端を断熱し、もう一方の端を30℃に加熱した時の温度分布変化を描画する。 構成  1. 離散化式   1.1 離散化方法   1.2 離散化式の計算誤差  2. 計算条件  3. プログラム添付 離散化式 離散化方法  1次元拡散方程式は、 $$\frac{\partial{q}}{\partial{t}}=\alpha\frac{\partial^2{q}}{\partial{x^2}}\tag{1}$$ であり、時間1階微分項に前進差分を適用すると、 $$\frac{\partial{q}}{\partial{t}}=\frac{q^{n+1}-q^{n}}{\Delta{t}}\tag{2}$$ 空間2階微分項に二次精度の中心差分を適用すると、 $$\frac{\partial^2{q}}{\partial{x^2}}=\frac{q_{j+1}-2q_{j}-q_{j-1}}{\Delta{x^2}}\tag{3}$$ ここで、$\Delta{t}$は時間刻み幅、$\Delta{x}$は空間刻み幅である。右上添え字は時間ステップ、右下添え字は空間ステップを表す。式(2)と(3)から、式(1)の離散化式は、 $$\frac{q^{n+1}-q^{n}}{\Delta{t}}=\alpha\frac{q_{j+1}-2q_{j}-q_{j-1}}{\Delta{x^2}}\tag{4}$$ 離散化式の計算誤差  式(4)の離散化誤差についてまとめておく。  時間一階微分項の前進差分について、ある時刻tから∆tだけ進んだ時刻t+∆tにおけるテイラー展開を考える。 $$q^{n+1}=q^{n}+\frac{\partial{q}}{\partial{t}}\Delta{t}+\frac{1}{2}\frac{\partial^2{q}}{\partial^2{t}}\Delta{t^2}+\frac{1}{3!}\frac{\partial^3{q}}{\partial^3{t}}\Delta{t^3}+・・・\tag{5}$$ 式(5)から、 $$\frac{\partial{q}}{\partial{t}}=\frac{q^{n+1}-q^{n}}{\Delta{t}}+O(\Delta{t})\tag{6}$$ 式(6)により、時間微分項が1次精度であることがわかる。  空間2階微分項について、ある位置xから∆xだけ進んだ位置x+∆xと、∆xだけ遅れたx-∆xにおけるテイラー展開を考える。 $$q_{j+1}=q_{j}+\frac{\partial{q}}{\partial{x}}\Delta{x}+\frac{1}{2}\frac{\partial^2{q}}{\partial^2{x}}\Delta{x^2}+\frac{1}{3!}\frac{\partial^3{q}}{\partial^3{x}}\Delta{x^3}+・・・\tag{7}$$ 式(7)から、 $$\frac{\partial^2{q}}{\partial{x^2}}=\frac{q_{j+1}-2q_{j}-q_{j-1}}{\Delta{x^2}}+O(\Delta{x^2})\tag{8}$$ 式(8)により、空間2階微分項が2次精度であることがわかる。 計算条件 初期条件 $$T(x,0)=21℃$$ 境界条件 $$T(0,t)=21℃,T(1.0,t)=30℃$$ 安定化条件 フーリエ解析(省略)によって、 $$\alpha\frac{\Delta{t}}{\Delta{x^2}}\leqq\frac{1}{2}$$ プログラム import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation def init(jmax): x = np.linspace(0, 1, jmax) T = [] for i in range(jmax): #初期条件の設定 T.append(21) return(x, T) def calc(x, T, alpha, dt, dx, tmax): #グラフの体裁設定 fig = plt.figure(figsize=(7,7), dpi=100) for t in range(0, tmax+1): Told = T.copy() for j in range(1, jmax-1): dT = alpha * dt * (Told[j+1] - 2.0 * Told[j] + Told[j-1]) / (dx ** 2) T[j] = Told[j] + dT #境界条件 T[0] = 21 T[-1] = 30 #グラフの格納 im = plt.plot(x, T, c='black', lw=2) ims.append(im) #グラフの描画設定 plt.xlabel('x [mm]') plt.ylabel('T [℃]') plt.gca().spines['right'].set_visible(False) plt.gca().spines['top'].set_visible(False) plt.legend() ani = animation.ArtistAnimation(fig, ims, interval=100) plt.show() jmax = 11 tmax = 1000 alpha = 0.1 #拡散係数の算出 dt = 0.01 dx = 1/(jmax-1) x, T = init(jmax) ims = [] calc(x, T, alpha, dt, dx, tmax) 実行することで、熱の拡散の様子を見ることができます。 参考文献 藤井孝蔵、立川智章、Pythonで学ぶ流体力学の数値計算法、2020/10/20
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

kivyMDチュートリアル其の肆什弍 Behaviors - Background Color篇

ハロー、Qiita!いかがお過ごしでしょうか。 肌寒い季節になってきましたね。まぁ時候の挨拶は来週に置いておくとして、 体調管理には気をつけたい時期となります。 ちょうど、来週からはAdvent Calendarなど始まってくると思いますが、 皆さんは参加されるのでしょうか。KivyMDにいたってはタグなどないので、 このビッグイベントには乗りませんw あったとしても1人で30日も投稿して たら死にます。。 なので、KivyMDは相も変わらず、粛々と週1で投稿します。ということで今週 からはBehaviors章から始め、Background Color篇となります。ということ でレッツラゴ。 Background Color 冒頭では説明文が載っけられていますね。見てみましょう。 The following classes are intended for in-house use of the library. またまた難しそうな英文ですね。in-houseを検索すると「組織内の」とか「社内」の とかが結果として出てきます。 そのまま当てはめると、イミワカランことになるので少し解釈を変えます。アプリ の中のライブラリ内ということでしょうか。ちなみにDeepL君とGoogle翻訳君に 依頼を投げてみると、以下のように結果が返ってきました。 [DeepL君の仕事内容] 以下の授業は、図書館の社内利用を目的としています。 [Google翻訳君の仕事内容] 以下のクラスは、ライブラリを社内で使用することを目的としています。 うん、君たちのやりたいことはわかった、君たちは何も悪くない、と言いたくなり そうですね。社内利用だけになると、なんのためにMITライセンスなんだとツッコミ そうな気持ちがします。 これもなかなか翻訳の限界みたいなものがあり、こういう言い回しはなかなか苦戦 する様子が伺えます。と言っても、やっぱ人間すげーとなることも翻訳ソフトって やっぱだめだ!となることも違う気もします。お互い共存関係にあるということで しょうか。 Custom Background Color ということで冒頭で飛ばし過ぎということもありますが、ここからは独自の節で やっていきたいと思います。APIだけでは少し物悲しいですからね。。 ずいぶん前に、デフォルトのテーマカラーから変更することができると投稿でも 触れていましたが、今回はそのことを試してみたいと思います。以下のマニュア ルでも変更することは出来るのですが、色の選択に幅があります。 数えてみたところ、19×14でざっと266色。いや十分だろと思われた方は、バツ ボタンでページを閉じた方がいいかもです。時間は有意義に過ごしたいものです。 ということで、色選択とかしてカスタムしたい!という方のためにこのページはある と言っても過言ではありません。ということでコードマシの実行結果マシでトッピ ングを付け加えていきます。 さっそくにはなりますが、以下にコードを載せておきます。といっても今回のために 新しく作った訳では無く、以前の触れ込みから流用したものになりますね。それが 何なのかですが、Button篇のものから流用します。詳細知りたい方とあんまりKivy- MD知らないんだけどという方は、以下リンクを先に見て頂ければすんなり入ってくる かと思われます。 xlii/customcolor_button.py (略) Builder.load_string( """ (略) - BoxLayout: + MDBoxLayout: padding: dp(10) spacing: dp(10) # これだけこちらに移動 - size_hint: None, None - size: self.minimum_size + adaptive_height: True orientation: "vertical" pos_hint: {"center_x": .5} + md_bg_color: 0, 0.3828215, 0.625, 1 # Cobalt Blue + line_color: 1, 0, 0, 1 MDIconButton: icon: "sd" pos_hint: {"center_x": .5} MDFloatingActionButton: icon: "plus" opposite_colors: True elevation_normal: 8 pos_hint: {"center_x": .5} (以下ボタンが続くが変更ないので省略) """ ) class MainApp(MDApp): (ここも特に変更ないがself.dataについてはマニュ アル通りだと上手く表示されないので順番を逆転) ということで、コードの全容は載せられませんが、差分としては上記のように なります。 まずBoxLayoutからMDBoxLayoutに変更したところで言うと、言わずもがな ですがmd_bg_colorを使いたかったので、その影響によります。合わせて、 size関連のプロパティもまとめられるところはまとめています。なんでこう なるの?と思われた方は、以下リンクもしくは該当マニュアルをご参照下さい。 あとは本ちゃんですが、色をカスタマイズするためにmd_bg_colorとline_ colorプロパティを追加してみました。ツールバーのデフォルト色はそのまま で、それ以外のところを色変更してると思われると分かりやすいかなぁと。プロ パティの詳細は、最後あたりのAPI節をご覧ください。 色については、想像してみてくださいと言おうとしていましたが、コメントに 書いていました。色の指定については以下を参照しています。 kvに対しての色の当てはめはとても簡単で、まず上記リンクからRGBの値を取得 してきます。さらに各値を256で割った値を、それぞれrgbに当てはめています。 アルファ値については1を入れ込んでいます。 line_colorについては赤色を表示するように、値を指定しています。 実行結果 ということで差分を抽出したところは全て触れ込めたので、触れ込みは以上と します。やっぱり実行結果を見てみないことにはつかめませんよね。 一旦は、以前にButton篇を触れ込んでいたときのキャプチャを載っけてみます。 以前こうだったものがどうなったかというと、 こうなりました。自分でやったことを言うのはどうかと思いますが、なかなか イケてるUIになったかなと。初めてみたときはワオ!となりました。赤線とかも 今後領域指定の際にどれだけ領域が取れているのか見るときなんかは参考になり そうですよね。 ここで参照ページの色と照らし合わせてみます。もちろん自分で作成したわけ ではないのは前持って言っておきます。 引用元 https://www.color-sample.com/colors/476/ 少しの誤差はありそうですが、再現はほとんど出来ているのではないかと思う 次第です。 アルファ値を変えるとどうなるか 色表示についての知識がある方は、そんなの分かっているよと思われるかもしれ ませんが、アルファ値を変えた場合はどうなるのかということを見てみます。少 なくとも自分はどう変わるのかはイメージできませんでした。。 コードを以下のように変更します。 xlii/customcolor_button.py (略) - md_bg_color: 0, 0.3828215, 0.625, 1 # Cobalt Blue + md_bg_color: 0, 0.3828215, 0.625, 0.5 # Cobalt Blue alpha 0.5 (略) ほとんど端折ってますねw まぁ、でもこれでアルファ値は変更出来るので、あとは挙動を見るのみになり ます。変更したことというと、アルファ値を1から0.5に変更しただけになり ます。 実行結果② さて、どう変わるかサクサク見てみます。 微妙に薄くなりましたね。理屈としては以下を見ると手っ取りはやいとは思い ます。 まず、RGBA値の説明が以下のようにあり、 RGBAとは、コンピュータで色を扱う際の表記法やデータ形式の一つで、色を赤(R:Red)・緑(G:Green)・青(B:Blue)の三原色のそれぞれの強度と、透明度(A:Alpha)の組み合わせとして表現する方式。各要素がそれぞれ8ビット(256段階)の場合、一つの色を32ビット(約10億種類)のデータ量で表現する。 RGBとしては、多くの方がご存知の通りだとは思いますが、アルファ値の詳細は、 画像データの各画素の色情報などを表現する形式の一つで、色の情報に背景色の透過度を加えたもの。画素ごとに透過度(アルファ値)を設定することができ、複数の画像を重ね合わせたときに手前の画像の一部が半透明になって背景が透けている表現などが可能となる。 実際に表示・印刷される色はRGB各色の強度に加え、その位置の背景色を透過度に応じて反映させたものとなる。例えば、透過度が0%ならばその色そのもの、100%なら背景色そのもの、50%なら両者を半分ずつ足し合わせた色となる。 とあります。長いので、読む気があまり起こらないかもしれませんが、要は 透過ということがキーワードになりそれをどう反映させるかということになり ます。 コードと実行結果から言えば、アルファ(a)値を1にすると透過度が100%となり コバルトブルーがそのまま反映され、0にすると透過度が0%で背景色が映しだされ ます。試しに0を指定すると、元の白色が映し出されたので気になる方は試してみて はいかがでしょうか。 今回だと、0.5を指定しているので白とコバルトブルーの半々の色が映し出された ということになります。それほど難しい話ではありませんね。 他の色を試してみる ということで、調子に乗って色々試してみたいと思います。色だけにね(ボソッ)。 コバルトブルーと藍色は同じ色なのかと思ってましたが、Color Sampleのサイト を見るとどうやらそういう訳ではないみたいです。最初は藍色を選択したいと思って たので、ここではその色を追い求めたいと思います。 さきほど同様、Color Sampleのサイトから藍色を探してみます。 また、計算し直してみて、以下のようにコード反映をします。 xlii/customcolor_button.py - md_bg_color: 0, 0.3828215, 0.625, 0 # Cobalt Blue alpha 0.5 + md_bg_color: 0.16796875, 0.29296875, 0.39453125, 1 # Indigo もともとあったコバルトブルーは一旦削除しておきます。そして、計算した 藍色を上記のように反映をします。 # 藍色はIndigoでしたね頭から抜け落ちてました 実行結果② ということで、結果の方を見てみたいと思います。 なかなか良きですな、と思ったのは間違いない事実でありますね。ダーク テーマに近い名付けるとするならば、ブルーダークテーマと(勝手に)言える くらいのものはあるかなと(勝手に)思っています。 こうするとデフォルトのテーマカラーと相性が良く、デフォルトの方がバエる (引き立ってあざやかに見える。また、よく調和する。の方※参照は以下)ことが よく分かります。デフォルトと白だけになると、少しありふれたアプリになり そうなので、他の色と組み合わせることもよさそうですね。 あと余談ですが、アルファ値を0.5にしたときの様子は以下のようになります。 少しグレーに近い色になりましたね。少し背景が黒に近づくときのアルファ値 は気をつけたほうがいいかもしれません。 デフォルトのテーマカラーはどういう指定なのよ ちょっとしつこいようですが、少し気になったのでタイトルの件を調査してみた いと思います。今まで見てきた色はどういう指定をしているのか、ちょうどよい 機会だったので詳しく見ていきます。 まずはコードを以下のように変更します。 xlii/customcolor_button.py (略) MDBoxLayout: padding: dp(10) spacing: dp(10) (略) MDIconButton: icon: "sd" pos_hint: {"center_x": .5} + on_release: print(app.theme_cls.primary_color) (略) MDBoxLayout配下の1番目のボタンに、on_releaseプロパティを追加します。 これも見てもらえればすぐ分かるかと思いますが、デフォルトのテーマカラーを print実行しているだけになります。 実行結果③ ここでは特にキャプチャとかはないですが、代わりに結果の様子を載せておき ます。 [0.12941176470588237, 0.5882352941176471, 0.9529411764705882, 1.0] まず、カラーサンプルからこれかなという色を見ていました。 名前もあってドジャーブルーと言うのですね。以下のように風除けという意味が あり、もともとは素早い人を表すらしいです。なんかかっこよくて、メジャー リーグのドジャースもここからきているのでしょうか。 という寄り道は一旦置いておいて、結果の方に返ります。 ですが、カラーサンプルに書かれた値から計算をした結果、近しいのですがなか なか誤差があることとなりました。もし分かる方がいらっしゃったらコメントの 方でご指摘もらえれば。。 # ソースみろよと言われそうですが API - kivymd.uix.behaviors.backgroundcolor_behavior ということで、やりたいことは全てやりましたのでここからは使用したAPIに 入っていきたいと思います。 kivymd.uix.behaviors.backgroundcolor_behavior.BackgroundColorBehavior(**kwarg) 冒頭には以下のように述べられています。 Common base class for rectangular and circular elevation behavior. とのことです。覚えておきたいところですね。 background Background image path. background is a StringProperty and defaults to None. こちらは使ってはいませんが、なにやら画像を指定出来るのか?と期待させて くれるプロパティですね。時間あれば見ておこうっと。 r The value of red in the rgba palette. r is an BoundedNumericProperty and defaults to 1.0. g The value of green in the rgba palette. g is an BoundedNumericProperty and defaults to 1.0. b The value of blue in the rgba palette. b is an BoundedNumericProperty and defaults to 1.0. a The value of alpha channel in the rgba palette. a is an BoundedNumericProperty and defaults to 0.0. md_bg_color The background color of the widget (Widget) that will be inherited from the BackgroundColorBehavior class. (略) md_bg_color is an ReferenceListProperty and defaults to r, g, b, a. 今日のメインディッシュだったところです。上のrbgaと一緒に覚えておきたい ところですね。まぁ忘れるけど。 line_color If a custom value is specified for the line_color parameter, the border of the specified color will be used to border the widget: (略) line_color is an ColorProperty and defaults to [0, 0, 0, 0]. ここも今後レイアウトを決めるときは貴重なプロパティになりそうです。しかし、 バージョンが0.104.2からとあるので、古いバージョンを使っている方は注意が 必要です。 あとは少し端折りますが、SpecificBackgroundColorBehaviorの方も参考 になりそうです。Color Definitionsを先に見ておかないとというのもあり ますが、デフォルトのテーマカラーのhueは500とあります。ふーん、参考に なった。 まとめ さて、いかがだったでしょうか。 少し早く終わろうかなと思っていましたが、なかなか手の込んだ結果となり ました。 ここまで自由度が高くなると、色については何も心配することはないかと思われ ます。必要に応じてカラーサンプルもしくは他で色の参考になるサイトを探せば いいですしで。 気になった方は色々好きな色を見つけて、実装してみてはいかがでしょうか。 それが面倒なんだよなーと思う方はColor Definitionsから色を見つける しかないですが、こちらも結構多くの色が用意されています。 ということで今週はここまでにします!来週はElevation篇ですが、これも多い ので2週連続みたいになりそうな予感。。では来週もお楽しみに〜。 それではごきげんよう。 参照 Behaviors » Background Color https://kivymd.readthedocs.io/en/latest/behaviors/background-color/ DeepL 翻訳ツール https://www.deepl.com/ja/translator Google 翻訳 https://translate.google.co.jp/?hl=ja Cobalt Blueの色見本 - color-sample.com https://www.color-sample.com/colors/476/ 藍色の色見本 - color-sample.com https://www.color-sample.com/colors/343/ Dodgerblueの色見本 - color-sample.com https://www.color-sample.com/colors/585/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】ラズパイのIPアドレスを取得したい

はじめに ラズパイを使用してIPアドレスを取得するコードを書く機会がありました。 はじめに実装したコードでプログラムを実行したときにIPアドレスを取得できましたが ループバックアドレスが取得されるだけということがありました。 IPアドレスを取得する方法はいくつかありますが その中でも3つ簡易的なコードと合わせて紹介していきます。 対象読者 IPアドレスを取得したい方 自分と同じようにそういった機会がある人 目次 はじめに 対象読者 目次 IPアドレスを取得するプログラム 1. 冒頭でも触れたループバックアドレスを取得するプログラム 2. Pythonの標準ライブラリを使用 3. 指定したインターフェイスのIPアドレスを取得することができる おわりに 参考文献 IPアドレスを取得するプログラム 1. 冒頭でも触れたループバックアドレスを取得するプログラム 「python IPアドレス」 と検索したらまずはじめにでてくる(でてきた)IPアドレスを取得する方法 import socket ip = socket.gethostbyname(socket.gethostname()) print(ip) 2. Pythonの標準ライブラリを使用 標準ライブラリを使用してお手軽にIPアドレスを取得する方法 ネットワークに接続するために使用されるインターフェイスのIPアドレスを取得 インターフェイスを指定するわけではないが実際に使用されるインターフェイスのIPアドレスを取得することができる import socket connect_interface = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) connect_interface.connect(("8.8.8.8", 80)) print(connect_interface.getsockname()[0]) connect_interface.close() 3. 指定したインターフェイスのIPアドレスを取得することができる モジュールをインストールしてIPアドレスを取得する方法 求めているインターフェイスを明示的に指定できるため取得したIPアドレスを取得することができる import ipget target_ip = ipget.ipget() print(target_ip.ipaddr("eth0")) おわりに どのような方法で取得するのが正しいかなどはわかりませんが たくさんの選択肢があるということを念頭においてユースケースによって使い分けられたらと思います。 参考文献 PythonでIPアドレスを取得する方法【初心者向け】 Pythonでeth0のIPアドレスを取得するにはどうすればよいですか? ラズバリーパイのIPアドレス取得方法について
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MATLAB x OpenAI Gym: 強化学習アプリでラクして強化学習!

はじめに MATLAB R2021a でリリースされた強化学習を GUI から実行可能な強化学習デザイナーアプリは、直感的に小難しい強化学習の 設定 学習 シミュレーション が実行できるとして、MATLAB ユーザーが乱舞狂乱したのは記憶に新しいことかと思います。本記事は、この機能を使って楽して OpenAI Gym の環境を強化学習するという試みをご披露します。 なお、当該アプリを使うと MATLAB/Simulink 下での強化学習はもはや Challenging でも何でもないため、ここではあえていばらの道、 Python (OpenAI Gym) 連携 に取り組みたいと思います。 強化学習デザイナーアプリ: 強化学習用に Python の仮想環境を用意する デフォルトでの MATLAB の Python 設定は以下のような感じ: 注意 MATLAB から Python を操作することが可能ですが、この準備ができているのが大大大前提です* *Python の MATLAB からの利用についてはこちら この環境に OpenAI Gym ライブラリをインストールしても良いのですが、Python のライブラリ間の依存関係 (ヴァージョン揃えたり) の解決には時間がかかるので、ここではあっさり以下のようにターミナルから強化学習用に Python の仮想環境を用意します。因みに anaconda 使っています: Console conda create --name matlab-rl python=3.7 Open AI Gym をインストールします。 conda pip がありますが、conda でインストールに苦戦したので、”えいや”で pip で以下のような感じでインストール: Console pip install gym==[version] pip install gym[atari] ここで、MATLAB から Python 連携を行うために pyenv コマンドを使いますが、上で作成した Python 仮想環境を MATLAB で使うために: Code(Display) rlPython = {'Version','C:\Users\HYCE\anaconda3\envs\matlab-rl\python.exe'}; pyenv(rlPython{:}) と Python を切り替えておきます。 OpenAI Gym 環境 Open AI Gym から1つ強化学習用の環境を引っ張ってきます: Code myenv = py.gym.make('MountainCar-v0') Python での実装は GitHub OpenAI Gym をご覧ください。 環境の絵を見てお分かりいただける通り、車を左右に加速させて、 山を登りきることを目指すための単純な環境です。観測は(x,y) 座標と、速度、そして報酬信号、と終了条件達成フラグ (isdone 信号)が得られると考えられます。 この環境の詳細 結局、環境の I/O だけ分かれば良いので情報を集めます: 環境の"観測"を行う 上の情報に依ると入手できる情報は以下の2つ: Car position Car velocity 確認しましょう。 Python オブジェクトのプロパティを表示するために、details 関数を使います。 Code details(myenv.reset()) アクション後のオブジェクトにある data プロパティが恐らく、観測データと睨む: Code res = myenv.reset(); res.data 間違いない... こいつらが観測データの2つだ。 ランダムな行動に対する応答をレンダリングして表示する myenvオブジェクトを触っていると、"いかにも"なメソッドが見つかります step render あたりのメソッドがアヤシイので、これを使ってランダムな行動に対して、 Position (観測1: 位置) Velocity (観測2: 速度) Reward (報酬) isdone (終了条件達成フラグ) を確認できるはずです。 Code for i = 1:1000 action = int16(randi([0,2],1)); myenv.step(action); myenv.render(); end あ、動いた... 環境から戻ってきたデータを受け取る準備 動作確認ができたので、最後の1つの行動 (action) を使って出力を確認: Code result = myenv.step(action) きっとこれが、[Position, Velocity, Reward, isdone] だ! これを MATLAB で受け取る。 ここで、MATLAB <--> Python 技: "とりあえず cell で受ける" を使う: Code % Python の結果をとりあえず cell で受けておく result = cell(result) % 観測 (Observation) だけ MATLAB に渡すことができるか確認 Observation = double(result{1})' % 報酬 Reward = double(result{2}) % isdone? IsDone = boolean(double(result{3})) これでとりあえず、MATLAB で扱うことができる変数型に変換することができました。ここまでくればあとは MATLAB World での作業なので大分楽になります。 Code % 最後に閉じる myenv.close(); 以上で実験終了。MATLAB で強化学習を実行する準備が整いました。 カスタム環境ラッパークラスを作成する 今までやってきたことを念頭に置いて、Python で作成した"環境" >> MATLAB の"環境" とする必要があるので、カスタム環境を作成します。次のリンクから作り方を真似します:Create Custom MATLAB Environment from Template Code(Display) rlCreateEnvTemplate("MountainCar_v0"); と実行すると、MountainCar_v0 環境クラスのテンプレートが作成されます。ここがスタートポイントです。上記の情報を引き抜く技をテンプレートに適用し、MountainCar_v0.mを次のように定義します。 MountainCar_v0 クラス Code classdef MountainCar_v0 < rl.env.MATLABEnvironment %MOUNTAINCAR_V0: Template for defining custom environment in MATLAB. %% Properties (set properties' attributes accordingly) properties open_env = py.gym.make('MountainCar-v0'); end properties % Initialize system state [x,dx/dt]' end properties(Access = protected) % Initialize internal flag to indicate episode termination IsDone = false; end %% Necessary Methods methods % Contructor method creates an instance of the environment % Change class name and constructor name accordingly function this = MountainCar_v0() % Initialize Observation settings ObservationInfo = rlNumericSpec([2 1]); ObservationInfo.Name = 'Mountain Car States'; ObservationInfo.Description = 'Position x, Velocity dx/dt'; % Initialize Action settings ActionInfo = rlFiniteSetSpec([0 1 2]); ActionInfo.Name = 'Mountain Car Action'; % The following line implements built-in functions of RL env this = this@rl.env.MATLABEnvironment(ObservationInfo,ActionInfo); end % Apply system dynamics and simulates the environment with the % given action for one step. function [Observation,Reward,IsDone,LoggedSignals] = step(this,Action) LoggedSignals = []; % Observation result = cell(this.open_env.step(int16(Action))); Observation = double(result{1})'; % ndarray >> MATLAB double % Reward Reward = double(result{2}); % is Done? IsDone = boolean(double(result{3})); if (Observation(1) >= 0.4) Reward = 0; IsDone = true; end this.IsDone = IsDone; % (optional) use notifyEnvUpdated to signal that the % environment has been updated (e.g. to update visualization) notifyEnvUpdated(this); end % Reset environment to initial state and output initial observation function InitialObservation = reset(this) result = this.open_env.reset(); InitialObservation = double(result)'; % (optional) use notifyEnvUpdated to signal that the % environment has been updated (e.g. to update visualization) notifyEnvUpdated(this); end end %% Optional Methods (set methods' attributes accordingly) methods % Helper methods to create the environment end methods (Access = protected) % (optional) update visualization everytime the environment is updated % (notifyEnvUpdated is called) %function envUpdatedCallback(this) %end end end 最初のプロパティメンバと、コンストラクタ、step 関数、初期値の設定、あたりをお馴染みのコマンドで記述すれば良いだけです。Python のデータの渡し方を事前に検証しておいたので、スムースに出来ると思います。 環境インスタンスをカスタム環境クラスから作成する ではカスタム環境クラスからインスタンスを作成します。これが今回学習する MATLAB での環境の定義になります。 Code matEnv = MountainCar_v0(); 一応チェックしておきます。カスタムで作った場合は、この関数 validateEnvironmentを使います。 Code validateEnvironment(matEnv) これは、 初期の観測と行動を作成 1-2 step だけ、環境をシミュレーション を実行し、強化学習の学習が大丈夫か事前にチェックできます。問題が無ければそのまま進みます。 学習 以降は強化学習デザイナーアプリを使って実施。以下の流れです 学習のセットアップ 学習 アプリ内でのシミュレーション Critic & Agent のエクスポート エクスポートしたものを使ってシミュレーション 1-4 までアプリ内で実行可能です。 設定 環境は先ほど作った matEnvです。agent は以下のように指定: DQN Critic ネットワークの幅は 256 学習の設定は次のようにします: 学習実行 実行ボタンをクリックして、あとは学習終了まで待ちましょう .... シミュレーションと可視化 学習終了したら、アプリからシミュレーションを実行しても良いですが、この場合レンダリングされず、車が動いている様子が見えないためモデルをエクスポートして、マニュアルシミュレーションを実行します。 学習後にモデルをエクスポート agent や agent の要素をエクスポートできます。深層強化学習のネットワークだけエクスポート: Critic のネットワークがワークスペースに落ちてきます。この設定だと agent_criticNetwork のはずです。 R2021a ではエクスポートされたネットワークは DAGnetwork オブジェクトでしたが、R2021b では dlnetwork オブジェクトです。そのため、ネットワークに渡す変数型がR2021b のものでは dlarray が要求されます。以下の可視化プログラムではそこに配慮を入れて、どちらでも動作するようにしています。 マニュアルシミュレーションを実行して車が登るか確認 可視化するコードを用意して、無理やり Gym のレンダリングをさせます。このあたりは、MATLAB と Python を連携させる際に、一手間必要なところになってしまいますが、本丸の強化学習の方で楽できるので、Qiita ユーザーなら屁でもないでしょう。 環境 学習したネットワーク (Critic) シミュレーション回数 を渡します: Code visSim(matEnv,trainedCritic,5); visSim.m コード内容こちら Code function visSim(env,net,numSim) % Run Simulation using "step" method % % if isa(net,"dlnetwork") % in case of dlnetwork dlnetFlag = true; end for i=1:numSim state = single(env.open_env.reset()); % initial [x, dx/dt] if dlnetFlag % dlnetwork state = dlarray(state,"BC"); end isDone = false; % Break criterion steps = 0; % step counts while(~isDone && steps < 200) % Take the best action according to state % Note that the network accepts S --> Q(S,A) [~,action] = max(predict(net,state)); if dlnetFlag action = extractdata(action); end % Recieve result from the environment: action \in {0,1,2} result = cell(env.open_env.step(int16(action-1))); % Display OpenAI Gym environment env.open_env.render(); % new [x, dx/dt] new_state = single(result{1}); % isDone signal isDone = boolean(single(result{3})); % stop criterion if (new_state(1) >=0.49) isDone = true; end state = new_state; % update [x, dx/dt] if dlnetFlag % dlnetwork state = dlarray(state,"BC"); end steps = steps + 1; end env.open_env.close(); % Close OpenAI Gym environment end end おー上手く動いた動いた: まとめ MATLAB の強化学習アプリを使って、OpenAI Gym の環境を学習しました Python のレンダリングを生かすには、マニュアルシミュレーションが必要でした 強化学習アプリのお陰で、かなり作業が楽になります。その分、Python での環境を使ってみようかなと、元気が出ました では、みなさん LGTMをしていただいて、良いお年をお迎えください
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python3】Streamlitの基礎

Streamlitとは 簡単に言うと、Webアプリケーションフレームワークです。 Streamlitを使用する大きなメリットとしては、 Pythonスクリプトを書くだけで、アプリケーションの構築が可能であるということです。 Webアプリケーションを作成する場合は、基本的にはJavaScriptやhtml/CSSでフロント部分の構築が必要になりますが、Streamlitを使用すれば、PythonのみでWebアプリケーションを作成することができます。 また、Pythonの学習のみを行っていて、フロントエンドに関する知識がまだない方や、簡単にWebアプリケーションを作成してみたいという方にお勧めです。 インストール方法 以下のコマンドをターミナル上で実行することで、インストールが可能です。 $ pip install streamlit 実際にインポートしWebアプリケーションの動きを見る インストールが完了したら、Pythonスクリプトでコマンドを実行するだけで、アプリケーションの起動ができます。 # streamlitのインポート import streamlit as st # ターミナルに以下のコマンドを入力 $ streamlit run main.py コマンド上にLocal URLとNetwork URLが表示されるので、 Local URLをクリックしブラウザにアクセスすることができていれば起動完了です。 Streamlitの基礎 Webアプリケーション上に文字を表示させるには以下のコードを入力します。 import streamlit as st st.title('For Qiita') # タイトルの表示 st.write('Explain') 実際に実行し、Webアプリケーションを更新すると以下の様に画面に表示することができます。 また、マークダウン記法にも対応しています。 以下の様に、ダブルコーテーション内にマークダウン記法を記述することで、Webアプリケーションに適用することができます。 """ # Qiita ## Qiita2 ### Qiita3  ```  print('Qiita')  ``` """ 実際に実行後の画面です。 表データの作成 データフレーム PandasのDataFrameを用いてデータの可視化を行います。 st.dataframeで表データを表示させることができます。 また引数にwidth,heightを設定することもでき、表の大きさも自分好みにできます。 import streamlit as st import pandas as pd st.title('For Qiita') # Pandasを使用し、データフレームを作成 df = pd.DataFrame({ '1St':[1,2,3,4], '2nd':[10,11,12,13] }) # Webアプリケーション上に表示 st.dataframe(df,width=300,height=300) 実行後の画面です。 実際に表データがWebアプリケーション上に作成されています。 折れ線グラフ numpyを使用しランダムな値を取得し、それを折れ線グラフで表示させていきます。 import streamlit as st import numpy as np import pandas as pd st.title('For Qiita') chart_data = pd.DataFrame( np.random.randn(20, 3), columns=['a', 'b', 'c'] ) st.line_chart(chart_data) 実行後の画面です。 Webアプリケーションに表示されたデータに関しては、画像として保存できたりもするので、とても便利です。 地図データ 緯度経度を使用し、地図上にプロットしていきます。 今回使用しているのは、大阪府の緯度経度です。 以下、実際のコードです。 import streamlit as st import numpy as np import pandas as pd st.title('For Qiita') df = pd.DataFrame( np.random.rand(100,2)/[50,50] + [34.6,135.5], columns=['lat','lon'] ) st.map(df) 実行後の画面です。 まとめ 今回紹介したのは、ほんの一部のAPIのみになります。 ですが、別の言語を使用することなく、少ないコードでWebアプリケーションを使用することができました。 Streamlitの公式ドキュメントには、今回紹介できていないものが沢山記載されていますので、興味のある方はドキュメントも参考にしてください。 また、StreamlitとGitを使用することで、Webアプリケーションを公開することも可能ですので、公開したい方は調べてみるのもよいかと思います。 まだまだ、初心者ですが皆様の役に立てれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python3】Streamlitで簡単にWebアプリケーション作成

Streamlitとは 簡単に言うと、Webアプリケーションフレームワークです。 Streamlitを使用する大きなメリットとしては、 Pythonスクリプトを書くだけで、アプリケーションの構築が可能であるということです。 Webアプリケーションを作成する場合は、基本的にはJavaScriptやhtml/CSSでフロント部分の構築が必要になりますが、Streamlitを使用すれば、PythonのみでWebアプリケーションを作成することができます。 また、Pythonの学習のみを行っていて、フロントエンドに関する知識がまだない方や、簡単にWebアプリケーションを作成してみたいという方にお勧めです。 インストール方法 以下のコマンドをターミナル上で実行することで、インストールが可能です。 $ pip install streamlit 実際にインポートしWebアプリケーションの動きを見る インストールが完了したら、Pythonスクリプトでコマンドを実行するだけで、アプリケーションの起動ができます。 # streamlitのインポート import streamlit as st # ターミナルに以下のコマンドを入力 $ streamlit run main.py コマンド上にLocal URLとNetwork URLが表示されるので、 Local URLをクリックしブラウザにアクセスすることができていれば起動完了です。 Streamlitの基礎 Webアプリケーション上に文字を表示させるには以下のコードを入力します。 import streamlit as st st.title('For Qiita') # タイトルの表示 st.write('Explain') 実際に実行し、Webアプリケーションを更新すると以下の様に画面に表示することができます。 また、マークダウン記法にも対応しています。 以下の様に、ダブルコーテーション内にマークダウン記法を記述することで、Webアプリケーションに適用することができます。 """ # Qiita ## Qiita2 ### Qiita3  ```  print('Qiita')  ``` """ 実際に実行後の画面です。 表データの作成 データフレーム PandasのDataFrameを用いてデータの可視化を行います。 st.dataframeで表データを表示させることができます。 また引数にwidth,heightを設定することもでき、表の大きさも自分好みにできます。 import streamlit as st import pandas as pd st.title('For Qiita') # Pandasを使用し、データフレームを作成 df = pd.DataFrame({ '1St':[1,2,3,4], '2nd':[10,11,12,13] }) # Webアプリケーション上に表示 st.dataframe(df,width=300,height=300) 実行後の画面です。 実際に表データがWebアプリケーション上に作成されています。 折れ線グラフ numpyを使用しランダムな値を取得し、それを折れ線グラフで表示させていきます。 import streamlit as st import numpy as np import pandas as pd st.title('For Qiita') chart_data = pd.DataFrame( np.random.randn(20, 3), columns=['a', 'b', 'c'] ) st.line_chart(chart_data) 実行後の画面です。 Webアプリケーションに表示されたデータに関しては、画像として保存できたりもするので、とても便利です。 地図データ 緯度経度を使用し、地図上にプロットしていきます。 今回使用しているのは、大阪府の緯度経度です。 以下、実際のコードです。 import streamlit as st import numpy as np import pandas as pd st.title('For Qiita') df = pd.DataFrame( np.random.rand(100,2)/[50,50] + [34.6,135.5], columns=['lat','lon'] ) st.map(df) 実行後の画面です。 まとめ 今回紹介したのは、ほんの一部のAPIのみになります。 ですが、別の言語を使用することなく、少ないコードでWebアプリケーションを使用することができました。 Streamlitの公式ドキュメントには、今回紹介できていないものが沢山記載されていますので、興味のある方はドキュメントも参考にしてください。 また、StreamlitとGitを使用することで、Webアプリケーションを公開することも可能ですので、公開したい方は調べてみるのもよいかと思います。 まだまだ、初心者ですが皆様の役に立てれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Google Colaboratory の matplotlib で日本語を使う方法

インストール、インポート !pip install japanize-matplotlib import matplotlib.pyplot as plt import japanize_matplotlib 例 !pip install japanize-matplotlib import matplotlib.pyplot as plt import japanize_matplotlib import numpy from scipy.stats import norm a_05, a_95 = norm.interval(alpha=0.95) x_value = 4 x = numpy.linspace(x_value * -1 , x_value, 1000) x_95 = numpy.linspace(a_95, x_value, 100) x_05 = numpy.linspace(x_value * -1, a_05, 100) plt.plot(x, norm.pdf(x)) plt.fill_between(x_95, 0, norm.pdf(x_95), facecolor='lightskyblue', alpha=1) plt.fill_between(x_05, 0, norm.pdf(x_05), facecolor='lightskyblue', alpha=1) plt.text(0, 0.45, r'正規分布 両側検定の棄却域(有意水準5%)',horizontalalignment='center') plt.xlim(x_value * -1, x_value) plt.ylim(0, 0.5) plt.show()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【データ分析】伊藤園の株価と気温の関係性を調べてみた

はじめに スーパーやコンビニなどで売ってある飲み物は、暑いときほど売れやすい気がしませんか? その過程が正しいならば、飲料メーカーの株価は気温が高くなるほど上がりやすいのでは?と考えました。 そこで今回は飲料メーカー大手の伊藤園の株価と東京の気温データを用いて関連を調査。 データ・手法 伊藤園の株価データとしてStooqから1日ごとの終値を使用。 Pythonで株価データは簡単に取得可能で、取得方法はこちらに記載。 東京における気温データは気象庁から日平均気温・最高気温・最低気温を使用。 順序通りに進んでいけば、問題なくダウンロード可能だと思います。 対象期間は2013年1月〜2020年12月までの8年間。 株価データは土日祝などが無いため、欠損値は含めずにデータを使用。 そのデータの相関係数を計算することで、株価と気温に関連があるかを調査。 以下のように解釈可能。 相関係数が正:気温が高くなるほど株価が上がりやすい 相関係数が負:気温が低くなるほど株価が上がりやすい これを下記3パターンで実施。 平均気温 vs. 終値 最高気温 vs. 終値 最低気温 vs. 終値 なお、対象期間内の各データの変化はこのような感じ。 結果 全期間で計算した場合における株価と各気温の相関係数はこのようになりました。 vs. 平均気温 vs. 最高気温 vs. 最低気温 0.044 0.059 0.039 季節ごとにも分けてみました。季節は下記で分類。 春:3〜5月 夏:6〜8月 秋:9〜11月 冬:12〜2月 季節 vs. 平均気温 vs. 最高気温 vs. 最低気温 春 -0.02 0.013 -0.038 夏 -0.064 -0.004 -0.098 秋 -0.076 -0.044 -0.082 冬 0.152 0.149 0.132 まとめ 今回はあまりうまくいかず。 0.2程度ではとても相関が...とかは言えませんね。 当然と言えば当然ですが、株価はそんな簡単なものでは無いということですね...。 その他 興味が湧いた方は、ブログにより詳しく書いているのでご覧いただけると嬉しいです。 実装したソースコードはGitHubにも置いています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AIへの好奇心が強すぎて公務員を退職し、『この人狂ってる』と言われた未経験エンジニアの競馬予想AIアプリ開発記録

0. はじめに 0.1 あいさつ ジーズアカデミーAdvent Calendar 2021の2日目の記事を担当する髙橋(@philosophy_note)です。 4月〜11月までTOKYO LABコースに所属しておりました。 11月4日に開催されたGGAにも登壇いたしました。 現在は主にデータエンジニアを目指して就職活動をしております。 ちなみにGGAでは主に機械学習モデルの開発過程と競馬の回収率の過程(1日9%を記録したこともあります笑)をとにかく熱く語り、次のような感想をいただきました。 「この人変態だ笑」 「いい意味で狂ってる」 「完璧に狂ってくれたので安心しました」 0.2 開発アプリ概要 アプリ名:競馬予想AI×SNS 「Horecast」 機能一覧 AI予想表示機能 データ収集から機械学習モデル構築に至るまで、私が0から一気通貫して開発した競馬予想モデルの予想内容を見ることができます。 開催情報・出走馬の一覧はもちろん、レース結果に加えて購入を推奨した馬券の的中結果・払戻金も確認することができます。 また、データは10分ごとに更新されます。 ダッシュボード 機械学習モデル開発時に使用した2010年から2021年の競馬レースデータを見ることができます。 競馬場とレース形式(芝・ダート・障害)と距離で検索すると、次の図のようなグラフが表示されます。 具体的には枠順・騎手・調教師・種牡馬ごとに勝率・連対率・複勝率を比較することができます。 項目を追加すると直ちにグラフも追加されます。 予想共有・検索機能 他のユーザーの予想を見ることができます。検索機能も備えています。 自分の予想結果を投稿することもでき、レース終了後に予想の反省を投稿し、次回以降の予想の参考とすることができます。 プロダクト設計仕様 (アプリ部分) HTML5 CSS3 Bootstrap5 Javascript jQuery3.6.0 Python 3.8.8 Django 3.2 Dash 2.0.0 PostgreSQL12.4 (モデル部分) Python 3.8.8 Beautiful Soup4 4.9.3 pandas 1.3.2 Numpy 1.19.2 seaborn 0.11.2 matplotlib 3.4.2 LightGBM 3.1.1 OPTUNA 2.6.0 scikit-learn 0.24.2 (インフラ・開発ツール) Heroku(Time Scheduler) AWS(Glue / S3) pgAdmin(DB管理ツール) Notion(タスク管理) Github(コード管理) Anaconda(Python実行環境) JupyterLab(Python実行環境) 0.3 執筆目的・対象読者・記事構成 記事の目的は機械学習モデルを用いたオリジナルアプリの開発経験の共有です。 開発手順や難所の突破方法、反省点や使用したツールなどを記載しています。 対象読者としては、 今後オリジナルアプリを開発するG's ACADEMY現役生 G's ACADEMYに興味を持っており、どんな人がいて、どんな物を開発しているのか気になる方 G's ACADEMYに限らず何らかの目的で個人開発しようとしている方 AIってどうやって作るのか興味がある方 競馬が好きな方 上記のいずれかに該当するのであれば是非読んでいただきたいと思います。 記事の内容について説明します。 「1.自己紹介」ではG's ACADEMYに入学した経緯と過ごし方そして今回のアプリの開発理由について記載しています。 「2.開発ロードマップ」〜「5.アプリ制作」で開発過程について記載しています。 「6.難所とその解決方法」では開発で詰まったところとその解決方法についてまとめています。 「7.反省点など」は開発後に後悔している内容を記載しています。 「8.さいごに」はまとめです。 「おまけ」は開発で使用したツールや学習教材などを記載しています。 1. 自己紹介 1.1 略歴(〜ジーズ入学前) 大学卒業後5年間都内の公的機関で働いておりました。 大学時代にデータ分析をしていた経験からAIに興味を持ち始めました。 AIを学ぶには自分で作ることが一番良いだろうと考え、 趣味である競馬予想の知識を元に実際にAIを作り始めました。 やがてプログラミングの楽しさに気づき、仕事を退職し、G's ACADEMYに入学しました。 なお、入学時にはPythonの知識は多少ありましたが、いわゆる"WEB系"の知識はゼロでした。 (例えばPythonでWebアプリケーションを作れることはジーズ入学後に知りました) 1.2 ジーズでの日々 G's ACADEMY LABコースは最初の2.5ヶ月間でJavascriptやPHPを学び、最低でも毎週1つWEBアプリケーションを作成するカリキュラムでした。 私が作成したものを一部抜粋してご紹介します。 Djangoの存在を知ってから2週間、HTML・CSSを学んでから10日間、Javascriptを学んでから1週間。『じゃんけんアプリを作る』という課題でDjangoを使用した読書レビュー掲示板を作成 『チャットアプリを作る』という課題をほぼ無視してPythonでTwitterAPIを操作して文章を取得し、Janomeで形態素解析した単語をマルコフ連鎖で適当にくっつけたり、WordCloudを作成 JavascriptハッカソンでDjangoを利用したWEBアプリケーションを作成し2位入賞 データベースを使えるようになったことが嬉しすぎて、とりあえずPythonでGAFAの株価5年分を取得して全てデータベースに格納したはいいがデータの数が多すぎて何もできなくなる PHPハッカソンではとうとうHTML・CSS・Javascript・PHPを一切使わずPythonだけでWEBアプリケーションを作成 次のリンクから作成物の一覧を確認できます。 また、一部開発物についてはG’s ACADEMY 学校長である山崎先生が作成した次の動画で説明もしています。 そして、後半期間でオリジナルアプリを開発します。 私は『AIを自分で作りたい』という思いを実現するため、 入学前から取り組んでいた競馬予想モデルをアプリケーション化することにしました。 1.3 競馬予想AI×SNSアプリの開発理由 さて、G's ACADEMYでは「why me?」という問いかけが重要視されています。 「why me?」を言い換えると「なぜあなたがそれをやるのか、作るのか?」という問いかけになります。 私の答えは次です。 AIの中身が知りたい。知るためには作ることが一番。だから作る。 …これだけです。 G'sの偉大な先輩方と比較したらなんと些細なことか。 まあwhy meは比較しようもないし、好奇心持ったからには思いっきりやろう そう自分にいつも言い聞かせていました。 と言いつつももう少し踏み込んで話します。 データへの興味がAIへの興味に繋がったことはお伝えしました。 では何故私はデータに興味があるのでしょうか? 私は小学生のころから統計表やグラフが大好きでした。 直接体験しなくても数字を通せば色々なことがわかります。 仮説を立てて検証することができます。シミュレーションすることができます。 私にとってデータは想像の原動力でした。 大学で計量経済学を専攻した理由もデータから色々な情報を引き出したいという欲求に従った結果です。 一方で実際にデータに触れることでデータの持つ恐ろしさも体験しました。 恣意的にカットすることにより、自分の主張が通るように都合のいい根拠を生み出してしまう本や論文を見てきました。 こうした点からデータの学習により構築されるAIについて理解したいという思いが生まれました。 また、ユーザーストーリーで後述する内容にもこの点が現れています。 世間では『競馬予想AI』がたくさん存在しています。 AIの予想結果を有料で販売しています。中身はブラックボックスです。 有料で販売することに対して不快感を覚えます。 加えて勿体ないなという思いも抱いていました。 色々な競馬の予想方法がありますが、私はデータを元に予想することが楽しみでした。 他人の予想を見ることも一つのデータだと思っています。 けれどもAIはブラックボックスです。根拠を示しているAIもありますが少数派です。 AIの予想を鵜呑みにせず、自分の頭で考えた予想で競馬を楽しむことを広めたい これがもう一つの開発理由です。 2. 開発ロードマップ 2.1 開発工程 開発工程は次の通りです。 時期 工程 8月上旬 機械学習モデル作成開始・追加データ準備 8月中旬 機械学習モデル作成完了(以後毎週末レースを予測し、修正を繰り返す) 8月下旬〜9月上旬 アプリ要件定義開始・技術選定 9月中旬 出馬表及び予想表示機能開発・仮デプロイ 9月下旬 要件定義終了・結果表示機能開発 10月上旬 ダッシュボード・予想共有機能開発 10月10日 完成・提出(以降、GGAまでエラー・バグ対応とデザイン修正)   振り返るとタイトなスケジュールだったと思います。 最終的な要件定義から完成まで2,3週間で一気に仕上げました。 ユーザーテストを行いたい場合はもっと早めに開発しないと絶対に間に合いません。 2.2 優先順位 次のようにレベルを決めていました。 開発レベル 内容 レベル1 Django内部で予測して結果を返す レベル2 自動でデータを取得する(ここまでがコア機能) レベル3 馬券の的中結果を表示する レベル4 ダッシュボード レベル5 予想共有機能 今後開発される方は次の言葉を覚えていただきたいです。 とにかくコア機能を早期に完成させてデプロイする レベル1と2がこのアプリのコア機能に当たる部分でした。 従って要件定義やワイヤーフレームの作成と並行して機械学習モデルとその結果を表示する部分の開発を真っ先に行いました。 また、最低限の機能が完成したところですぐにサーバーにデプロイしました。 今回の場合も出馬表のスクレイピング機能をHerokuにデプロイしたところ、 Herokuのリクエストタイムアウトの関係で1件も取得できないことが判明しました。 また、ダッシュボードをデプロイしたところ、メモリ超過でサーバーが強制的にシャットダウンしました。 提出3日前のことです。 早めにデプロイしましょう。 3. 要件定義 実は当初はGGAに出場することは考えていませんでした。 プロダクトの機能も次のように自分だけが使うことが考えるように設計していました。 毎週末の競馬予想をする競馬予想モデルを作成する 予想に必要なデータは自動取得する(結果の取得は行わない) DjangoでWEBアプリケーション化する(モデルの結果を表示するだけ) しかしながら、メンターさんの勧めや自分自身の心境の変化もあり、GGAへの出場も視野に入れることになりました。 そうなると、上述した内容だけではサービスとしては不十分でした。 3.1 ユーザーストーリー そこでメンターさんからユーザストーリーを作成することを勧められました。 ユーザーストーリーについては次の記事などで確認してください。 自分が作った最初の図が次になります。 メンターさんからは『これではただ機能の羅列ですね』と言われました。 『まず考えるべきなのはバックボーンです』が次の言葉でした。 つまり『ユーザーはどういう考えを持ってアプリを利用(今回の場合は競馬予想)するのか』をまず考えます。 次にユーザーの利用時の考えを時系列順に考えます。 その考えを具体的な行動に落とし込みます。これがナラティブフローになります。 最終的にユーザーの動きを想定し、優先順位をつけてどの機能を実装するかを決めます。 最終的には次の図のようになりました。 3.2 ワイヤーフレーム作成(できず) 機能が固まったところでワイヤーフレームを作成します。 最初の自分用のプロダクトを作成した際には次のNoteを参考にしながらFigmaで作成しました。 ただしここで重大なミスをします。 上述したように最終的なユーザーストーリーが完成したのが、提出期限の2週間ほど前のためワイヤーフレームを再度作る時間がなく、 ノートに手書きで簡単なイメージを作成し、細かいところは作りながら修正すればいいという考えで先に進めました。 これが次の工程に響くことになります。 ちなみに当初作成していたワイヤーフレームの一部が次になります。 完成品と全く異なります。 3.3 ER図作成 ワイヤーフレームが固まった段階でER図を作成します。 最初は画面イメージとER図の関係性が今ひとつ掴めていませんでしたが、 画面イメージなしにER図を作ると後々苦労します。 具体的には、データベースにどのような値が格納されるのか、 どのタイミングでどのデータを取得するのかは画面を作る段階を踏んでから先に進まないと 正確なER図を作ることができません。 私の場合予想表示機能についてはワイヤーフレームを作成していたので特に問題なく進めることができました。 しかしながら、予想共有機能の画面推移をまともに設計していなかったので、 作ったテーブルを壊し、再度設定からやり直すことを何回もしてしまいました。 せめて手書きで画面表示を考えておけばよかった… 同じく当初作成していたER図です。最終形はこの倍のテーブルを作成しました。 3.4 技術選定・アーキテクチャ ジーズ生はここで悩む方は多いと思います。 私の同期でVueやReactを学習した人はNuxt.jsやNext.jsでフロント中心で開発し、DBはFirebaseを使うケースが多数でした。 その一方でLaravelで本格的なWEBアプリケーションを作成する人もいました。 私の場合は ・FastAPIでAPIサーバーを構築し、別にWEBサーバーを構築する ・Djangoの内部にモデルを組み込み全て完結させる の2択で迷いました。 最終的には開発経験があることやドキュメントの充実さから後者を選択しました。 Dockerでコンテナを作ることも検討しましたが、あくまで個人開発という観点から見送りました。 (ただモダンな技術で開発することを重視するのであれば次の本のようにDocker×FastAPIが良かったのかな…) DBに関してはDjangoとセットで使用されている事例が多いという理由でPostgreSQLを採用しました。 モデル部分とバックエンドに時間を割きたかったのでフロントエンドはBootstrapとjQueryで簡潔に済ませることにしました。 4. モデル制作 4.1 データ収集 競馬情報サイトより分析に必要なデータをスクレイピングで取得しました。 取得したデータは次の通りです。 ・レースデータ(2011~2020の10年分の中央競馬で開催された全レースの結果) ・競走馬データ(上のレースに出走した競走馬のレース結果のデータ、血統も含む) ・騎手データ(2011~2020の10年分の騎手の成績・獲得賞金) ・調教師データ(2011~2020の10年分の調教師の成績・獲得賞金) またレースの予想対象となる出馬表についても同じくスクレイピングで入手しました。 スクレイピングはPandasとBeautifulsoupで実装しました。 単勝オッズなどリアルタイムで変化するデータに対してはSeleniumで取得することも考えましたが、 使用しなくても予想はできるので採用は見送りました。 (もう一つ見送った理由としてブラウザを直接操作するため取得に時間がかかるという点もありましたが、 今考えてみると高々36レースの出馬表の取得にそこまで時間はかからないので採用してもよかったのかもしれません) 4.2 データ前処理 取得したデータをPandasやNumpyを使用してクレンジングします。 次以降の行程次第で何度もやり直すことになります。地味な作業ですが大切な行程です。 4.3 EDA(探索的データ解析) 取得したデータについてデータ型や値の分布、欠損値、外れ値をチェックします。 また、目的変数と各変数の相関や関係性を把握します。 その後Yes/Noで答えられる仮説を立てながらデータを分析していきます。 立てた仮説は例えば次のようなものです。 斤量が前走と比べて小さくなると着順が良くなる コースの距離が前走と比べて短くなると着順が良くなる ダートレースはパワーを要するため馬体重が重い方が着順が良くなる こうした仮説を記述統計量の数値比較やヒストグラム・クロス集計図といった図示すること確認していきます。 ちなみに仮説は自分の経験から立てたものあれば、競馬予想の本を買ってそこに書いてある内容を再検証したものもあります。 4.4 特徴量エンジニアリング 3.3と同時並行で行っていました。 カテゴリ変数の作成や順位変換、テーブル結合といった手法を用いて EDAで検証した仮説をモデルに入力できるように変数を変換します。 4.5 モデル構築・性能評価 使用するモデルは使いやすさと精度の高さからGBDTを採用しました。 使用したライブラリはLightgbmになります。 パラメータチューニングはグリッドサーチを当初採用していましたが、 手数がかかるので、ベイズ最適化を行うフレームワークであるOptunaで求めた値でモデルを構築しました。評価指標はAUCを採用し、クロスバリデーションで評価を行っています。 5. アプリ制作 5.1 予想表示機能 一言で予想表示機能と言っていますが、予想だけでなく結果や払戻金も取得して表示しています。 仮デプロイで判明したスクレイピングできないという問題はHeroku Schedulerでの定期実行で解決しました。 5.2 ダッシュボード機能 インタラクティブなグラフ(入力値に応じてすぐに変化するグラフ)を表現するため、 PlotlyDashを採用しました。 使用するデータはAmazon S3に保存し、AWS Glueを使用してcsv形式から軽量なParquet形式に変換しています。 5.3 予想共有掲示板機能 基本的なCRUD操作が中心ですが、 DjangoのModelChoiceFieldを利用してレースを選択すると、該当するレースの出走馬が選択肢として自動的に表現されるように 設定しました。 views.pyでmodel.pyからform.pyに値を受け渡す箇所で苦労しました。 6.問題の解決方法 開発中色々困難がありましたが、技術面は全て一人で解決しました。(後述しますがこれは反省ポイントです。) 心がけていたのは検索だけでなく公式ドキュメントを読むようにするです。 エラーなどが発生した場合は検索すれば色々な忘備録や質問への返答が出てきます。 これだけで解決する場合もありますが、なるべく公式ドキュメントで確認することを心がけていました。 以前は対応できていましたが、バージョンが変わったことで今ではその方法が通用しないことがあります。 例えばPandasのデータフレームをPostgreSQLでDBに保存する際にpangresというライブラリを用いました。 この時ネットの記事を参考にしたのですが、記事通りにしても上手くいきません。 仕方がないので公式ドキュメントを参照したところ、バージョンが変更になり、命名規則などがかなり変わっていました。 他にもPlotlyDashやS3は使用例が少なかったり、バージョンの移り変わりが激しかったので公式ドキュメントが頼りになりました。 7.反省点 7.1 UI/UXにこだわることができなかった ワイヤーフレームの仕上げが甘かったことは上述した通りです。 加えて本来であればユーザーストーリーとワーヤーフレームの間にどういうデザインにすれば ユーザーは使いやすくなるかと言ったUI/UXについて考えるステップを飛ばしてしまいました。 対応策として、今回は次の既存の2つのサービスを参考にしてながら作成しました。 何らかのサービスを作成する際には類似サービスを必ず研究すべきです。 ユーザーは既存のサービスのUXを学習しているので、 それに合わせる形で作成しないとユーザーとのコミュニケーションが上手くいきません。 その点もある程度考慮して開発したのですが、 ユーザーが本当に使いやすいデザインかと言われると肯定はできません。 まだ改良の余地はあると思います。 (例えばレース詳細画面はタブ形式で別のレース切り替えた方が使いやすいと思っています) 7.2 技術面を完全な独学で進めた メンターの方には要件定義やアーキテクチャなど様々な点でお世話になりました。 一方で技術面は完全に独学で進めました。 幸いなことに検索すれば知りたい情報は出てくるので自力で完成はできました。 ただ、独学で十分条件は満たせないと思います。 例えば技術選定は何がベストプラクティスかわからないまま進んでしまいました。 コードの書き方もとりあえず動けばOKという考え方で可読性などの面を考慮していません。 就職を見据えると技術面に関して相談できる場を用意した方がよかったのかなあと思っています。 7.3 モチベーションを保つ工夫をするべきだった 開発期間は約2ヶ月。朝から晩までコミットできる! …頭の中では理解できるんです。頭の中では。 特に最後の方は身体と心がついてきませんでした。 いくら自分が熱量を持って取り組めることだとしても2ヶ月間一つのことに集中することは思った以上に大変でした。 今回の開発は一人でやる場合『まあこれていいだろう』と妥協してしまいがちになり、 それで後々後悔することがありました。 スケジュール・タスク管理をこまめに行い、日報を記入して進捗に遅れがないか振り返ることは継続していましたが、 それでも不十分でした。 自分を引き締めたり、逆に気を緩めたり、あるいは別のことに没頭するといったモチベーションの管理に意識的に取り組むべきでした。 8. 最後に 長い記事を読んでいただき本当にありがとうございました! 記事を書こうと思ったきっかけはG's ACADEMYで教わったナレッジの共有の重要性にあります。 学校の説明会や事業対策講座でもこの点を何度も学びました。 私の経験が誰かにとって有益なものとなり、私の想像を超えた新しいプロダクトやサービスの開発に結びつけば嬉しい。 その一心で書きました。技術的な話は極力控え、多くの人が直面するであろう悩みを中心的に書いたつもりです。 ただ、私自身が色々な意味で外れ値だと自負しているので、参考にならなかったらごめんなさい笑 参考になったという方がおりましたら、是非LGTM、ストック、Twitterでのシェアお願いします! してくれたら泣いて喜びます笑! また、ご質問などありましたら、TwitterでDMいただけますと幸いです! 特にG's生であれば大歓迎です!! Twitter: https://twitter.com/philosophy_note 追記 この記事を書き上げた後、次の記事を読みました。 例えば、「ポートフォリオ」を作るにしても、あなたが感じている課題を解決するために作るというのはどうでしょう。そうすれば、作って終わり> にはならないはず。実際に使ってみて直したい部分が出てきたり、新しい機能がほしくなったり、継続して開発したくなるのではないでし ょうか。一通り作ってそのままになっている「ポートフォリオ」を見せられても、何も伝わりません。 (太字は私が追加) メモリの関係でHerokuに月5,000円弱費やしているので見直しが必要 加えてHerokuのDBは課金しないと10,000レコードしか入らないので毎週末わざわざ消している 結論:Heroku→AWS移行は必要 従ってHeroku Scheduler→AWS Lambdaに変える必要あり データはローカルではなくS3で保管したい。AWS Glueも使いたい。 ダッシュボードがメモリを圧迫しているのでAWS移行したら削除→代わりにQuickSightでダッシュボード作る モデルもデータ分析から考え直したい DjangoでAPI開発もしてみたい 今後も継続して開発することにします。(Heroku→AWSまでは達成済) おまけ 使用したツール・学習教材などについて記載します。 ツール Notion スケジュール管理・タスク管理・イメージ共有・参考記事のデータベース・日報の記入、全てこなす最強ツールです。 特にNotion Web Clipperはクリック一つで参考記事のデータベースが簡単にできるので、大変重宝しました。 Miro Miroを見ろ ユーザーストーリーやアーキテクチャの作成の際にメンターさんと共有するために使用していました。 オンラインホワイトボードです。 Figma ワイヤーフレーム作成時に使用。 色々できますが、慣れるまで時間がかかります。 LaravelDB.com 名前にもあるように本来はER図を作成してLaravelのMigrationファイルまで作成してくれるサービスです。 ただ、簡単にデータ型やプライマリーキーの設定、リレーションシップの構築ができるため、ER図だけの作成用として使っていました。 Pixabay 使用したフリー画像は全てここからダウンロードしました。 Material Design for Bootstrap マテリアルデザインが簡単に作成できるBootstrapです。 見やすくカッコいいデザインでフロントエンドを作成することができます。 Shopify Hatchful 制約はありますがイメージに従うだけで ロゴやファビコンを自動作成してくれます。 canva 自分である程度自由に作成したい場合はこちら。 自分で作るだけではなく、色々なテンプレートを見ることができるのでデザインの参考になります。 ColorSpace メインカラーとしたい色の色コードを入力するだけで 相性のいい色のセットを10種類ほど表示してくれます。 ColorZilla(GoogleChrome拡張機能) 『このサイトの色何使っているんだろう?』というときに使える拡張機能。 知りたい部分をクリックするだけで色コードがわかります。 DeepL翻訳 翻訳ができます。頼りすぎ厳禁。 学習教材 Udemy オンライン動画学習教材が大量に提供されています。 Djangoについては次の教材が大いに参考になりました。 Pythonでのデータ分析は次の動画から始めました。 ちょっと古くなってしまったので、今始めるなら次の動画が良いと思います。 (ただし機械学習がない) Pythonではじめる機械学習 スクール入学前にPythonでの機械学習実装を勉強するために購入した本です。 これ一冊で基本的なモデル作成などの知識を学ぶことができます。 Kaggleで勝つデータ分析の技術 書名の通りKaggle対策本ですが、特徴量の作り方やモデルの評価方法が 実装コードとともに記載されていて非常に勉強になりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】mplfinance で複数グラフを描くとワーニング

はじめに 株価データの分析のために,複数銘柄のチャートを描きたいと思った 例えば,TOPIX Core30 だと30銘柄ある 1銘柄ずつループで回していると,チャートは表示されるものの,ワーニングが表示される /usr/local/lib/python3.9/site-packages/mplfinance/_mplwraps.py:60: RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (`matplotlib.pyplot.figure`) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam `figure.max_open_warning`). f = plt.figure(FigureClass=Mpf_Figure,*args,**kwargs) ※本記事中,見やすさのため,出力結果の一部に改行をいれているところがあります 20個より多くのグラフを描くとワーニングになるようで, このワーニングがでなくなる方法を備忘録として残しておく ワーニングが出たコード こちらのデータ(SPY_20110701_20120630_Bollinger.csv)を使って同じチャートを30回描くコードだ warn_code.py import pandas as pd import mplfinance as mpf df = pd.read_csv('SPY_20110701_20120630_Bollinger.csv',index_col=0,parse_dates=True) # 30回チャートを描く for index in range(30): # チャート描画 fig = mpf.figure(figsize=(3, 3),style='yahoo') ax1 = fig.add_subplot(1,1,1) mpf.plot(df, ax=ax1, style='yahoo', type='candle', xrotation=30) グラフは30個表示されるが, 上述のワーニングが表示される 問題解決のため調査をはじめる このワーニングの対処法を調査すると,matplotlib の解決策ばかりだった plt.clf() plo.close() matplotlib では clf と close を呼べばいいらしい しかし,mplfinance の場合は,closeやclfがない 試しに読んでみると >>> mpl.close() AttributeError: module 'mplfinance' has no attribute 'close' >>> mpl.clf() AttributeError: module 'mplfinance' has no attribute 'clf' モジュール内の一覧を表示してみると >>> import mplfinance as mpl >>> dir(mpl) ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '__version__', '_arg_validators', '_helpers', '_mpf_warnings', '_mplwraps', '_panels', '_styledata', '_styles', '_utils', '_version', '_widths', 'available_styles', 'figure', 'make_addplot', 'make_marketcolors', 'make_mpf_style', 'mplfinance', 'plot', 'plotting', 'show', 'write_style_file'] 確かになさそうだ fig を作った plt を得るしかなさそうである mplfinance のソースを見てみる mplfinance.figure が定義されている,_mplwraps.py を見ると plt がいた _mplwraps.py def figure(*args,**kwargs): style = _check_for_and_apply_style(kwargs) f = plt.figure(FigureClass=Mpf_Figure,*args,**kwargs) f.mpfstyle = style return f class Mpf_Figure(mplfigure.Figure): … matplotlib.pyplot.figure()関数を呼び出している 引数FigureClass=Mpf_FigureのMpf_Figureクラスは,_mplwraps.py 内で定義されているクラスだ The wrapper function is the same as `matplotlib.pyplot.figure()` except that it additionally accepts kwarg `style=` to set the mplfinance style. _mplwraps.py のヘッダに書かれているが,mplfinance.figure()関数は基本 matplotlib.pyplot.figure()関数と同じだ ただし,mplfinance の style を設定できるところだけが違う style = _check_for_and_apply_style(kwargs) _check_for_and_apply_style()関数で matplotlib.pyplot.figure()関数 に渡す kwargs から style を削除しているようだ f.mpfstyle = style そして,matplotlib.pyplot.figure()関数でできた mplfinance.figure に後からスタイルを設定している 解決案 matplotlib.pyplot と,mplfinance._mplwraps.Mpf_Figure をインポート import matplotlib.pyplot as plt import mplfinance as mpf from mplfinance._mplwraps import Mpf_Figure figure の生成を,mplfinance.figure()関数から,matplotlib.pyplot.figure()関数に変更する style は後から設定してあげる # もともとの figure 生成 fig = mpf.figure(figsize=(3, 3),style='yahoo') # 新しい figure の生成方法 fig = plt.figure(FigureClass=Mpf_Figure,figsize=(3, 3)) fig.mpfstyle = 'yahoo' plot が終わったら,clf と close を呼んであげる plt.show() plt.clf() plt.close() まとめると以下のようだ ok_code.py import pandas as pd import matplotlib.pyplot as plt import mplfinance as mpf from mplfinance._mplwraps import Mpf_Figure df = pd.read_csv('SPY_20110701_20120630_Bollinger.csv',index_col=0,parse_dates=True) # 30回チャートを描く for index in range(30): # チャート描画 fig = plt.figure(FigureClass=Mpf_Figure,figsize=(3, 3)) fig.mpfstyle = 'yahoo' ax1 = fig.add_subplot(1,1,1) mpf.plot(df, ax=ax1, style='yahoo', type='candle', xrotation=30) plt.show() plt.clf() plt.close() めでたくワーニングが消えた おわりに matplotlib でのワーニングを消す方法を参考に mplfinance で行ってみた うまいやり方,良い方法かどうかわからない… これは違う,こうしたほうが良いなどあれば,コメントいただければと思う 追記 単にこれでよかったのかもしれない インポートについて理解していなかったが, Pythonのモジュールインポートのしくみを読むと, モジュールオブジェクトはシングルトンなため, 自分で import した plt(matplotlib.pyplot)を使って clr,close を呼ぶだけ # matplotlib.pyplot をインポート import matplotlib.pyplot as plt # plot,show の後に,pltのclf,closeを呼ぶ plt.clf() plt.close() まとめると ok_code2.py import pandas as pd import matplotlib.pyplot as plt import mplfinance as mpf df = pd.read_csv('SPY_20110701_20120630_Bollinger.csv',index_col=0,parse_dates=True) # 30回チャートを描く for index in range(30): # チャート描画 fig = mpf.figure(figsize=(3, 3),style='yahoo') ax1 = fig.add_subplot(1,1,1) mpf.plot(df, ax=ax1, style='yahoo', type='candle', xrotation=30) plt.show() plt.clf() plt.close()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

BERT+Grad-CAM

概要  Grad-CAMはCNNベースのモデルに対しての視覚的な説明を作成する手法です.本記事ではGrad-CAMを言語処理モデルBERTに実装することでどのような結果になるかを確認していきます. 畳み込みベース以外の手法に適応できるのか?  畳み込みが使用されるモデル(特にFully Convolutional Networks)では,ピクセルとその周辺ピクセルの情報を抽出するため位置情報とモデルの出力がダイレクトに対応するように思えますが,BERTなどのSelf-Attentionベースのモデルでは周辺だけではなく全体から重みを付けて情報を抽出するため位置情報とモデルの出力が対応しません.  上記の理由からBERTにGrad-CAMを適応させるのは適切ではありません.本記事ではそれを承知しながら実装していますが,上記を覆す根拠などが無い限り研究などには応用しづらいと考えています. 準備  BERTはHuggingFaceから公開されている文のネガポジ判定モデルdaigo/bert-base-japanese-sentimentを使用します.  python = "3.6.8"  pytorch = "1.6.0"  pip install transformers  pip install japanize_matplotlib コード  ・インポート import types import torch from transformers import AutoTokenizer, AutoModelForSequenceClassification import numpy as np import matplotlib.pyplot as plt import japanize_matplotlib  ・トークナイザとモデルを用意します. tokenizer = AutoTokenizer.from_pretrained("daigo/bert-base-japanese-sentiment") model = AutoModelForSequenceClassification.from_pretrained("daigo/bert-base-japanese-sentiment")  ・順伝播&逆伝播時の内部状態と勾配を取得するための関数を用意しておく. def save_feature_map(self, module, input, output): self.feature_maps.append(output[0].detach()) def save_grad(self, module, grad_in, grad_out): self.grads.append(grad_out[0].detach()) def init_cam(self): self.feature_maps = [] self.grads = [] model.save_feature_map = types.MethodType(save_feature_map,model) model.save_grad = types.MethodType(save_grad,model) model.init_cam = types.MethodType(init_cam,model) model.feature_maps = [] model.grads = []  ・CNNベースの画像認識モデルでは抽出された特徴的を最下層から取得するが,Self-Attentionベースの言語モデルでは一概に下層ほど特徴がより正確にエンコードされているとは限らないため,全ての層のアウトプット時の状態と勾配を取得する. for i in range(12): model.bert.encoder.layer[i].register_forward_hook(model.save_feature_map) model.bert.encoder.layer[i].register_backward_hook(model.save_grad)  ・下記の様にサンプル文を解析すると出力ラベルは1(ネガティブ)と出力されました. tokens = tokenizer("私は不運な人間です.",return_tensors='pt') output = model(tokens["input_ids"],tokens["token_type_ids"],tokens["attention_mask"],labels=torch.tensor([0])) logits = output.logits.detach() pred = logits.argmax(-1) ##1 one_hot = torch.nn.functional.one_hot(pred) ##[0,1]  ・下記のような逆伝播を行う. logits = output.logits logits = torch.sum(logits*one_hot) logits.backward()  ・可視化に関する数値を取得する.camsに各層の数値が追加される. cams = [] for i in range(12): weights = np.mean(list(reversed(model.grads))[i].cpu().numpy(), axis=1)[0, :] features = model.feature_maps[i].squeeze().transpose(0,1).numpy() cam = np.sum(features * weights[:, None], axis=0) cam = np.maximum(cam, 0) cams.append(cam)  ・入力文をデコードする. decode_tokens = [tokenizer.decode(token,skip_special_tokens=True).replace(" ","") for token in tokens["input_ids"].squeeze().tolist()]  ・可視化を行う. (数値が0の場合は水色,大きくなるほど赤に) cams_matrix = np.concatenate([np.expand_dims(cam,axis=0) for cam in cams],axis=0) fig, ax = plt.subplots(figsize=(12, 12)) ax.set_xticks(np.arange(len(decode_tokens))) ax.set_xticklabels(decode_tokens) ax.set_title('CAM_Sample') plt.imshow(cams_matrix, cmap='cool', interpolation='nearest') plt.savefig("CAM_Sample.png") plt.show() ・Grad-CAM with PyTorchを参考にさせていただきました. 結果  ・上記のコードではネガティブ判定されるサンプル文を使っていますが,ポジティブ判定される文でも実行してみました.  ・縦軸は層を示しています.(例えば目盛りの0は1層目での数値を示しています)  ・「不運」や「楽し」などの特徴的な部分が色付けされているのが確認できますが,画像認識モデルほどは的確にできません. まとめ  ・可視化できる根拠無しにBERTに対してGrad-CAMを適応し,可視化を行いました.  ・CNNベースの画像認識モデルほど的確に可視化を行うことはできませんでしたが,それとなく可能性を感じるような結果でした. 最後に  ・誤っている部分等ございましたら,コメント等で優しく指摘して頂けると嬉しいです.(気付かなかったら申し訳ありません)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

BERT+Grad-CAM (PyTorch)

概要  Grad-CAMはCNNベースのモデルに対しての視覚的な説明を作成する手法です.本記事ではGrad-CAMを言語処理モデルBERTに実装することでどのような結果になるかを確認していきます. 畳み込みベース以外のモデルに適応できるのか?  畳み込みが使用されるモデル(特にFully Convolutional Networks)では,ピクセルとその周辺ピクセルの情報を抽出するため位置情報とモデルの出力がダイレクトに対応するように思えますが,BERTなどのSelf-Attentionベースのモデルでは周辺だけではなく全体から重みを付けて情報を抽出するため位置情報とモデルの出力が対応しません.  上記の理由からBERTにGrad-CAMを適応させるのは適切ではありません.本記事ではそれを承知しながら実装していますが,上記を覆す根拠などが無い限り研究などには応用しづらいと考えています. 準備  BERTはHuggingFaceから公開されている文のネガポジ判定モデルdaigo/bert-base-japanese-sentimentを使用します.  python = "3.6.8"  pytorch = "1.6.0"  pip install transformers  pip install japanize_matplotlib コード  ・インポート import types import torch from transformers import AutoTokenizer, AutoModelForSequenceClassification import numpy as np import matplotlib.pyplot as plt import japanize_matplotlib  ・トークナイザとモデルを用意します. tokenizer = AutoTokenizer.from_pretrained("daigo/bert-base-japanese-sentiment") model = AutoModelForSequenceClassification.from_pretrained("daigo/bert-base-japanese-sentiment")  ・順伝播&逆伝播時の内部状態と勾配を取得するための関数を用意しておく. def save_feature_map(self, module, input, output): self.feature_maps.append(output[0].detach()) def save_grad(self, module, grad_in, grad_out): self.grads.append(grad_out[0].detach()) def init_cam(self): self.feature_maps = [] self.grads = [] model.save_feature_map = types.MethodType(save_feature_map,model) model.save_grad = types.MethodType(save_grad,model) model.init_cam = types.MethodType(init_cam,model) model.feature_maps = [] model.grads = []  ・CNNベースの画像認識モデルでは抽出された特徴的を最下層から取得するが,Self-Attentionベースの言語モデルでは一概に下層ほど特徴がより正確にエンコードされているとは限らないため,全ての層のアウトプット時の状態と勾配を取得する. for i in range(12): model.bert.encoder.layer[i].register_forward_hook(model.save_feature_map) model.bert.encoder.layer[i].register_backward_hook(model.save_grad)  ・下記の様にサンプル文を解析すると出力ラベルは1(ネガティブ)と出力されました. tokens = tokenizer("私は不運な人間です.",return_tensors='pt') output = model(tokens["input_ids"],tokens["token_type_ids"],tokens["attention_mask"],labels=torch.tensor([0])) logits = output.logits.detach() pred = logits.argmax(-1) ##1 one_hot = torch.nn.functional.one_hot(pred) ##[0,1]  ・下記のような逆伝播を行う. logits = output.logits loss = torch.sum(logits*one_hot) loss.backward()  ・可視化に関する数値を取得する.camsに各層の数値が追加される. cams = [] for i in range(12): weights = np.mean(list(reversed(model.grads))[i].cpu().numpy(), axis=1)[0, :] features = model.feature_maps[i].squeeze().transpose(0,1).numpy() cam = np.sum(features * weights[:, None], axis=0) cam = np.maximum(cam, 0) cams.append(cam)  ・入力文をデコードする. decode_tokens = [tokenizer.decode(token,skip_special_tokens=True).replace(" ","") for token in tokens["input_ids"].squeeze().tolist()]  ・可視化を行う. (数値が0の場合は水色,大きくなるほど赤に) cams_matrix = np.concatenate([np.expand_dims(cam,axis=0) for cam in cams],axis=0) fig, ax = plt.subplots(figsize=(12, 12)) ax.set_xticks(np.arange(len(decode_tokens))) ax.set_xticklabels(decode_tokens) ax.set_title('CAM_Sample') plt.imshow(cams_matrix, cmap='cool', interpolation='nearest') plt.savefig("CAM_Sample.png") plt.show() ・Grad-CAM with PyTorchを参考にさせていただきました. 結果  ・上記のコードではネガティブ判定されるサンプル文を使っていますが,ポジティブ判定される文でも実行してみました.  ・縦軸は層を示しています.(例えば目盛りの0は1層目での数値を示しています)  ・「不運」や「楽し」などの特徴的な部分が色付けされているのが確認できますが,画像認識モデルほどは的確にできません. まとめ  ・可視化できる根拠無しにBERTに対してGrad-CAMを適応し,可視化を行いました.  ・CNNベースの画像認識モデルほど的確に可視化を行うことはできませんでしたが,それとなく可能性を感じるような結果でした. 最後に  ・誤っている部分等ございましたら,コメント等で優しく指摘して頂けると嬉しいです.(気付かなかったら申し訳ありません)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む