- 投稿日:2020-10-23T23:36:51+09:00
Chalice で Excel 向けの CP932 CSV ファイルを作成して返す
はじめに
Webアプリケーションの管理系の仕組みを作っていると「システムのデータをExcelで開けるCSVファイルにまとめてダウンロードできるようにして欲しい」というパターンに経験上結構遭遇する。
Python3系で何も考えずにCSVを作ると UTF-8 の文字エンコーディングになるので、Excel のデフォルトの設定だと文字化けしてしまう。 もちろん、Excelでちゃんと読み込みエンコーディングを指定すれば問題なく開けるのだが、「そんなの分からんから最初から文字化けないようにして」と言われることも多い。その場合、Windows用のエンコーディング (CP932, あるいは Shift_JIS, SJIS) でCSVファイルを作成する必要がある。今回は Chalice を使ってやっていたのだが、ドキュメントをよく見た結果ドはまりしたのでメモ。
version など
$ pipenv run chalice --version chalice 1.21.2, python 3.8.2, linux 5.4.0-52-genericドキュメントを読む
今回は Response で返したいので、Responseクラスの body に適切に値を入れればどうにかなると思っていた。 しかし、記事執筆(2020/10/23)時点のドキュメントにはこうある。
class Response(body, headers=None, status_code=200)
body: The HTTP response body to send back. This value must be a string.https://aws.github.io/chalice/api.html#response から引用・抜粋。 一部強調。
これを読んだときの思考。
「え…? CP932 にエンコードした文字列は bytes 型になるからここに渡せない…? 無理とするとファイルを作ってS3にアップロードして、これをダウンロードさせるか…」
が、body には bytes を入れても通る 。このドキュメントの
string
はstr型
だけではなく、より広い文字列を意図しているのだそうだ。 改めて調べてみると、同じ疑問を持った人が質問してくれていた。実装例: 直接レスポンス
Chalice の
Response#body
には bytes型も渡すことができる。 それを踏まえて実装するとこんな感じになる。 ブラウザで/
にアクセスすると、CSVファイルとしてダウンロードされる。app.py#!/usr/bin/python # -*- coding: utf-8 -*- import csv import tempfile from chalice import Chalice, Response app = Chalice(app_name='csvtest') def csv_response(filename, encoding='utf8'): """ CSVファイルを返す方法1: 直接レスポンス """ with tempfile.TemporaryFile(mode='r+', encoding=encoding) as fh: # writer で書き込み writer = csv.writer(fh, lineterminator='\r\n') writer.writerow(['ユーザー名', 'ログイン日時']) writer.writerow(['user01', '2000/01/01 00:00:00']) # 書き込んだ全てのデータを data に読み込み fh.seek(0) data = fh.read() headers = {} headers['Content-Type'] = 'text/csv' headers['Content-Disposition'] = f'attachment;filename="{filename}"' return Response(body=data, status_code=200, headers=headers) @app.route('/') def index(): return csv_response('test.csv', encoding='cp932')別解: S3へのファイルアップロード
先の例では
tmpfile
を使ってオンメモリにファイルを作成、さらにdata
を読み込んでいる。 しかし、AWS Lambdaの場合はメモリ利用量が制限された環境での実行となるので、ファイルが大きくなってくる場合はメモリ利用量にかなりの影響を与えることが想像できる。 もちろんメモリ利用量の上限を上げることで回避できる問題だが、料金が倍となると躊躇することもあるだろう。
そういった場合は AWS Lambda で提供されている/tmp
以下に一時的にファイルを作り、作成したファイルを S3 にアップロードするといった方法を用いればよい。
S3にアップロードした後はCloudFront - S3
の通信経由で作成したファイルにアクセスさせたり、一時URLを作成・提供してユーザーにダウンロードをさせることができる。なお、
TempfileContext
は AWS Lambda で用いた/tmp
以下のファイルをエラーハンドリングなど考えずに消せるように準備したものであるので、コードの本質ではない。import os import csv import uuid import boto3 s3 = boto3.client('s3') class TempfileContext: """ 一時ファイルを作って消去するコンテキストを提供します """ def __init__(self): tmpfile = str(uuid.uuid4()) self.filename = f'/tmp/{tmpfile}' def __enter__(self): return self def __exit__(self, ex_type, ex_value, trace): try: if os.path.exists(self.filename): os.remove(self.filename) except Exception: pass def create_and_upload_csv(filename, encoding='utf8'): """ CSVファイルを返す方法2: CSVを作って S3にアップしておく """ with TempfileContext() as tmp: # 1. /tmp 領域に CSVファイルを作成 with open(tmp.filename, 'w', encoding=encoding) as fh: # writer で書き込み writer = csv.writer(fh, lineterminator='\n') writer.writerow(['ユーザー名', 'ログイン日時']) writer.writerow(['user01', '2000/01/01 00:00:00']) # 2. S3 にアップロード s3.upload_file(tmp.filename, BUCKETNAME, f'uploads/{filename}')
- 投稿日:2020-10-23T22:30:44+09:00
Lambda Layer と CloudFormation / sam-cli で楽ちん X-Ray
はじめに
こちらの Lambda Layer と X-Ray - Qiita という記事で、X-Ray を Lambda Layer で組み込む方法が紹介されています。複数の lambda をデプロイする場合に、個別に X-Ray を組み込まなくても一発で付け外しができるので、けっこういい感じです。
けっこういい感じなんですが、すこしコマンドを叩くのが手間なので、CloudFormation 化できないかな、と思っていたところ AWS SAM CLI で Lambda Layers が ビルドできるようになったよ - Qiita という記事を見つけました。
というわけで、2つの記事の内容をガッチャンコしたら、こんなかんじで
X-Ray の LambdaLayer を CloudFormation で作れて楽チン!
という記事です。
なお、 X-Ray は、AWS Lambda で使えるトレーシングツールです。X-Ray を使うことで、「Lambda のどの部分で時間がかかっているか?」「どこのリソース (DynamoDB, S3, etc) 呼び出しで時間をくっているか?」を下の図のような感じで確認できるようになります。
(図の参照元: AWS X-Ray コンソール: - AWS X-Ray)
X-Ray な Lambda Layer のテンプレート定義
用意するのは、template.yaml の他には xray-layer-src/requirements.txt だけ で OK です。
template.yaml
template.yamlAWSTemplateFormatVersion: "2010-09-09" Transform: AWS::Serverless-2016-10-31 Description: CloufFormation Template X-Ray lambda layer sample Resources: # X-Ray の Lambda Layer 定義 XRayLayer: Type: AWS::Serverless::LayerVersion Properties: Description: Lambda Layer for XRay ContentUri: xray-layer-src CompatibleRuntimes: - python3.8 # ここは利用している Python のランタイムを指定 Metadata: BuildMethod: python3.8 # sam-cli でビルド時に指定が必要 # XRayLayer を利用するラムダ定義の例 SomeFunction: Type: AWS::Serverless::Function Properties: CodeUri: lambda/src/ Handler: lambda_handler.lambda_handler Runtime: python3.8 Tracing: Active Layers: - !Ref XRayLayer # 作成した Layer の参照xray-layer-src/requirements.txt
xray-layer-src/requirements.txtaws-xray-sdkLambda Layer に入れるライブラリの指定で、ここでは aws-xray-sdk のみを指定すれば OK です。
これで、X-Ray Lambda Layer のリソースが作成できます。デプロイは通常の sam を使うときと同じです。
sam build sam deployX-Ray を利用する Lambda の例
X-Ray を利用するソースコードは、例えば次のようになります。
lambda-src/lamda_handler.pyimport ... # XRay SDK をインポート。公式ドキュメントにあるように最後にインポートする必要あり from aws_xray_sdk.core import patch_all # boto3 などの関連ライブラリにまとめて X-Ray のパッチをあてる patch_all() def lambda_handler(event, context): ...参考リンク
- 投稿日:2020-10-23T22:07:42+09:00
tuple配列のsortは、key指定で高速化できる(Python)
はじめに
競プロ精進中に「計算量的には間に合いそうなのにTLEする...」と悩んでいたところ、前準備のsort部分がボトルネックになっていたと発覚。
ハマったポイントを共有します。tupleの配列のsort
key指定あり/なしの違いについて、簡潔に説明します。
key指定あり
a = [(3,3), (2,2), (3,1), (4,2)] a.sort(key=lambda x: x[0]) print(a) # => [(2, 2), (3, 3), (3, 1), (4, 2)]指定したkeyの大小によって、sortを行います。keyの要素が等しければ、その順序は保持されます。
sort() メソッドは安定していることが保証されています。ソートは、等しい要素の相対順序が変更されないことが保証されていれば、安定しています。(公式ドキュメント)
sort(key=lambda x: (x[0],x[1]))
のようにkeyをtupleにして複数指定することもできますが、
本記事では単一の要素をkey指定することを考えます。key指定なし
何もkeyを指定しないと、
「tupleの0番目の要素で大小比較」→「同じなら1番目の要素で大小比較」→「...」
という方法でsortが行われます。a = [(3,3), (2,2), (3,1), (4,2)] a.sort() print(a) # => [(2, 2), (3, 1), (3, 3), (4, 2)]このとき内部では、tuple同士の比較演算が行われているようです。
key は一引数をとる関数を指定し、リストのそれぞれの要素から比較キーを取り出すのに使います (例えば、 key=str.lower)。それぞれの項目に対応するキーは一度計算され、ソート処理全体に使われます。デフォルトの値 None は、別のキー値を計算せず、リストの値が直接ソートされることを意味します。(公式ドキュメント)
「tupleの先頭の要素でsortする」だけなら、key指定ありでもなしでも目的は果たせます。
ここで「じゃあkey指定なしでいいや〜」と思ってkey指定しないと、思わぬ罠にハマる可能性があります。
tuple同士の比較は遅いのです。実験
「tuple同士の比較」 vs. 「tupleの要素同士の比較」
この2つで、どれほどの速度差があるのかを確認します。
実行環境はAtCoderのコードテスト(PyPy3)です。
各tuple内の要素数は3です。import random random.seed(1) ''' n: 3通り N = 1*(10**3) N = 3*(10**3) N = 5*(10**3) ''' tl = [] # list of tuples for i in range(N): l = random.randint(1,N) r = random.randint(1,N) tl.append((l,r,i)) # 3 elements in tuple ''' # 1. tuple同士の比較 for i in range(N): for j in range(N): res = tl[i] < tl[j] # 2. tupleの要素同士の比較 for i in range(N): for j in range(N): res = tl[i][0] < tl[j][0] '''計測結果は以下です。
N 1. tuple同士 2. tupleの要素同士 $1*10^3$ 78 ms 17 ms $3*10^3$ 672 ms 129 ms $5*10^3$ 1875 ms 368 ms タプルの要素が3つなので「高々3倍程度の誤差か?」と思いきや、かなり大きな差が出ました。
怖い。tuple配列のsort 「key指定なし」 vs. 「key指定あり」
import random random.seed(1) ''' n: 3通り N = 1*(10**5) N = 3*(10**5) N = 5*(10**5) ''' tl = [] # list of tuples for i in range(N): l = random.randint(1,N) r = random.randint(1,N) tl.append((l,r,i)) # 3 elements in tuple ''' 3. tl.sort() 4. tl.sort(key=lambda x:x[0]) '''測定結果は以下です。
N 3. keyなし 4. keyあり $1*10^5$ 198 ms 98 ms $3*10^5$ 735 ms 359 ms $5*10^5$ 1361 ms 708 ms 最初の実験ほどではありませんが、およそ2倍程度の差が出ました。
おわりに
さほど気にする差ではないかもしれませんが、問題によってはこれでハマる可能性があります。
実験のコードを見て「あの問題かな?」と思った人もいるはず。
ちなみに、key指定はlambda
ではなくitemgetter
のほうが高速らしいですね。
- 投稿日:2020-10-23T21:38:20+09:00
いろいろなサービスを使ってLINEbotを作ろう【Heroku編】
概要
LINEbotのバックエンドををさまざまなサービスを利用して作ってみようというシリーズです。
第2弾はHerokuを利用します。そのほかのサービスを利用した構築については、以下を参照ください。
- 第1弾:ngrok
- 第2弾:Heroku ※本記事
- 第3弾:Google CloudFunctions ※近日更新
- 第4弾:Google Cloud Run
- 第5弾:Google Kubernetes Engine
- 第6弾:AWS Lambda (予定)
前提
- herokuおよびLINE Developersへのユーザー登録およびチャンネル作成が済んでいる
- herokuCLIをインストールしている
全体像
ハンドラ実装
コード全容はこちら
main.pyimport logging import os from flask import Flask, abort, request from linebot import LineBotApi, WebhookHandler from linebot.exceptions import InvalidSignatureError from linebot.models import MessageEvent, TextMessage, TextSendMessage app = Flask(__name__) app.logger.setLevel(logging.INFO) # 変更点1 handler = WebhookHandler(os.getenv('CHANNEL_SECRET')) line_bot_api = LineBotApi(os.getenv('CHANNEL_ACCESS_TOKEN')) @app.route('/callback', methods=['POST']) def callback(): signature = request.headers['X-Line-Signature'] body = request.get_data(as_text=True) app.logger.debug("Request body: " + body) try: handler.handle(body, signature) except InvalidSignatureError: # 変更点3 app.logger.warn('Invalid signature. Please check your channel access token/channel secret.') abort(400) return 'OK' @handler.add(MessageEvent, message=TextMessage) def handle_message(event): # 変更点2 if (event.reply_token == '00000000000000000000000000000000' or event.reply_token == 'ffffffffffffffffffffffffffffffff'): app.logger.info('Verify Event Received') return # オウム返し line_bot_api.reply_message( event.reply_token, TextSendMessage(text=event.message.text)) if __name__ == "__main__": app.logger.setLevel(logging.DEBUG) app.run()ソースコードはPython SDKのサンプルをベースに以下の修正を加えています。
1. シークレットをコードに埋め込まない
チャンネルシークレット
とアクセストークン
は秘匿情報の部類となります。仮に流出してしまった場合は、他人に自分のアカウントをのっとられてしまいます。
サンプルではチャンネルシークレット
とアクセストークン
を直接コードに記述するような書き方になっていますが、この状態でだれもが見られるリモートリポジトリにコードをpushすることはできませんね。(プライベートリポジトリであれば多少話は別ですが)
そこで、実行時のサーバ・コンテナの特定の環境変数に値を設定し、コード側はその環境変数から値を読み込むように記載します。(万全ではないですが、対応前に比べれば段違いにセキュアなつくりとなっています)- handler = WebhookHandler('YOUR_CHANNEL_SECRET') - line_bot_api = LineBotApi('YOUR_CHANNEL_ACCESS_TOKEN') + handler = WebhookHandler(os.getenv('CHANNEL_SECRET')) + line_bot_api = LineBotApi(os.getenv('CHANNEL_ACCESS_TOKEN'))2. LINE Developersからの
Verify
を成功させる本対応は必須ではありません。
LINE DevelopersのWebhook URL
を設定する画面にVerify
ボタンが存在し、ここから設定したバックエンドへの疎通を取るような形になっています。
しかし、サンプルをそのまま利用すると、このVerify
のレスポンスコードが200になることはありません。
これはVerify
リクエストのreply_token
が00000000000000000000000000000000
またはffffffffffffffffffffffffffffffff
となっており、どうもWebAPI側でこの2つのトークンは不正であると判別していることによるようです。
そこで、この2種類のトークンは特別扱いで200のステータスコードを返すようにhandlerへ分岐を追加します。+ if (event.reply_token == '00000000000000000000000000000000' or + event.reply_token == 'ffffffffffffffffffffffffffffffff'): + return
3. print → logger
本対応は必須ではありませんが、推奨設定です。
ローカルでのprintデバッグというのは有用な方法ですが、通常ソースコードにFlask
のLoggerを利用するのが手っ取り早いと思います。- print("Invalid signature. Please check your channel access token/channel secret.") + app.logger.warn('Invalid signature. Please check your channel access token/channel secret.')Webサーバ設定ファイルの記述
上記実装で使用したFlaskフレームワークには、Webサーバとして起動することのできる機能も備わっていますが、デバッグ向けの機能となります。サービスとして公開する場合には、安定性や速度を鑑みてWSGIサーバを使用するのがよいでしょう。(このあたりの詳細はこちらも参照いただけると?)
今回はgunicornを利用します。
設定ファイル
config.pyimport os host = '0.0.0.0' port = os.getenv('PORT', 5000) bind = str(host) + ':' + str(port) # Debugging reload = True # Logging accesslog = '-' loglevel = 'info' # Proc Name proc_name = 'Line-Bot-Practice' # Worker Processes workers = 1 worker_class = 'sync'ポイントは1点のみです。
ポートはheroku側で自動設定される
herokuの仕様で、デプロイ後にポートが自動設定されるため、ポートに固定値を使用することはできません。
環境変数PORT
にこの値は設定されているため、os.getenv('PORT')
で取得します。ライブラリバージョンの指定
外部ライブラリを使用する際は、利用バージョンを明示・固定するのがよいでしょう。
ローカルにインストールしている場合は、pip freeze > requirements.txt
コマンドで作成することも可能です。requirements.txtFlask==1.1.2 line-bot-sdk==1.17.0 gunicorn==20.0.4herokuデプロイ設定
Deploy With Git / Deploy With Docker の2パターンを選ぶことが可能です。
共通手順
どちらを選択する場合でも共通する手順です。
Terminal# Git初期設定 git init # ログイン / ログイン用ページへ遷移するので必要情報を入力 heroku login # アプリケーション作成 heroku create ${APP_NAME} # 環境変数のセット heroku config:set CHANNEL_SECRET=${YOUR_CHANNEL_SECRET} --app ${APP_NAME} heroku config:set CHANNEL_ACCESS_TOKEN=${YOUR_ACCESS_TOKEN} --app ${APP_NAME}Gitデプロイ
ディレクトリ構成
ルートディレクトリを起点に処理がなされるため、すべてルートに配置します。
./ ├── config.py ├── Procfile ├── requirements.txt ├── runtime.txt └── main.pyまた、2つのファイルを追加します。
ランタイムの指定ファイル
Python固有の設定です。
runtime.txt
を作成することで、実行するPythonバージョンを指定することができます。
サポートされているバージョンはこちらを参照ください。runtime.txtpython-3.8.6起動設定ファイル
ルートディレクトリに
Procfile
が存在する場合は、記載されている内容でプロセスが起動されます。
ファイルの記述内容はこちらに詳細が書かれていますが、プロセス種別はほとんどのケースでweb
でしょうからweb: {コマンド}
と書くことが多いでしょう。Procfileweb: gunicorn main:app -c config.pyデプロイ
herokuリポジトリのmaster(またはmain)ブランチに
push
することでアプリケーションがデプロイされます。Terminalgit add . git commit -m "first commit" git push heroku master動作確認
LINE Developersで
Webhook URL
にhttps://${YOUR_HEROKU_APP}.herokuapp.com/
を設定してVerify
、または実際にLINEからメッセージを送信してオウム返しされてくれば問題なく稼働しています。トラブルシューティング
git push heroku master
でエラーgitのリモートブランチ設定がされていない可能性があります。
heroku git:remote --app ${APP_NAME}
コマンドを実行してみてください。
設定が正しい場合は、以下のように表示されます。$ git remote -v heroku https://git.heroku.com/mintak-heroku-linebot-practice.git (fetch) heroku https://git.heroku.com/mintak-heroku-linebot-practice.git (push)
- 送信したメッセージが返ってこない
まずは
heroku logs --app ${APP_NAME}
でどのようなログが出ているかを確認しましょう。
ディレクトリ構成の誤り、または環境変数が未設定の可能性が高いと思われます。コンテナデプロイ
自身で作成したDockerfileをビルド、コンテナデプロイする方法です。
ディレクトリ構成
herokuは
docker build
を実行する際のコンテキストが、自動的にDockerfileの存在するディレクトリに固定されてしまい、外部から指定することができないため、Dockerfileはルートディレクトリに配置します。./ ├── example/ │ └── python/ │ ├── config.py │ ├── requirements.txt │ └── main.py ├── heroku.yml └── herokuDockerfileこちらも2つのファイルを追加します。
Dockerfile
ライブラリのインストールと、起動コマンドの設定を行います。
herokuアプリに設定した環境変数をよしなにコンテナに設定してくれるようです。
PYTHONDONTWRITEBYTECODE
の意義については、こちらを参照ください。
なお、PYTHONUNBUFFERED
の設定を行っているDockerfileをみかけますが、Python3.7以降デフォルト設定となっているため、3.7以降であれば明示する必要性はありません。herokuDockerfileFROM python:3.8-alpine ENV PYTHONDONTWRITEBYTECODE 1 COPY example/python /usr/local/application RUN pip3 install -r /usr/local/application/requirements.txt && \ rm -f /usr/local/application/requirements.txt WORKDIR /usr/local/application CMD ["gunicorn", "main:app", "-c", "config.py"]デプロイ設定ファイル
Git側での
Procfile
と同様です。
setup
build
release
run
の4ステップを設定することが可能ですが、build
ステップで対象のDockerfileを指定することができていれば起動するでしょう。
ちなみにrun
ステップを記述すると、CMDを上書きすることができます。heroku.ymlbuild: docker: web: herokuDockerfileデプロイ/動作確認
Gitデプロイと同様です。
参考
- 投稿日:2020-10-23T21:36:17+09:00
視覚と聴覚を統合して学習させたぞ! な論文(原典:See, Hear, and Read: Deep Aligned Representations )
原典
See, Hear, and Read: Deep Aligned Representations
arxiv-vanity.com/papers/1706.00932/どんな論文?
画像とキャプションのペアを学習させた。
それによって、未知の画像に対しても、定量的にいい感じの音とテキストの関係も学習できていたことがわかった。
テキスト、サウンド、イメージについてCNNを施す。
下位の層は重みを共有しないが、上位の層でのみ重みを共有するという面白さ。どうやって学習させているのか?
ペアになっている画像と音、あるいは画像とテキストの2つについて、それらが同じグループに属するように学習をさせた(KLダイバージェンス)
どんなもの?
音声とテキストと画像データを食べさせた。学習については、画像+テキスト、画像+音声の2ペアである。しかしながら結果として音声とテキストのペアも学習することができた。(画像をブリッジとすることでペアを学習していると言っても良いよねというスタンス)
先行研究と比べてどこがすごい?
画像と音楽とテキストの3つのマルチモーダルなものにまたがる認識を機会に学習させるという点。非常に人間らしい知覚を習得する点。
これほど大規模でかつ3つの感覚・モーダルへのアプローチは初めてだと考える。
結果として音とテキストのペアは学習させていないが、画像をブリッジとしてしようすることで可能となる。
つまり 英語→あらゆる言語へ翻訳するときに、ブリッジ言語を通すみたいなやつ(鉄壁英単語的なアプローチ)
英語→画像 画像→フランス語
英語が音orテキスト。 フランス語がテキストor音
技術や手法の肝はどこ?
音声と画像とテキストに関して全てのCNNをおこなった。その上層レイヤをすべて結合する新たなネットワークを作成している点。どうやって有効だと検証した?
クロスモーダル検索のスコアを比較した。
クロスモーダル検索とは、例えばイメージしたモーダル(テキストか画像か音)のデータが欲しいときに、別のモーダルからのクエリ検索で、目的のデータが手に入るかを試す。メモ書き
音と言語
音声検索とか。割とよくある。
確率モデル
言語と視覚
画像→テキスト・キャプション自動生成とは違う。この実験では、画像と音声orテキストとの関係性のみを学習させている。
また、テキストに対してRNNを使わずに、CNNを使っている店も新しい。
- 投稿日:2020-10-23T21:34:55+09:00
クリエイティブなアートをAIで作らせてみた! 斬新をプログラミングしたぞ!(論文:Creative Adversarial Network)
元の論文
https://arxiv.org/abs/1706.07068
どんなもの?
GANとは少し違います。それは、識別器が、スタイルも学習するということ。
そして、生成器はスタイルも異なるように生成を学習することで、従来のGANよりもクリエイティブになるのではという論文。
アート論文として、アーティストは、スタイルブレイクを目指すことが多いとわかった。
アートの分布からの逸脱は最小限にしつつ、スタイルの逸脱を最大限にするように生成器を学習
クリエイティブシステムの要件としてある学者は、1想像力、2スキル(品質)、3独自の創造物を評価する能力の3つが必要であると述べているが、この3つ全てを満たしている。
提案されたシステムの主な特徴の1つは、 アートを作成するプロセスでアートの歴史について学習することです。ただし、スタイルの概念の背後にあるアートについての意味的な理解はありません。主題、要素の明示的なモデル、芸術の原理については何も知りません。ここでの学習は、芸術への露出とスタイルの概念にのみ基づいています。その意味で、システムは新しいアートから継続的に学習する機能を備えており、学習した内容に基づいて世代を適応させることができます。背景
昔の DE Berlyne(1924-1976)によって提案された理論に基づいています。
美学の最も重要な覚醒を高める特性は、新規性、驚き、複雑さ、あいまいさ、および不可解さであることを強調しました。
そして、一人のアーティストが作品を作り続けると、どうしても慣れてしまうから、それを避けるためにこのシステムでは頑張る。
また、刺激は強すぎても弱すぎてもでダメであるから、そこをコントロールしている。
カテゴリ(例:[ 18 ])またはキャプション(例:[ 19 ]) に基づいて画像を生成することを容易にするGANの拡張機能があります 。)。このようなラベルにトレーニングを提供することで、さまざまなアートスタイルまたはさまざまなアートジャンルの画像を生成するように設計およびトレーニングできるGANを考えることができます。先行研究と比べてどこがすごい?
フィードバックに人間を必要としない。
googleDreamというのがあったけど、それは曖昧すぎて、抽象アートではないし、コンピュータが生成したとばれちゃう。認識不可能すぎる。適度な曖昧さを頑張る技術や手法の肝はどこ?
識別器は、通常のGANのようにアートかアートではないかの判別も生成器にかえす。それとどのスタイルにどの程度分類できるかの値を生成器に返す。
識別器は、スタイルラベル(ルネサンス、バロック、印象派、表現主義など)に関連する多数のアートにアクセスでき、それを使用してスタイル間の区別を学習します。
生成器は、アートであるものを作り、かつ分類をできるだけ混乱させようと頑張る。次に読むべき論文
覚醒のさまざまなメカニズムの中で、芸術にとって特に重要かつ関連性のあるものは、外部刺激パターンの特性です [ 3 ]。
マーティンデールは、アート制作システムの導出における慣れの重要性を強調しました [ 15 ]。
Wundt曲線(覚醒の度合いを図る曲線?)
Google DeepDream [ 16 ]によって生成された画像について、「ほとんどが、寮の部屋のマンダラ、またはテレンスマッケンナの本の表紙にあると思われるデジタルサイケデリアのように見えます」とコメントしました 3。他の人々は、「まばゆく、ドラッグ、そして気味が悪い」とコメントしました 4。この否定的な反応は、過度の覚醒の結果として説明される可能性があり、その結果、Wundt曲線に従って否定的な快楽がもたらされます。
- 投稿日:2020-10-23T21:33:36+09:00
論文:音楽の脳内処理
元の論文
http://papers.nips.cc/paper/6222-brains-on-beats.pdf
どんなもの?
画像認識などの機械学習の分野では脳を模倣をヒントに画像認識している。そのCNNという技術で行うと、画像の色や何が描かれてるか、などぱっと見わかりやすい情報を学習している層と、ぱっと見ではわかりづらい情報を学習している層(画風など)がある。すなわち分けて認識していることが明らかになっている。
この論文では、音楽を脳がどう処理するかを調べた。
結果として高レベルの処理(スタイルとか)と低レベルの処理(メロディとか)の処理は脳内でも明確に分かれていることが明らかになった。
すなわち、AIは人間を模倣して作られたものであるが、実際に人間の脳も同じように分けて物事を認識していることがわかった。(認識のやり方が複数ある)
- 投稿日:2020-10-23T21:32:32+09:00
論文:脳内の映像を再現する機械学習論文, (Deep image reconstruction from human brain activity)
元論文
https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1006633
どういう論文なの?自分の言葉でまとめると?
→脳内でみている映像を可視化するぞ!という論文。結果を見たら、意外とぼんやりしてるんだなぁと思いました。(もちろん精度の悪さもあると思われる)
詳しく
まず画像を見ている状態で、fMRIの反応を測定する。その測定状態において、fMRI→画像の特徴量を生成するものがある。
すなわち、本来であれば、画像→特徴量なのだが、それをfMRI→特徴量としている。この点で精度が画像→特徴量よりも低いのは致し方ない。むしろここにこそ、曖昧さが存在していて、創造性研究に役立つ可能性もあると思う。
そしてその結果から、画像の特徴量が得られる。これはおそらくCNNみたいな感じで、複数のレイヤーにおける特徴量が得られると思う。すなわち浅いレイヤーでは具体的な形を捉えており、深いレイヤーでは抽象的な形を保持しているものと考える。
これらの画像の特徴量に関する複数のレイヤーをうまく組み合わせる2つ目のネットワークに通す。
すなわち、特徴量から画像を生成するというCNNの逆を行うDGNというネットワークモデルを使っている。
結果として、アルファベットなどの幾何学的な構造はうまくいった。
しかしながら、複雑な状態の画像を想像してもらう→それを画像化 というのはものすごく難しかったとの結果。 原因としてはネットワークにもあるだろうが、おそらく人間の想像力ではそこまで詳細に描けないという点が挙げられる。思ったこと・興味
プロ棋士の頭の中を描いてみたい。それをしてみたらどのくらい詳細に画像を描くことができるのだろうか気になる。
人それぞれ違うということもあり得るだろう。
宮崎駿も見てみたい。
fMRI→画像の特徴量 という変換ネットワーク。この時点で結構な論理的飛躍が起こっていて、精度が低いのかなぁと思った。
もちろん 画像の特徴量→画像生成 というDGNの精度も低いのだと思う。
精度が悪いフィルタを2つ通すことで、結構下がっているのは共通認識っぽいなぁ。ただその精度の悪さでも、今はアルファベットくらいなら画像生成できるのがすごいと思った。
- 投稿日:2020-10-23T21:30:35+09:00
論文: 頭の中で映像を描けない人(アファンタジア)についての論文,The blind mind: No sensory visual imagery in aphantasia
https://www.gwern.net/docs/psychology/2018-keogh.pdf
アファンタジアとは?
アファンタジアは低レベルの感覚的視覚イメージの欠如によって特徴づけられている。メタ認知欠如ではないことを支持する。
アファンタジアは空間イメージは平均より得意である。
空間イメージは、精神回転課題のパフォーマンスと相関があるようだ。また、関連として、幻覚症の人は、このスコア完璧。
脳の構造について、背側(初期視覚野から頭頂葉までの初期視覚野)には空間内の物体の位置に関する情報が含まれる。
腹側(初期視覚野から側頭葉までの初期視覚野)または「何を」の流れには物体の同一性に関する情報が含まれている。
2つは階層が上に行くほど複雑になる。心の中で物体を回転させる能力は?
→補助運動野や一次運動野などの運動野に加えて、頭頂皮質(具体的には頭頂皮質)を活性化する
対照的に、参加者が静止画像を想像すると、視覚野は活動の増加を示す傾向がある
画像を想像する→視覚野から複合される。
視覚野の応答レベルが高いほど、イメージの主観的な鮮明度と相関がある。この2つの証拠から、静的な物体イメージと精神的な回転や空間イメージに使用される神経ネットワークの分離を示唆しています。
人が風景や物体を想像するとき→視覚野だけでなく、頭頂部や前頭前野にまで及ぶ大規模なネットワークが活性化される
前頭前野→視覚野の感覚表象を活性化するフィードバック接続を駆動(アファンタジアはこれができない可能性)
視覚野と前頭前野の両方の皮質の興奮性が、イメージの強さを支配する上で重要な役割を果たしていることが示されています
以上より、アファンタジアは視覚領域、前頭前野領域、またはその両方の領域で以上な活動レベルの可能性がある。
私たちの内的世界に対する能力や経験が大きく異なる原因となっている神経学的な違いを理解するのにも役立つでしょう。
- 投稿日:2020-10-23T21:28:55+09:00
論文:動的な自然風景のために、ディープラーニングを使って神経から生成するよっていう論文(Neural Encoding and Decoding with Deep Learning for Dynamic Natural Vision )
どういう論文なの?ざっくり
機械学習(ニューラルネットワーク,CNN)は、人間の脳を模倣してきた。それを利用すると、そこから画像を見たときの脳内反応(MRI)を再現できるようになった!そして逆もできるようになった!つまり逆とは、MRIの反応結果から、ある画像の特徴量を予測できる!ということ。
今まで模倣と言われていたけれど、それが比喩ではなく本当にすごい精度で模倣していたんだなぁと裏付けられる論文な気がしました!
https://academic.oup.com/cercor/article/28/12/4136/4560155
アブストと自分的まとめ
畳み込みニューラルネットワークは、脳みその画像処理機構を真似しているとされたが、それがきちんとわかってきたということ。
つまり、CNNの特徴量から→fMRIを作成できるし、逆もまた然りなことが最近の研究でわかってきた!
CNNは、時系列とかを考慮してないのに学習してるのがすごい!end to end ってこういうことなのかなぁって思った。
CNN と fMRIの双方向性を補強する論文。
また、CNNはおなか側だけでなく(こっちはすでに完璧に予測している)、背面側のfMRIも上手に予測したよ!程度は低いけど。
fMRI信号を直接デコードすることで、視覚空間と意味空間の特徴表現を推定し、視覚的再構成と意味的分類を行うことができました。
自分の意見:これはあくまで、fMRIと同レベルまできたというだけの話であり、FMRIも完全にはわかっていないはず。あくまで別のやり方で、同じくらいわかるようになってきた、つまり手法Aと手法Bで矛盾が生じなかったという説明にしかならないと思われる。ただそれだけで、畳み込みニューラルネットワークすごい!とは言い切れない。その可能性もあるけど。結果
CNNとfMRIの関係性についての結果。CNNと視覚野は、低レベルの視覚的特徴(例えば網膜トピ)と高レベルの意味的特徴(例えば顔)の類似した表現を共有しているだけでなく、抽象度の高い複数の中間レベルの視覚情報の階層的な表現も共有している(図2)
神経を再現することについての結果と妥当性。CNNの結果から、どの脳領域が活性化しているかを線形回帰モデルでやってみた。ラベルづけで分類する。
- 投稿日:2020-10-23T21:27:54+09:00
論文: 「赤い」+「丸い」のような単語指定だけで、いろんな画像作ってくれる論文(SCAN: Learning Hierarchical Compositional Visual Concepts
仕組みについて
画像から意味を理解する。大量のリンゴ画像→イメージへ。これにより、丸い・赤い・小さい などの特徴を獲得する。
具体化の時は、獲得した概念をもとに画像を生成する。 丸い・赤い・小さい、などによる拘束はあるが、それ以外の縛りは存在しない。したがって照明や背景について自由度はあるため、多様性のある画像が生成される。超大事ポイント
以下のような命令によって到達することができます。"blue" AND "small"(より一般的なものからより特定のものへと階層を下降させる)、"blueeberry" IN COMMON "blueebell"(より特定のものからより一般的なものへと階層を上昇させる)、または "blueeberry" IGNORE "round"(同じく階層を上昇させる)という指示によって、概念{blue, small}に対応する新しいノードに到達することができる。
- 投稿日:2020-10-23T21:26:44+09:00
論文: マルチリンガルの子供の学習方法を機械学習で模倣するぞ! (Visual Grounding in Video for Unsupervised Word Translation)
アブスト
視覚ベースをヒントに、多言語間の概念マッピングを行います
良いポイント3つは何か?
異なる言語の間のペアを学習させることができる。
データセットが少ない言語についても対応できる
テキストベースの翻訳技術に関しても、初期化の際に役立つことができる。やり方
1つの料理の工程に対して、複数の言語によるナレーションをデータセットとして用いる。これにより視覚的なヒントから複数の言語を学習すると言うマルチリンガルの子供の学習方法の模倣することができる。
比較対象
ウィキペディアを用いたフランス語と英語などの多言語間のマッチング。非類似性からスコア
3つの貢献
我々の方法が、テキストベースの方法の多くの欠点に対処する既存の単語マッピング技
。教師なし学習であること。
教師なし学習
2つの言語間のコーパスなしで学習できるという点で教師なし学習である。
ビジュアルドメインZを土台として学習するぞ。結論
YouTubeの動画から学習することができる。多様なコーパスに直面したときに、より強い効果を発揮する。
- 投稿日:2020-10-23T20:48:45+09:00
チームで出欠管理システムを作りました
社内チームで今年一年の成果物として出欠管理システムを作成しました!!
この記事ではその概要を紹介します。プロジェクト概要
成果物として何を作成するかチームで話し合い、社内全体で利用でき作業効率化につながるシステムについて考えました。
社内行事の出欠管理がExcel管理されており、作業負荷がかかる状態だったため、この改善としてQR認証を利用した出欠管理システムを作ることになりました。システム運用イメージ
開催者がイベント開催日を登録
開催者がイベントの出席状況を確認する
システムの使い方
QRコード発行
イベント登録/QRコード読み取り
イベント履歴確認
活用技術
メンバーの各々が得意な分野を用いて作成されています。
- 実行環境
- GCP
- Compute Engine
- Cloud DNS
- Cloud Load Balancing
- Docker
- Nginx
- PostgreSQL
- Webアプリ
- JavaScript
- node.js
- Vue.js
- TypeScript
- API
- Python
- Flask
システム構成
GCPを利用して環境を構築しました。
Web画面、API、DBをVM上にDockerコンテナとして常駐させています。
タブレットでブラウザからURLへアクセスするとDNS・LB・Nginxを経由、Web画面またはAPIへリクエストし、QRコード生成/認証・イベント履歴閲覧などの各処理を行う実装方式としています。
QR認証履歴などの各データへのアクセスはAPIからDBへSQLを発行することで保存するようにしました。開発期間
主担当3人で日常業務の片手間で約3か月ほどかかりました。
実際の作業時間はもっと短かったです。苦労した点
- 担当者3人で作った成果物の結合
インフラ面で統一した開発環境を用意せず進めたため、実際に結合した際にCORSの考慮漏れ等でwebアプリがうまく動作しませんでした。
躓いた点はIssueに整理して今後のチーム開発では気を付けていきます。- クライアントからAPIへのアクセス
Web画面からAPIにリクエストする際、クライアントからリクエストを飛ぶことを意識していなかったため、ファイアウォールやDNS・LB・Nginxの設定に苦労しました。
今後システム構成を考える際は意識していきたいと思います。今後の展開
- 管理画面へのログイン認証機能の実装
実装工数が足りなかったため実装できませんでした。
Amazon Cognitoを利用した実装を検討中です。- システム基盤をAWSへ移行
前述のAWS Cognito実装などAWSサービスの勉強も今後チームで実施していきたいと考えています。
併せて、基盤移行も検討中です。- 各社内イベントでの運用
コロナの影響で実際に人が集まる機会がなく、テストが不十分です。
データ量や負荷など分からない点が多いので、今後活用してもらっていきたいと考えています。
- 投稿日:2020-10-23T19:50:22+09:00
【python】numpy.empty 初期値設定
numpyで初期値設定をするときにいつも numpy.zeros か numpy.ones しか使ってなかった。
この他にも初期値設定する方法 numpy.empty があったので書く。
また、初期値設定の使い方を確認する。環境
linux
pyhtonnumpy.empty
これの引数↓
numpy.empty(shape, dtype=float, order='C')
実験1
引数に何も持たせない。>>> import numpy >>> numpy.empty() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: empty() missing required argument 'shape' (pos 1shapeの引数がないというエラーが出る。
実験2
shapeを持たせる。>>> import numpy >>> numpy.empty(shape=1) array([0.]) >>> numpy.empty(shape=2) array([-5.73021895e-300, 6.93125508e-310]) >>> numpy.empty(shape=3) array([0., 0., 0.]) >>> numpy.empty(shape=4) array([0., 0., 0., 0.]) >>> numpy.empty(shape=5) array([4.66352184e-310, 4.03179200e-313, 4.66352172e-310, 5.54048513e+228, 7.56680154e+168]) >>> numpy.empty(shape=6) array([4.66352179e-310, 5.72938864e-313, 6.93125428e-310, 6.93125428e-310, 0.00000000e+000, 1.16707455e-072]) >>> numpy.empty((2,3)) array([[4.66352179e-310, 5.72938864e-313, 6.93125428e-310], [6.93125428e-310, 0.00000000e+000, 1.16707455e-072]]) >>> numpy.empty(5.6) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'float' object cannot be interpreted as an integerできた。。。このことから、shape=はなくても良い、数字に少数はできない、初期値はshapeによってすべての値が0になることもある、ということがわかった。
実験3
dtypeを持たせる。実験1からshapeは必須ということが分かるのでshapeも持たせた。>>> import numpy >>> numpy.empty(2,dtype=float) array([-5.73021895e-300, 6.93125508e-310]) >>> numpy.empty(2,dtype=int) array([-9093133594791772939, 140290164735896]) >>> numpy.empty(2,int) array([130238442063002869, 140290164735896])このことから、dtypeをfloatにしたら初期値が少数に、intにしたら初期値が整数になることがわかる。また、引数のdtype=の部分は省略してもよいこととが numpy.empty(2,int) からわかる。
実験4
order='C'を変える。>>> import numpy >>> numpy.empty(2,dtype=float,order='C') array([-5.73021895e-300, 6.93125508e-310]) >>> numpy.empty(2,dtype=float,order='F') array([5.73021895e-300, 6.93125508e-310]) >>> numpy.empty(2,dtype=float,order='E') Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: order must be one of 'C', 'F', 'A', or 'K' (got 'E') >>> numpy.empty(2,dtype=float,order='A') Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: only 'C' or 'F' order is permittedこのことから、order='C'かorder='F'しか使えないことが分かる。また、order='C'とorder='F'の違いは見られなかった。そのため、どちらでも良いのではないかと思った。
- 投稿日:2020-10-23T19:22:52+09:00
Mayaのpythonでprint命令自体がエラーになる場合の対処法
以下、Windowsの特定のMayaバージョンだけに関連する話です。Macや最近のバージョンのMayaではでくわしたことがありません。
そんな馬鹿な!?
上記のようにprint命令自体がエラーになるいう謎現象にあった場合、システム環境変数に MAYA_NO_CONSOLE_WINDOW が設定されていないか確認しましょう。
これはコンソールウィンドウを表示しなくするために設定する環境変数です。
https://support.borndigital.co.jp/hc/ja/articles/360002474194-Maya-%E3%81%AE-Output-Window-%E3%82%92%E9%9D%9E%E8%A1%A8%E7%A4%BA%E3%81%AB%E3%81%99%E3%82%8BMAYA_NO_CONSOLE_WINDOWが設定されているとMaya2017 Update 5など(正確な対象バージョンはわかりませんが)一部の環境でprint文が通らなくなります。自分が試した限り、設定数値が1ではなく0であってもダメでした。
printが通らない状況というのは、stdout 設定がおかしくなっているときのようで、コンソールウィンドウを表示しない事で割り当てがなくなってしまうのでしょうね。
一応、下記のコード実行でFIXできますが、他何かおかしくなってるかもしれないので、MAYA_NO_CONSOLE_WINDOW 定義をなくすほうが良さそうです。
# https://stackoverflow.com/questions/43633433/maya-python-ioerror-errno-9-bad-file-descriptor import maya.utils as utils sys.stdout = utils.Output()特定の環境だけ不具合が起こるというのがいやらしいです。
FIXED!
- 投稿日:2020-10-23T17:01:04+09:00
Pythonのデータフレームを可視化する (Rstudio)
前置き
はっきり言ってとても無駄なことをしてしまいました。(ぶっちゃけコードの保存用に載せます)
Rstudioでpythonを使っていくのをより便利にしたいと考え、関数を作ってみましたが、結局利便性はほとんど上がっていないです。。Rstudioでpythonを使う方法などはこちら。
Pythonのデータフレーム
どうも。reticulate大好き人間です。
pythonのデータフレームはpandasを使うのが一般的ですが、Rstudioでpythonを使っているとデータフレームが見づらい、、という問題があります。
例えば、、
#「データサイエンティスト協会スキル定義委員」の #「データサイエンス100本ノック(構造化データ加工編)」のデータを # 利用させていただきます。 >>> import pandas as pd >>> df = pd.read_csv("customer.csv") >>> print(df)うーん、、悪くないんだけど。。
途中省略されちゃってるしちょっとびみょいですね!RstudioのView( )関数
解決策は簡単です。
・素直にgoogle colabを使う
(dataframeは比較的きれいに表示されます)・Rstudio特有のView( )関数を使う
View( )関数が一番手っ取り早いです。
>>> quit #pythonを抜けてRに戻る > # library(reticulate) > View(py$df)こうすれば、いつも通りグリグリ動かせる超見やすいdataframeが出てきます。
関数化
ではこの
「reticulateパッケージの読み込み(読み込まないとpy$を使えません) → Viewerでの表示 → pythonに戻る」
という一連の動作を関数にしてみましょう。
py_view = function(py_df_name, i_overwrite = FALSE, repl = TRUE){ library(reticulate) # if(class(py_df_name) == "character"){ if((class(try(py$i, T)) == "try-error") | (class(try(py$i, T)) != "try-error" & i_overwrite == TRUE)){ try(py_run_string(paste("i = globals()['", py_df_name, "']", sep = "")), T) if(class(try(class(py$i), T)) == "data.frame"){ View(py$i, py_df_name) py_run_string("del i") #repl if(repl == T){repl_python()} }else{ message(paste(" データフレーム'", py_df_name, "'が見つかりませんでした。", sep = "")) try(py_run_string("del i"), T) } }else{ message(" 'i'が存在します。削除するか引数i_overwrite = TRUEを設定してください。") } }else{ message(" 引数py_df_nameを文字列で指定してください。") } }めちゃめちゃ苦戦してしまいました。まず引数(dataframe名)を「py$~」としてしまえば一瞬で終わるのですが、文字列で一致する名前のdataframeを持ってくるという形にしました。
動作としては、引数py_df_nameがcharacter(文字列)だった場合python内でiという変数にdataframeを移動して、それをView( )関数でみている形になります。
tryによって例外処理をいくつも作ってますね。解説めんど、、、
使ってみる
使い方は簡単。
> py_view("df") >>>これだけで先ほどのViewerが表示され、pythonに戻ることができています。
まとめ
以上!!!
思ったよりも無駄でした。「library(reticulate)」をしておかないと「py$~」で躓くのとView( )内で毎回「py」を付けないといけないのをめんどくさいと思って作ってみましたが、結局この作業した方が速いんじゃないかって感じですね。
とりあえずRでの例外処理の練習になったのでよし。
- 投稿日:2020-10-23T16:38:33+09:00
【Python】アルゴリズムを意識したコード
アルゴリズムを意識したPythonコード
問題
ある学校の授業の時間割が与えられる。 1つの教室で同時に出来る授業は1つまで。 学校には最低いくつの教室が必要か求めるプログラムを実装せよ。 [(開始時間, 終了時間), ...] なお、"解決①"の実装より処理速度の速い実装であること。INPUT/OUTPUT
# 例題 classes1 = [(0, 50), (50, 100)] answer1 = 1 classes2 = [(0, 50), (50, 100), (20, 70)] answer2 = 2 classes3 = [(10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (60, 80)] answer3 = 3 classes4 = [(0, 50), (50, 100), (20, 70), (50, 100)] answer4 = 3 classes5 = [(0, 20), (10, 30), (20, 40), (30, 50)] answer5 = 2 classes = [ (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), (10, 50), (20, 30), (30, 70), (60, 100), (70, 90), ]解決①
def dec_speed(func): def wraps(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f'処理速度: {end - start}') return result return wraps def check_time_overlaps(a: tuple, b: tuple) -> bool: return b[0] < a[1] < b[1] @dec_speed def solution(classes: list) -> int: num_classes = len(classes) max_classes = 1 for i in range(num_classes): rooms = 1 for j in range(num_classes): if i != j: check = check_time_overlaps(classes[i], classes[j]) rooms += 1 if check else 0 max_classes = max(max_classes, rooms) return max_classes assert solution(classes1) == answer1 assert solution(classes2) == answer2 assert solution(classes3) == answer3 assert solution(classes4) == answer4 assert solution(classes5) == answer5 print('OK') # 処理速度の確認 solution(classes)回答
@dec_speed def solution2(classes: list) -> int: timeline = [] for start, end in classes: timeline.extend([(start, True), (end, False)]) timeline = sorted(timeline) max_rooms = 0 rooms = 0 for _, is_start in timeline: rooms += 1 if is_start else -1 max_rooms = max(max_rooms, rooms) return max_rooms assert solution2(classes1) == answer1 assert solution2(classes2) == answer2 assert solution2(classes3) == answer3 assert solution2(classes4) == answer4 assert solution2(classes5) == answer5 print('OK') # 処理速度の確認 solution2(classes)参考Youtube
- 投稿日:2020-10-23T16:05:25+09:00
Cloud Vision API (GCP vision AI ) で "AttributeError: module 'google.cloud.vision' has no attribute 'types'"
概要
GCPの物体検出API1のチュートリアルを実行していたら,エラーが発生したので解決方法を執筆.
APIの認証方法は省略.
使用言語はPython.エラーについて
チュートリアルの以下のコードを実行すると,AttributeErrorが発生する.
(下記コードはチュートリアルからコピペ)def localize_objects(path): from google.cloud import vision client = vision.ImageAnnotatorClient() with open(path, 'rb') as image_file: content = image_file.read() image = vision.types.Image(content=content) objects = client.object_localization( image=image).localized_object_annotations print('Number of objects found: {}'.format(len(objects))) for object_ in objects: print('\n{} (confidence: {})'.format(object_.name, object_.score)) print('Normalized bounding polygon vertices: ') for vertex in object_.bounding_poly.normalized_vertices: print(' - ({}, {})'.format(vertex.x, vertex.y))エラー内容は以下の通り.
7行目で発生している.google.cloud.visionからtypesが削除されたらしいのでエラーが発生する.AttributeError: module 'google.cloud.vision' has no attribute 'types'解決策
visionから直接Imageを使えばOK
image = vision.types.Image(content=content) # エラー image = vision.Image(content=content) # 解決全体像は以下の通り,7行目しか変更していない
"""Localize objects in the local image. Args: path: The path to the local file. """ from google.cloud import vision client = vision.ImageAnnotatorClient() with open(uri, 'rb') as image_file: content = image_file.read() image = vision.Image(content=content) objects = client.object_localization( image=image).localized_object_annotations print('Number of objects found: {}'.format(len(objects))) for object_ in objects: print('\n{} (confidence: {})'.format(object_.name, object_.score)) print('Normalized bounding polygon vertices: ') for vertex in object_.bounding_poly.normalized_vertices: print(' - ({}, {})'.format(vertex.x, vertex.y))余談
公式チュートリアルって意外とバグがあったりしますよね.
関係ないですが,GCPのImageはPillowのImageと被ってて若干使いづらいです.
- 投稿日:2020-10-23T15:47:19+09:00
Pythonでビン取りゲームを作ってみた
ビン取りゲームというゲームをPythonで作ってみた。
しかし何本とるかをランダムで決めているだけだからすごく弱い...コード
BottlePickingGame.pyimport random import time import sys print(""" ビン取りゲームを始めます。ビン取りゲームとは、 ビンの本数を決めて交互にビンを取っていき、 最後のビンを取った人が負けです。 ビンの全体の最低本数は15本で、 1回に取れる本数は1本、または2本、または3本です。 """) time.sleep(2) Number=0 while 15>Number: try: Number=int(input("ビンは何本にしますか?\n")) if 15>Number: print("ビンの最低本数は、15本です") except ValueError: print("半角数字で整数を入力してください") remaining=0 remaining=int(remaining) turn=0 take=0 take=int(take) print("\n\nゲームを開始します") time.sleep(1) while Number>=remaining: print("\n残りの本数は、",Number-remaining,"本です",sep="") time.sleep(0.5) print("\nあなたの番です") turn=0 take=0 time.sleep(0.5) while take>3 or 0>=take: try: take=int(input("何本取りますか?\n")) if take>3 or 0>=take: print("取れる本数は、1〜3本です") except ValueError: print("半角数字で整数を入力してください") remaining=remaining+take print("\n残りの本数は、",Number-remaining,"本です",sep="") if remaining>=Number: break time.sleep(0.8) print("\nCPの番です") turn=1 if Number>remaining+3: take=random.randint(1,3) take=int(take) elif Number>remaining+2: take=2 take=int(take) elif Number>remaining+1: take=1 take=int(take) else: take=1 take=int(take) remaining=remaining+take time.sleep(0.5) print(take, "本取りました",sep="") if remaining>=Number: break if turn==0: time.sleep(0.5) print("\n\nあなたの負けです...") else: time.sleep(0.5) print("\n\nあなたの勝ちです!!") time.sleep(1) print("ゲームを終了します") time.sleep(5) sys.exit(0)
- 投稿日:2020-10-23T15:38:34+09:00
poetryのuninstall
概要
備忘録。
poetryのpoetry self update
を行った後、poetryを実行したら、あらゆるpackageが足りないと無限にerrorを吐かれたので、
仕方なく、poetryをuninstallした後、installをし直した。公式ドキュメントにて詳細は参照されており、本記事もそれに参考してます。
https://python-poetry.org/docs/#:~:text=If%20you%20see%20something%20like,variable%20before%20executing%20the%20installer.前提
- How to install poetry
$ curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -Uninstall方法
- How to uninstall poetry
$ curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python - --uninstall以上、解散
- 投稿日:2020-10-23T15:08:26+09:00
tif形式の気象データをnumpy配列に変換する
やりたいこと
- tif形式で格納されている気象データをnumpy.arrayに変換しpythonでかんたんに利用できるようにしたい!
そもそもtif形式の気象データ とは?
世界全体の気温や降水量などの数値が格納されているデータ.ラスタデータと呼ばれる.
世界地図をグリッド(1マスを1度,0.5度など様々な粒度がある)に区切り,その1マス毎に気象データを格納されている.例:worldclim
WorldClimの[Historical climate data]
1970-2000年までの1−12月の月平均数値を記録したもの.最低/最高/平均気温,降水量などのデータが10m~30sまでの粒度で利用できる.とりあえず触ってみる.
今回はとりあえずminimum temperature-5minutesをダウンロードしてみる.
ちなみに
5minutesとは,1辺が緯度経度で言う1/12度の大きさ(1hで1度)の正方形グリッドで世界地図を区切ったもの.
世界地図は緯度が90~-90,経度が180~-180の180*360の長方形で表すことができる.今回のデータは1/12度で1マスなので,縦2160*横4320マスである.
以下のQiitaの記事に詳しい記載があったので,詳細はそちらを.とてもわかり易い.
- GISで使う経緯度の基礎知識とかTIPSとか - Qiitaダウンロードして展開すると,以下のような[wc2.1_5m_tmin_XX.tif]と名前のついた12のtifファイルが確認できる.それぞれが1−12月の30年分の月最低気温データになっている.
なお今回の粒度だと1ファイルあたりのサイズは11MB程度と可愛いが,これが30s(現在の10倍の粒度)とかになるとファイルサイズも可愛くなくなってくるので注意してほしい.
QGISを使ってデータを取り出す
QGISとはオープンソースGISソフトで,地理情報システムの一種だ.今回のような気象データや地形データなど,様々な地理データを扱うことに向いている.今回はtifファイルをnumpyに変換することが目的なので詳しくは触れない意が,興味がある方は以下を参照していただきたい.
- QGISビギナーズマニュアルかんたんな使い方
- QGISをダウンロードし起動,CMD+Nまたはメニューボタンで新規プロジェクトを開く.
- 左側にあるレイヤウィンドウに先程のtifファイルをドラッグアンドドロップする.
右クリックしtifファイルの情報を確認できる.ファイルの幅と高さを確認し,自分の求めているスケールと一致しているか確認する.
ここからは実際にラスタデータをnumpy配列に変換していく.
QGIS内のPythonコンソールを使っていく.
メニューバーにあるPythonアイコンをクリックするとIpythonコンソールが起動される.
これ以降の操作はラスターデータの読み込み · GIS実習オープン教材を参考にした.numpyに変換する.
はじめに必要なライブラリーをインポートする.
from osgeo import gdal import numpy as np次に読み込みたいtifファイルのパスを与え,開く.
uri = 'YOUR_PASS/wc2.1_5m_tmin/wc2.1_5m_tmin_01.tif' src = gdal.Open(uri, gdal.GA_ReadOnly) res = src.GetRasterBand(1) res_arr = res.ReadAsArray()これで
res_arr
にnumpy配列が格納されたはずだ.
確認してみよう.>>> res_arr array([[-3.4000000e+38, -3.4000000e+38, -3.4000000e+38, ..., -3.4000000e+38, -3.4000000e+38, -3.4000000e+38], [-3.4000000e+38, -3.4000000e+38, -3.4000000e+38, ..., -3.4000000e+38, -3.4000000e+38, -3.4000000e+38], [-3.4000000e+38, -3.4000000e+38, -3.4000000e+38, ..., -3.4000000e+38, -3.4000000e+38, -3.4000000e+38], ..., [-1.9660000e+01, -2.1010000e+01, -2.1010000e+01, ..., -2.2410000e+01, -2.2410000e+01, -1.9708000e+01], [-2.2403999e+01, -2.3939999e+01, -2.3939999e+01, ..., -2.5539999e+01, -2.5539999e+01, -2.2451000e+01], [-1.6927999e+01, -1.8080000e+01, -1.8080000e+01, ..., -1.9279999e+01, -1.9279999e+01, -1.6963999e+01]], dtype=float32)問題なさそうだ.ちなみに値が
-3.4000000e+38
となっている箇所は,陸地がなく,データが入っていないNAのマスだ.
念の為arrayのshapeも確認しておく.>>> res_arr.shape (2160, 4320)先程プロパティで確認したサイズと一致している.
np.save
で保存すればnumpyarrayとして利用できる.おまけ:12ヶ月の平均データを取る.
ここまでくればあとは簡単で,01~12までのtifを読み込んで平均をとってやれば
目的のデータは入手できるはずだ.dict = {} from osgeo import gdal import numpy as np for a in range(1,13): uri = '/YOUR_PASS/wc2.1_5m_tmin/wc2.1_5m_tmin_'+str(a).zfill(2)+'.tif' print(uri) src = gdal.Open(uri, gdal.GA_ReadOnly) res = src.GetRasterBand(1) res_arr = red.ReadAsArray() dict[a] = res_arr res = np.stack(dict.values(),axis=1) data_m = np.mean(res, axis=1) np.save('/YOUR_PASS/wc2.1_5m_tmin/mean.npy', data_m)これで完成.
以上です.はじめ検索した際にやり方がまとまったサイトを見つけられなかったのでまとめておきました.
- 投稿日:2020-10-23T14:04:19+09:00
[Python] Python文法のサンプルコード
はじめに
Pythonの勉強のために以下のようなプログラムを書きましたが、
もっと効率的な書き方があると考えているので、
有識者の方々の意見が聞きたく記事にしました。
もっと行数短く書けるよ!っていう有識者の方々、是非ともコメント頂きたいです。和暦変換
和暦変換# 年数(西暦)で入力 year=int(input()) Syear=[1868, 1912, 1926, 1989, 2019, 9999] wareki=['明治', '大正', '昭和', '平成', '令和', ''] i=0 # 和暦変換 while Syear[i]<=year: Wyear=year-Syear[i]+1 Wname=wareki[i] i+=1 # 出力 print(str(year)+'年は'+Wname+str(Wyear)+'年')年齢判定
年齢判定import datetime # 生年月日(西暦)を入力 Tymd=input() # 年月日に分割 param=Tymd.split('/') Tyear=int(param[0]) Tmonth=int(param[1]) Tday=int(param[2]) # 現在の日付を取得 Today=datetime.datetime.now() year=Today.year month=Today.month day=Today.day # 年齢計算 age=int(year)-int(Tyear) if int(month) < int(Tmonth): age-=1 else: if month==Tmonth: if day < Tday: age-=1 # 出力 print(Tymd+'の人は'+str(age)+'歳')曜日判定
曜日判定import math # 入力値(例: 1997/9/11)をうけとる x=input() # 入力値を年月日に分割 y=x.split('/') year = int(y[0]) month=int(y[1]) day=int(y[2]) # 出力 print(str(year)+'年'+str(month)+'月'+str(day)+'日は', end='') if month < 3: month=month+12 year=year-1 # 曜日を求める weekday = (year + math.floor(year / 4) - math.floor(year / 100) + math.floor(year / 400) + math.floor((13*month+8) / 5) + day) % 7 week = ['日', '月', '火', '水', '木', '金','土'] # 出力 print(str(week[weekday])+'曜日です')複数桁の桁分解
複数桁の桁分解# 入力値をうけとる x=int(input()) num=[] # 1桁ごとに分解 while x>0: num.append(x%10) x//=10 num.reverse() # 出力 for i in range(len(num)): print(num[i], end=' ')閏年判定
閏年判定# 年数(西暦)を入力 year=int(input()) # 閏年判定 if year%4==0: if year%100==0: if year%400==0: print(str(year)+'年は閏年') else: print(str(year)+'年は閏年') else: print(str(year)+'年は閏年ではない')干支判定
干支判定# 年数(西暦)を入力 year=int(input()) # 全干支 Alleto =["申(さる)", "酉(とり)", "戌(いぬ)", "亥(いのしし)", "子(ねずみ)" , "牛(うし)", "寅(とら)", "卯(うさぎ)", "辰(たつ)", "巳(へび)", "午(うま)", "未(ひつじ)"] # 干支判定&出力 print(str(year)+'年の干支は'+Alleto[year%12])素数判定
素数判定# 2以上の数値を入力 num=int(input()) # 0: 素数 # 1: 素数ではない sosu=0 # 素数判定 for i in range(2, num): if num%i==0: sosu=1 break # 出力 if sosu==0: print(str(num)+'は素数である') elif sosu==1: print(str(num)+'は素数ではない')
- 投稿日:2020-10-23T12:41:27+09:00
matplotlibで複数系列をまとめた棒グラフを描画する方法
ちょうどよい記事がなかったので備忘録として。
# version matplotlib == 3.2.2import numpy as np import matplotlib.pyplot as plt # 縦棒 def barplot(ax, labels, datas): x = np.arange(len(labels)) # the label locations width = 0.35 # the width of the bars rects1 = ax.bar(x - width/2, datas[0]["val"], width, label=datas[0]["label"]) rects2 = ax.bar(x + width/2, datas[1]["val"], width, label=datas[1]["label"]) # Add some text for labels, title and custom x-axis tick labels, etc. ax.set_xticks(x) ax.set_xticklabels(labels) # 横棒 def barhplot(ax, labels, datas): x = np.arange(len(labels)) # the label locations width = 0.35 # the width of the bars rects1 = ax.barh(x - width/2, datas[0]["val"], width, label=datas[0]["label"]) rects2 = ax.barh(x + width/2, datas[1]["val"], width, label=datas[1]["label"]) # Add some text for labels, title and custom x-axis tick labels, etc. ax.set_yticks(x) ax.set_yticklabels(labels) plt.gca().invert_yaxis()使い方は以下の通り。
labels = ['G1', 'G2', 'G3', 'G4', 'G5'] datas = [ {"label": "men1", "val": [20, 34, 30, 35, 27]}, {"label": "women2", "val": [25, 32, 34, 20, 25]}] fig, ax = plt.subplots() # barhplot(ax, labels, datas) barplot(ax, labels, datas) plt.legend() plt.show()3以上の場合には、widthを調整して並べる必要がある。
- 投稿日:2020-10-23T12:19:34+09:00
Codeforces Round #673 (Div. 2) バチャ復習(10/22)
今回の成績
今回の感想
D問題が解けそうで解けなかったのでムカついて記事を書いています。
[追記]
よく見たら自明な条件を見逃していました。解法に固執し過ぎました…。頭が硬すぎる…。A問題
$i,j$を選び$a_j$に$a_i$を足すので最小の$a_i$を選んで操作を行うのが最大の操作回数です。したがって、$i \neq j$のもとで任意の$j$について操作を行うときの最大の操作回数を考えます。$k$を超えないので、それぞれの$j$で$[\frac{k-a_j}{a_i}]$が際だの操作回数になります。
A.pyfor _ in range(int(input())): n,k=map(int,input().split()) a=list(map(int,input().split())) m=min(a) l=a.index(m) ans=0 for i in range(n): if i!=l: ans+=(k-a[i])//m print(ans)B問題
$f(b)=(b_i+b_j$=$T$となる$i<j$の組の総数)であり、$a$を分割した$c,d$の$f(c)+f(d)$を最小にすることを考えます。ここで、$x$を$T-x$とは異なる方の配列に入れればいずれも0にすることができるのではと考えました。
$x \neq T-x$であれば必ず成り立たせられますが、$x=T-x \leftrightarrow x=\frac{T}{2}$となる$x$が三つ以上存在する場合は0にすることができず、$x=\frac{T}{2}$となる要素を$k$個として$c,d$にそれぞれ$\frac{k}{2},k-\frac{k}{2}$個ずつ振り分ける時に$f(c)+f(d)$は最小になります。
よって、以下のような場合分けをします。
(1)$T$が奇数のとき
$[\frac{T}{2}]$以下の要素を$c$に,$[\frac{T}{2}]$より大きい要素を$d$に入れれば良いです。(2)$T$が偶数のとき
$[\frac{T}{2}]$未満の要素を$c$に,$[\frac{T}{2}]$より大きい要素を$d$に入れた元で$[\frac{T}{2}]$の要素のインデックスを$cand$に一旦入れます。
$cand$のサイズが先ほどの$k$なので、$c,d$にそれぞれ$\frac{k}{2},k-\frac{k}{2}$個ずつ振り分けます。B.pyfor _ in range(int(input())): n,t=map(int,input().split()) a=list(map(int,input().split())) ans=[-1]*n cand=[] for i in range(n): if t%2==0: if a[i]<t//2: ans[i]=0 elif a[i]>t//2: ans[i]=1 else: cand.append(i) else: if a[i]<=t//2: ans[i]=0 else: ans[i]=1 l=len(cand) for i in range(l): if i<l//2: ans[cand[i]]=0 else: ans[cand[i]]=1 print(" ".join(map(str,ans)))C問題
少し実装が面倒でしたが、実装するだけです。Bより方針は立ちやすいと思います。
ある長さ$k$の任意の連続部分列に含まれる要素で最小の要素を任意の$k$で求めます。まず、$k$が大きくなるにつれて単調減少するのは自明です。この時、それぞれの値がどのタイミングで出てくるか(主客転倒!)に注目すると、ある値の条件を満たす長さ$k$の連続部分列のうち最小の長さ$k$を求めれば良いのではと考えました。
したがって、ある値$x$となるインデックスの集合$x_1,x_1,…,x_l$(昇順で0-indexed)があり、さらに$x_0=-1,x_{l+1}=n$とした時、任意の$0 \leqq i \leqq {l}$で$x_{i+1}-x_i \leqq mi$が成り立てば良いです。よって、$mi=max(x_{i+1}-x_i)$が解です。(これは、図を書くと成立条件が見えやすいと思います。)
よって、それぞれの値で題意の最小の長さを求めることができるので($O(n)$)、あとは答えとなる最小の要素を求めることを考えます。つまり、実装は以下のようになります。
$values$=(<値,題意の連続部分列の長さの最小値>のペアを値の昇順で保存した配列)
$now$=(最小の要素が決まってない中で最長の長さ)
$ans[i]$=(長さ$i+1$の任意の連続部分列に含まれる要素で最小の要素)最小の要素から順に決めていけば良いので、$i$の昇順で$values$の$i$番目の要素を見ていくことを考えます。この時、$values[i].S$が$now$以下の時、$now$から$values[i].S$は$values[i].F$とすれば良いです。そして、$now$は$values[i].S-1$に更新します。また、$values[i].S$が$now$より大きいときは更新の処理を行わずに次の$values$の要素を見れば良いです。また、最小の要素が決まらない長さ$k$の部分列が存在する場合は$ans[k-1]$を-1とする必要があるので、初めに$ans$は-1で初期化しておきます。
C.cc//デバッグ用オプション:-fsanitize=undefined,address //コンパイラ最適化 #pragma GCC optimize("Ofast") //インクルードなど #include<bits/stdc++.h> using namespace std; typedef long long ll; //マクロ //forループ //引数は、(ループ内変数,動く範囲)か(ループ内変数,始めの数,終わりの数)、のどちらか //Dがついてないものはループ変数は1ずつインクリメントされ、Dがついてるものはループ変数は1ずつデクリメントされる //FORAは範囲for文(使いにくかったら消す) #define REP(i,n) for(ll i=0;i<ll(n);i++) #define REPD(i,n) for(ll i=n-1;i>=0;i--) #define FOR(i,a,b) for(ll i=a;i<=ll(b);i++) #define FORD(i,a,b) for(ll i=a;i>=ll(b);i--) #define FORA(i,I) for(const auto& i:I) //xにはvectorなどのコンテナ #define ALL(x) x.begin(),x.end() #define SIZE(x) ll(x.size()) //定数 #define INF 1000000000000 //10^12:∞ #define MOD 1000000007 //10^9+7:合同式の法 #define MAXR 100000 //10^5:配列の最大のrange //略記 #define PB push_back //挿入 #define MP make_pair //pairのコンストラクタ #define F first //pairの一つ目の要素 #define S second //pairの二つ目の要素 signed main(){ //小数の桁数の出力指定 //cout<<fixed<<setprecision(10); //入力の高速化用のコード //ios::sync_with_stdio(false); //cin.tie(nullptr); ll t;cin>>t; REP(_,t){ map<ll,vector<ll>> m; ll n;cin>>n; REP(i,n){ ll a;cin>>a; m[a].PB(i); } vector<pair<ll,ll>> values; FORA(i,m){ ll s=SIZE(i.S); ll mi=-1; REP(j,s){ if(j==0){ mi=max(mi,i.S[j]+1); } if(j==s-1){ mi=max(mi,n-i.S[j]); } if(j!=s-1){ mi=max(mi,i.S[j+1]-i.S[j]); } } values.PB(MP(i.F,mi)); } vector<ll> ans(n,-1); ll sv=SIZE(values); #if 0 REP(i,sv){ if(i==sv-1)cout<<values[i].F<<" "<<values[i].S<<"\n"; else cout<<values[i].F<<" "<<values[i].S<<endl; } cout<<endl; #endif ll now=n; REP(i,sv){ while(values[i].S<=now){ ans[now-1]=values[i].F; now--; } } REP(i,n){ if(i==n-1)cout<<ans[i]<<"\n"; else cout<<ans[i]<<" "; } } }D問題
$3n$回以下で自由に操作を行うことができるので、都合の良い場合を考えます。まず気づくのが、$i=1$とすれば値を調整することができるということです。また、$\sum_{i=1}^{n}{a_i}$が$n$の倍数であるときは達成できず、他の場合は達成できるものとして考えました。
ここで、$i=1$を選択した時に$a_1$は減る方向の変化をするので、$i=1$にできるだけ要素を集めてから振り分ける方法が良いのではと考えました(ここからの考察を詰め切ることができませんでした)。
また、$a_i$から$a_1$へと要素を移動させる時に$a_i$は$a_i \ mod \ i$だけ余ってしまいます。ここで、この余りの分も$a_1$に移すには一旦$a_1$から$a_i$に$i- a_i \ mod \ i$だけ移しておけば、再度$a_i$から$a_1$に移すことで$a_i=0$を達成することができます。この時、$a_1\geqq i- 1\geqq i- a_i \ mod \ i$を満たす必要がありますが、$i$の昇順で$a_i$から$a_1$へ要素を移動させたものとすると、$a_1=\sum_{j=1}^{i-1}a_i \geqq i-1$よりこの条件を満たします。
よって、$i$の昇順で上記を行って$a_1 =\sum_{i=1}^{n}{a_i}$とした(最大$2(n-1)$回)後に任意の$i$について$a_1$から$a_i$へと$b(=\frac{\sum_{i=1}^{n}{a_i}}{n})$だけ要素を移動させる(最大$n-1$回)ことで、全体で最大$3(n-1)$回で全要素を$b$に等しくすることができます。
D.pyfor _ in range(int(input())): n=int(input()) a=list(map(int,input().split())) ans=[] if sum(a)%n!=0: print(-1) continue b=sum(a)//n for i in range(1,n): if a[i]%(i+1)==0: ans.append([i+1,1,a[i]//(i+1)]) a[0]+=a[i] a[i]=0 else: x=i+1-a[i]%(i+1) ans.append([1,i+1,x]) a[0]-=x a[i]+=x ans.append([i+1,1,a[i]//(i+1)]) a[0]+=a[i] a[i]=0 for i in range(1,n): ans.append([1,i+1,b]) l=len(ans) print(l) for i in range(l): print(" ".join(map(str,ans[i])))E問題以降
今回は飛ばします。
- 投稿日:2020-10-23T12:16:33+09:00
はやぶさ2の軌道を可視化する
はやぶさ2の軌道データ
先日(2020/10/21),「はやぶさ2」の軌道情報が公開されました.
打ち上げから地球帰還直前(TCM-3)までの「はやぶさ2」の軌道上の記事に"hayabusa2_orbit_20201021.txt"というファイルが添付されています.
中身には,2014年の打上げ以降の,時刻・打上げからの起算日・はやぶさ2・地球・りゅうぐうの座標(太陽中心)などが記載されています.軌道精度は1000kmのため研究等には使えないと思います.本記事では公開された軌道データを用いて,はやぶさ2やりゅうぐうの軌道をアニメーションで可視化したいと思います.
(やっていることは,ファイルを読み込んで数値データをグラフ化しているだけです)ソースコードはgithubで公開しています(Pythonで実装しています).
https://github.com/motthi/hayabusa2_orbit太陽中心座標での軌道
往路
復路
地球中心座標での軌道
はやぶさ2と地球,りゅぐうと地球のデータの差を取れば地球から見た軌道も分かります.
往路
復路
はやぶさ2の今後
カプセルの地球帰還日は2020/12/6です.探査機本体はその後も運用を続け,2031年に1998 KY26という小惑星に向かう探査ミッション(EAEEAシナリオ)が計画されています.
- 投稿日:2020-10-23T11:55:05+09:00
geopandasのインポートでOSError: could not find or load spatialindex_c-64.dll
geopandas のインポートでコケた
OSError: could not find or load spatialindex_c-64.dllhttps://github.com/conda-forge/geopandas-feedstock/issues/78
ここによると、チャンネルは混ぜるな危険?ということらしい・・・私の環境では、Pythonはデフォルトチャンネル、
geopandasをインストールしたのはconda-forge
だったのが原因らしい。conda install python -c conda-forgeこれで解決!
ということは逆のパターンもあるのかな・・・
- 投稿日:2020-10-23T11:08:57+09:00
複数のpythonのバージョンを1つの jupyter で扱う
Overview
- python のバージョンを複数使いたい(venv含む)
- けど、それぞれのバージョンに jupyter 入れるのは効率が悪い
- ので、どれか1つのバージョンに jupyter を入れて kernel だけ選べるようにしたい
ときってあるじゃないですか。
具体的に
こういう状況で、
bash$ pyenv versions system 3.6.12 * 3.8.6 (set by /Users/kuryu/.pyenv/version)
3.8.6
で jupyter を起動して、3.6.12
の kernel を動かすのを目指す。まず jupyter を入れる
bash$ python -V Python 3.8.6 $ pip install jupyterpython のバージョンを変更する
bash$ pyenv global 3.6.12 $ python -V Python 3.6.12必要なら venv を作成
pyenv 環境にそのまま構築することも可能だけど、今回は venv 作ります。
bash$ python -m venv .venv $ . .venv/bin/activate (.venv) $ python -V Python 3.6.12 (.venv) $ pip list Package Version ---------- ------- pip 18.1 setuptools 40.6.2 You are using pip version 18.1, however version 20.2.4 is available. You should consider upgrading via the 'pip install --upgrade pip' command.ipykernel を入れる
bash(.venv) $ pip install ipykernel (.venv) $ ipython kernel install --user --name=hoge仮想環境を出て
bash(.venv) $ deactivatepython のバージョンも戻す
bash$ pyenv global 3.8.6 $ python -V Python 3.8.6おもむろに jupyter を起動
bash$ jupyter notebook
kernel が追加されているので
実行してバージョンを確認
3.8.6 で起動した jupyter の中で 3.6.12 の kernel を起動できました。
ちなみに、スクショ撮り忘れちゃったんですけど、
jupytersys.executableとかやると、ちゃんと venv 環境の python のパスが表示されます。
kernel が不要になったら
bash$ jupyter kernelspec uninstall hoge
っていう話なんですけど、まぁまぁややこしいので混乱しないように注意。
cf.
- 投稿日:2020-10-23T11:05:49+09:00
tkinterで超簡単にGUIアプリを作る
前置き
Pythonの人気が上昇してきたためか、Pythonの需要が最近ではとても高まっています。Pythonでは機械学習などの人工知能に関する技術が強いイメージですが、GUIのアプリを作ることも出来ます。
Pythonを始めたばかりでライブラリ?モジュール?という方向けの記事なので、中級者の方には物足りないかもしれませんが、読んでいただけると嬉しいです。ファイルの準備
tkinter-gui/
├ app.py
├ face.png上のようにapp.pyを作成して下さい。フォルダ名を自分はtkinter-guiとしましたが、何でも良いです。
face.pngは以下の画像を保存してapp.pyと同じ場所において下さい。コードを書く
app.py# Tkinterモジュールの読み込み import tkinter # ウィンドウの生成 root = tkinter.Tk() root.attributes("-topmost", True) root.minsize(width=200, height=200) # Frameウィジェットの生成 frame = tkinter.Frame(root, width=300, height=300, bg="black") frame.propagate(False) frame.pack() # テキストのLabelウィジェットの生成 label= tkinter.Label(frame, text="Hello! How are you?", fg="white", bg="black", font=("", 16)) label.pack() # 画像のLabelウィジェットの生成 import os png = tkinter.PhotoImage(file=os.path.dirname(__file__)+"/face.png") image = tkinter.Label(frame, image=png, bg="black") image.pack() # Entryウィジェットの生成 entry = tkinter.Entry(frame, width=20, bg="gray", fg="white") entry.insert(0, "happy") entry.pack() # クリックイベント関数 def show_text(): new_label = tkinter.Label(frame, text=entry.get(), fg="white", bg="black", font=("", 32)) new_label.pack() entry.destroy() button.destroy() # Buttonウィジェットの生成 button = tkinter.Button(frame, text="Say", bg="gray", fg="yellow", command=show_text) button.pack() # ウィジェットが配置されたウィンドウを表示 root.mainloop()上のコードをapp.pyに書いて下さい。
その後、 app.pyを実行すると以下のようなウィンドウが立ち上がります。トカゲがあなたの気分を聞いていますね。テキストボックスに入力した後にSayボタンを押して答えてあげましょう。
happyと答えることが出来ました。
最後に
アプリケーションに必要なのは、
- ユーザーからの入力をプログラムが受け取る
- 入力に対してプログラムが何らかの処理をする
- プログラムはユーザーに出力を返す
この流れです。
今回は、本当に必要最低限なアプリケーションとしての機能のみを実装しました。この上のコードを土台に色々と改変を施せば、電卓アプリなどを作ることも可能です。コードの1行1行が画面上に何を出力しているのかを理解し、自分だけのアプリケーションを作ってみましょう。
今回の記事の内容は、下のサイトでも詳しく説明しています。良かったら見て下さい。
- 投稿日:2020-10-23T10:41:14+09:00
デバッグ実行時にsudoを付ける方法
- 投稿日:2020-10-23T08:44:42+09:00
Python StatsD クライアントの構成
絶えず変化するソフトウェア開発環境で、拡張性の高い分散アプリケーションを構築して展開することは、道のりの半分に過ぎません。 残りの半分は、正確なメトリックを記録しながら、アプリケーションの状態とインスタンスを監視することです。
消費されているリソースの数や、特殊なプロセスによってアクセスされているファイルの数などを確認したい場合があります。これらのメトリックは、技術スタックの実行と管理に関する貴重な洞察を提供します。 これにより、設計したものの最終的なパフォーマンスを理解するためのレバレッジが得られ、最終的には最適化に役立ちます。
仕事を成し遂げるためのツールの大部分はすでにそこにありますが、今日はStatsDの詳細について話します。 独自のPythonStatsDクライアントをデプロイする方法、Pythonアプリケーションを監視するためにそれを使用する方法、そして最終的に記録されたメトリックをデータベースに保存する方法を説明させて頂きます。 その後、PrometheusまたはGraphiteを搭載したGrafanaダッシュボードにStatsDメトリックを表示したい場合は、 MetricFireのデモを予約し無料トライアルをゲットしましょう。 しかし、その前に、まずはStatsDから始めましょう!
StatsDとは何か?
StatsDは、統計とシステムパフォーマンスメトリックを収集してリッスンするnode.jsプロジェクトです。 これらの統計はネットワークを介して送信され、さまざまな種類のメトリックデータを収集できます。 StatsDを使用する主な利点は、Grafana、Graphite、InfluxDBなどの他のツールと簡単に統合できることです。
StatsDの長所
- 優れた起動時間
- パーセンタイルの処理はサーバーによって行われ、一度に複数のインスタンスの集約ビューを提供します(同じサービスに対して)。
- クライアント側のオーバーヘッドが比較的低くなります。
- 接続の問題を防ぐためにすべてのデータを送信するためにUDPを採用しています。
- 短命のアプリを構築するときに、シンプルで効果的かつ簡単に実装できます。
- メトリックは到着時にのみサーバーにプッシュされるため、メモリ使用率が低くなります。
しかし…メトリックが来ると「プッシュ」されるとはどういう意味でしょうか?
主に、メトリックレポートには2つの実行モデルがあります。 プルモデルでは、監視システムは特定のHTTPエンドポイントでアプリを「スクレイプ」します。 StatsDで使用されるプッシュモデルでは、アプリケーションはメトリックを監視システムに送信します。
前提条件とインストール
1.まず、Python3.6以降とpipをシステムにインストールする必要があります。
Linux、Windows、またはmacOSシステムで次のコマンドを実行することにより、Pythonのインストールを確認できます。
$ python --versionインストールされていない場合は、システムのこれらのインストール手順を確認してください。
- StatsD、Flask、Flask-StatsDが必要になり、フラスコ関連のメトリックが自動的に収集されます。 それに加えて、virtualenvが必要になります。これは分離されたPython環境を作成するためのツールであり、SQLALCHEMYはサンプルデータベースです。
pip install StatsD, flask, Flask-StatsD, virtualenv, Flask-SQLAlchemyPipは、これらのパッケージの最新バージョンを自動的にインストールします。
StatsDパッケージを使って遊んでみましょう
基本タイマーを実装することから始めます。
import statsd timer = statsd.Timer('Statsd_Tutorial_Application') timer.start() # we can do just about anything over here timer.stop('TutorialTimer')同様に、基本カウンターの場合:
import statsd counter = statsd.Counter('Statsd_Tutorial_Application') # again do anything random over here counter += 1Flaskによるモニタリング
このチュートリアルでは、Flaskで基本的なTo Doリストアプリケーションを設計し、操作メトリックを記録します。
完全なチュートリアルリポジトリは、Githubからフォークできます。
ステップ1:依存関係をインポートする-5〜12行目:
from flask import Flask, request from flask import render_template from flask import redirect from flask_sqlalchemy import SQLAlchemy import time import statsdステップ2:Flaskアプリ、StatsdクライアントおよびDBを起動します-14〜23行目:
c = statsd.StatsClient('localhost',8125) app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True db = SQLAlchemy(app)タスククラスを作成し、DBモデルで定義します-行26〜35:
class Task(db.Model): id = db.Column(db.Integer, primary_key=True) content = db.Column(db.Text) done = db.Column(db.Boolean, default=False) def __init__(self, content): self.content = content self.done = False db.create_all()
- 主キーとして整数を保持するDB列の変数IDを作成します。
- テキストのコンテンツ列を作成します。
- タスクが完了/解決されたかどうかを示すために、デフォルトがfalseのブール値のdone列を作成します。
- コンテンツと完了列を開始します。
- データベースを印刷可能な形式で返送してください。
- 新しいDBを作成して開始します。
ここで、タスクを追加します-行42-57:
@app.route('/task', methods=['POST']) def add_task(): start=time.time() content = request.form['content'] if not content: dur = (time.time() - start) *1000 c.timing("errortime",dur) c.incr("errorcount") return 'Error' task = Task(content) db.session.add(task) db.session.commit() dur = (time.time() - start) *1000 c.timing("tasktime",dur) c.incr("taskcount")このコードは、フォームから受け取ったタスクのコンテンツをPOSTリクエストに追加します。 ただし、ここで説明することがより重要なのは、追加されるメトリックレポートです。
- 機能が開始するとすぐに、基本タイマーが開始されます。
- コンテンツにエラーがある場合、エラーは基本カウンターでインクリメントされます。 同様に、エラーの時間が記録されます。 最終的に、エラーが返されます。
- DBがタスクを追加すると、関数が実行された完全な期間が計算され、インクリメンターが更新されます。 合計期間も更新されます。
タスクの削除-60行目から65行目:
@app.route('/delete/<int:task_id>') def delete_task(task_id): task = Task.query.get(task_id) db.session.delete(task) db.session.commit() c.incr("deletecount")上記のコードは、インクリメントのために削除カウントを基本カウンターに追加することにより、DBからのタスクの削除を実行します。
pyStatsDからMetricFireへのメトリックの送信
StatsDを使用してこれらのメトリックを記録するなどは、初心者には助かります。 ただし、より業界グレードの本番環境に対応する環境では、これらのメトリックは、グラフの保存と処理を容易にするサービスによって処理される必要があります。 これがGraphiteの出番です。
Graphiteの紹介:
Graphiteは、Webサイト、アプリケーション/その他のサービス、およびネットワークサーバーのパフォーマンスを追跡するために使用される監視ツールとして設計されています。 Graphiteは、テクノロジーの世界でセンセーションを巻き起こし、本質的に新世代の監視ツールに火をつけ、時系列データの保存と取得だけでなく、共有と視覚化もはるかに簡単にしました。
Graphiteは基本的に2つの操作を実行します。
- 数値の時系列データを保存する
- このデータのグラフをオンデマンドでレンダリングする
Graphiteは収集エージェントではないため、収集エージェントのように扱うべきではありません。むしろ、測定値を時系列DBに取り込むためのより簡単なパスを提供します。 サーバーまたはローカルマシンからすでに実行されているgraphiteインスタンスへのメトリックの送信をテストするには、次の1行のコマンドを実行します。
`$ echo "foo.bar 1 `date +%s`" | nc localhost 2003`インストールしたら、StatsDでメトリックをログに記録するだけで、ログに記録されたすべてのデータをGraphiteが取得します。 さて、Graphiteは大したことのようですが、開発者が解決したいと思っているGraphiteの特定のフォールバックがまだあります。 これがMetricFireの出番です。
なぜMetricFire:
- Graphite-as-a-Serviceを提供します
- 現在のGraphite版のギャップを埋めるために組み込みのエージェントを追加しました
- チームアカウントがコラボレーションに関する以前の問題を解決できるようにします
- カスタムの詳細なダッシュボード権限
- AWS、Herokuなどの他のサービスとの素晴らしい統合
- APIを介した操作は、開発を強化します
- ダッシュボードとユーザーデータの1時間ごとのバックアップ
- 必要に応じて簡単かつ迅速にスケーリング
- Graphiteモニタリングにおける確かな実績
- 経験豊富なエンジニアによる24時間年中無休のサポート
- プラグアンドプレイモデルで簡単に利用可能
しかし、それでも自己ホスト型および自己管理型のサービスを希望し、すべてを完全に制御したい場合は、StatsDとdockerを使用してgraphiteを起動するのが簡単な方法です。
デプロイメント
StatsDは、お好みのアーキテクチャやその他のサービス/マイクロサービスを使用して、お好みの環境にデプロイできます。 StatsDサーバーにメトリックを送信するすべてのクライアント側アプリがStatsDサーバーに到達可能であることを確認してください。そうすれば、StatsDはそれについて文句を言いません。
ジャストイン:AWS Cloudwatchは、インフラストラクチャのホスティングにAWSクラウドを採用している場合に備えて、StatsDメトリクスもサポートするようになりました。
私たちが蓄積したメトリックの視覚化に関する限り、Grafanaはその事実上の可視化ツールです。
自分で試してみたい場合は、デモにサインアップして、私たちはあなたに最適なgraphiteやPrometheusのモニタリングソリューションについて話すことができます。お気軽に、予約してください。
StatsD API
Python StatsDクライアントにもAPIが付属しており、それを統合したい場合はHTTPリクエストを介してトリガーできます。
参照