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

Pandasで列を抜き出すときはreindexを使った方がいいと思った話

要約 事前に列の存在をチェックしないでPandasのDataFrameから列を抜き出すのはやめよう. DataFrameから列を抜き出す際は, 事前に列の存在を確認してから抜き出す reindex関数を使って抜き出す のどちらかにしましょう. よくあるサンプル import numpy as np import pandas as pd df = pd.DataFrame( np.arange(12).reshape(3, 4), index=["X", "Y", "Z"], columns=["A", "B", "C", "D"] ) print(df) # A B C D # X 0 1 2 3 # Y 4 5 6 7 # Z 8 9 10 11 # AとB列だけ抜き出す df_AB = df[["A", "B"]] print(df_AB) # A B # X 0 1 # Y 4 5 # Z 8 9 # 存在しない列を抜き出そうとするとエラー df_PD = df[["P", "D"]] # KeyError: "['P'] not in index" 存在しない列をとろうとするとエラーなのはいいとして,pandasの関数を使うと,知らぬ間に列がなくなっていることがあります. sample_data.csv 日付,店舗,売上 2019-12-31,A,100 2019-12-31,B,200 2019-12-31,C,300 2020-01-01,A,100 2020-01-01,C,300 2020-01-02,A,100 2020-01-02,C,300 from datetime import datetime import pandas as pd # あるスーパーの店舗ごとの売り上げをCSVから読み取った例 df = pd.read_csv( "sample_data.csv", index_col=[0, 1], parse_dates=[0,] ) print(df) # 売上 # 日付 店舗 # 2019-12-31 A 100 # B 200 # C 300 # 2020-01-01 A 100 # C 300 # 2020-01-02 A 100 # C 300 # unstack関数を使って,店舗を列方向に並び替える df_unstack = df.unstack() print(df_unstack) # 売上 # 店舗 A B C # 日付 # 2019-12-31 100.0 200.0 300.0 # 2020-01-01 100.0 NaN 300.0 # 2020-01-02 100.0 NaN 300.0 # 店舗ごとの売り上げが日ごとに分けられて見やすくなった # 2020年のデータだけ抜き出して同じことをしてみると・・・ df_only_2020 = df.loc[df.index.get_level_values("日付").year == 2020] print(df_only_2020) # 売上 # 日付 店舗 # 2020-01-01 A 100 # C 300 # 2020-01-02 A 100 # C 300 df_only_2020_unstack = df_only_2020.unstack() print(df_only_2020_unstack) # 売上 # 店舗 A C # 日付 # 2020-01-01 100.0 300.0 # 2020-01-02 100.0 300.0 # 店舗Bのデータが列にないからこの状態で取り出そうとするとエラーになる なので DataFrameから列を抜き出すときは存在の確認をするかreindexを使いましょう. 個人的にはreindexがおすすめなのでreindexの場合の結果をチラっと. from datetime import datetime import pandas as pd # あるスーパーの店舗ごとの売り上げをCSVから読み取った例 df = pd.read_csv( "sample_data.csv", index_col=[0, 1], parse_dates=[0,] ) print(df) # 売上 # 日付 店舗 # 2019-12-31 A 100 # B 200 # C 300 # 2020-01-01 A 100 # C 300 # 2020-01-02 A 100 # C 300 # 2020年のデータだけ抜きだす df_only_2020 = df.loc[df.index.get_level_values("日付").year == 2020] print(df_only_2020) # 売上 # 日付 店舗 # 2020-01-01 A 100 # C 300 # 2020-01-02 A 100 # C 300 # 列方向に店舗を並べる df_only_2020_unstack = df_only_2020.unstack() print(df_only_2020_unstack) # 売上 # 店舗 A C # 日付 # 2020-01-01 100.0 300.0 # 2020-01-02 100.0 300.0 # 上記のデータから3店舗分のデータをreindexで取り出す df_only_2020_ABC = df_only_2020_unstack.reindex(columns=["A", "B", "C"]) print(df_only_2020_ABC) # 売上 # 店舗 A B C # 日付 # 2019-12-31 100.0 NaN 300.0 # 2020-01-01 100.0 NaN 300.0 # 2020-01-02 100.0 NaN 300.0 # 店舗BのデータはNaNで埋めてくれる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PyTorch】AttributeError: 'GeneralizedRCNNTransform' object has no attribute 'fixed_size'

TL;DR GPU環境で学習したモデルをtorch.saveで保存し、CPU環境でtorch.loadで読み出し推定を行うとエラーが発生する場合がる。 解決方法 torch.loadではなく各種学習モデルのクラスからインスタンス化してload_state_dictで学習したパラメータを読み出す。 詳細 [PyTorch] TORCHVISION OBJECT DETECTION FINETUNING TUTORIALを基に物体検出を試す 環境 CPU側 anaconda3 Python 3.8.10 pytorch 1.10.0-py3.8_cpu_0 torchvision 0.11.1-py38_cpu 原因 torch.save, torch.loadでモデルを保存・読み込みすると、__init__()経由でインスタンス化されずNoneのクラスメンバが未定義となってしまう。結果としてAttributeErrorで落ちる。 train.py import torch import torchvision from torchvision.models.detection.faster_rcnn import FastRCNNPredictor # モデルの定義 device = torch.device("cuda") model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=True) # get number of input features for the classifier in_features = model.roi_heads.box_predictor.cls_score.in_features # replace the pre-trained head with a new one model.roi_heads.box_predictor = FastRCNNPredictor(in_features, 2) # 学習処理 # ~中略~ # モデルの保存 torch.save(model, "path/to/model.pth") torch.save(model.state_dict(), "path/to/model_weight.pth") predict.py import torch from PIL import Image from torchvision.transforms import functional as F device = torch.device('cpu') model = torch.load("path/to/model.pth", map_location=device) model.to(device) model.eval() img = Image.open("path/to/image").convert("RGB") img = F.to_tensor(img) imgs = [img.to(device)] with torch.no_grad(): out = model(imgs) print(out[0]["boxes"].numpy()) 修正 predict.py - model = torch.load("path/to/model.pth", map_location=device) + model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=True) + in_features = model.roi_heads.box_predictor.cls_score.in_features + model.roi_heads.box_predictor = FastRCNNPredictor(in_features, 2) + model.load_state_dict(torch.load("path/to/model_weight.pth", map_location=device))
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

データ分析の長期インターンを1年間続けて学んだことポエム

はじめに 2021年の10月末日に、株式会社EXIDEAでデータ分析のエンジニアインターンを始めて1年が経ちました。この節目に自分自身が経験したことをアウトプットしたいと思います! データ分析系のインターンをお探しの方、もしくは他企業のインターンでは何をやっているのか興味がある理系の方などの参考になれば嬉しいです。 インターンを始めてから触るようになった技術 数ヶ月前に、インターン先の分析開発チームで技術スタックを作成しました。 インフラ周りで書ききれないものがいくつかありますが、使う頻度が多いのは大体こんな感じです。 以降では社内で経験したことと、個人で経験したことの2つの視点で、クォーターごとの振り返りをしていきたいと思います! 社内で経験したこと 1Q ログを見たこともなければ、ネットワーク等の基礎知識も0の状態で入社した僕が最初に行った業務はログ分析でした。 自社開発のツールから取得できるアクセスログを用いて、どの機能がどれくらい使われているかを分析可視化する業務をメインで行なっていました。以下に経験したことを列挙してみます。 社内で経験したこと Linuxコマンド ログの整形 ログの分析 ステータスコード、GET/POSTの違い、静的動的ファイルの扱いの理解 分析の可視化 手順書の作成 ETL処理 BIツールの設定 Pythonistaなプログラム設計 感想 分析業務を一通り体験してみたのが1Qでした。 データ分析基盤とはどういうものなのかとか、手順書の重要性とか、可読性のあるコードの意義とか、新卒エンジニアが1年目に知るようなことを体験し始めた感じな気がします! とはいえこのクォーターでは、社内のエンジニアが使っている言葉は5%くらいしか理解できず。 知らない言葉を検索すると新しい知らない言葉が出てくるという、無限ループと永遠遊んでいました。 インターンのおかげで平日休みが消えたので、生活にメリハリが出たりもしました! 2Q この時期から、社内業務と個人タスクをそれぞれ持ちながら業務に取り組みました。 社内業務 ビジネスサイドとの連携が必要な案件や、自社開発ツールの機能改善を担当していました。 他のインターン生が行っていた業務を引き継ぐこともあったので、ここで初めて人が書いたコードをガッツリ読む機会がありました。 また、新規案件の分析要件を決める経験などもしました。 個人タスク Webアプリケーション開発で必要な知識や経験を網羅的に習得するため、 0→1でFlaskアプリを作ってみるのが個人タスクになりました。 元々SNSコミュニケーションを分析することに興味があったので、LINEの個人チャットスクリプトを自然言語分析するアプリケーションを作ることにしました。 3Q 春から夏にかけて、大学の対面が少しずつ始まってしまったせいで、あっぷあっぷしながら働いていました。笑 社内業務 新規ツールのアジャイル分析 レポート作成の自動化 社内全体会議の動画分析 個人タスク Flaskアプリの完成 詳細 特にこのクォーターは、アジャイル分析で好き放題やっていた内容の動線整理をしたり、ドキュメンテーション作業が多かったり、複数の案件を巻いたり。新しいことというよりは、今までの業務をまとめるような仕事が多かったです! レポート作成ではopenpyxlのライブラリをフル使用して、PythonでExcelを長い時間触っていました。 動線整理では、confulence上でこーゆーの作ってました。 社長の動画分析は、AWSのTranscribeというサービスを用いて、 AIによる動画の自動文字起こし→自然言語分析 を行いました。 4Q このクォーターは、振り返ってみるといろんな案件に首を突っ込んでいるような状態でした。 社内業務 運用手順書作成 分析設計書の作成 社内運用ツールの社員傾向分析 顧客データ分析 → 展開 見出し自動作成の研究開発 インプションデッキの作成(技術スタックもここ) Jinja2を用いたExcelファイルのテンプレート作成 ETL処理Again 他インターン生のレビュー 個人タスク 基本情報技術者試験とった 詳細 そろそろ1年目ということもあったのか、分析の与える影響範囲が広がったり、案件に関わる人数や種類が増えてきました。 また、研究開発というものに初めて挑戦して、先行研究調べるために論文漁るという、学部2年生ではなかなかできない?ような経験もしました。 ビジネスサイドへ資料展開することもあり、プレゼンで使ってはいけない言葉も勉強しました。(FBで突っ込んでいただきました) 最後に インターンの1年間を通して、確かにできることは多くなりましたが、逆に自分ができることの少なさを痛感する1年でもありました。 今後もエンジニア業界にどっぷり浸かって、より学生らしからぬ働きをしていきたいなと思っています。 ご精読ありがとうございました!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WindowsでTensorflowがGPUを認識しないとき

前提 Windowsにて、pipで入れたtensorflowがGPUを認識してくれない。 CUDA、CUDNNはダウンロードしてpathを通してある状態。 原因 Windows Storeから入手したPythonでは、TensorflowがGPUを上手く認識してくれない模様。 [重要] しかもWindowsが勝手に設定したエイリアスで、pythonと打つとStore版を使うようにしてくるので、それをoffにする必要あり。 解決法 「アプリと機能」から「アプリ実行エイリアス」を開く。 画像の真ん中です。 PythonとPython3がMicrosoft Store版のPythonに紐づけられているので、オフにする。 画像の状態にしてください。 Python公式 (python.org) からTensorflowが対応しているPythonを入れる。 Note: 記事執筆時ではTensorflow2.7はPython3.10をサポートしていないので、3.9を入れました。 PythonはWindows embeddable package と Windows installerがありますが、理由がないならWindows installerにした方が良いです。 最後に 感想や指摘などありましたら、是非コメントください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

前処理

前提 データ分析する際のメモ用と記事を作成しました。 データセットは、Cancerデータを使用しました。 from sklearn.datasets import load_breast_cancer from sklearn.model_selection import train_test_split from sklearn.preprocessing import MinMaxScaler cancer=load_breast_cancer() x_train,x_test,y_train,y_test=train_test_split(cancer.data,cancer.target,random_state=1) 前処理の種類 Standard Scaler 個々の特徴量を以下のように変換 平均:0 分散:1 from sklearn.preprocessing import StandardScaler scaler=StandardScaler() x_train=scaler.transform(x_train) RobustScaler 個々の特徴量を以下のように変換 平均:中央値 分散:四分位数 from sklearn.prerpocessing import RobustScaler scaler=RobustScaler() x_train=scaler.transform(x_train) MinMaxScaler データを0〜1の間に入るように変換 from sklearn.preprocessing import MinMaxScaler scaler=MinMaxScaler() x_train=scaler.transform(x_train)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ETL処理を用いて、デイトレードの取引データを加工→蓄積する流れのまとめ

概要 データ分析のインターンのおかげでETL処理を組むことができるようになりました。 今回はこの技術を用いて、為替のデイトレードで得た取引履歴の、データ加工と蓄積の流れを構築したいと思います。 できるようになること AWSでETL処理が書けるようになる データストレージの使い方が少しわかる 補足 今回はGlueやLambdaを用いない、データ分析の半自動化を目指しています。とはいえ、スクリプトさえできてしまえば自動化も簡単なので別記事でその方法を書こうと思います。 利用サービス AWS S3 Amazon SageMaker HighLow Australia (証券会社) ETL処理フロー データフローの全体像はこちらです。 S3のDataLakeというバケットに溜まった取引履歴をデータ整形して、DataWarehouseに蓄積します。(HighLow側にAPIがないので、DataLakeへのデータ蓄積は手動アップロードにします。) ETL処理の前準備 ETL(抽出、変換、読み込み)処理を行う前に、そもそも抽出するデータがストレージに蓄積されていないといけません。本来のAWSのS3活用方法とは異なりますが、今回は気にせずデータのアップロードを手動で行います。 データ取得 HighLowオーストラリアという証券会社の、個人的な取引履歴を利用します。 写真の様に、[検索する]から数字の順番に取引履歴取得範囲を設定し、[ダウンロード]でCSV形式でダウンロードします。 取引データの配布等は行っていませんのでご了承ください。 分析頻度は週単位想定しているので、とりあえず1ヵ月を4週間に分けて4つのCSVファイルを取得しました。 S3へデータアップロード AWSのS3では、 - datalake-trading - datawarehouse-trading というバケットを用意しておいてください。 ここの、datalake-tradingというバケットに、[trading-history/]というプレフィックスを切り、その中に先ほど取得したCSVファイルをアップロードします。 SageMakerでETL処理を書く データの準備ができたことで、いよいよデータ中身を見ながら整形していきます。 コーディング環境の作成 AWSのSageMakerを開いて、ノートブックインスタンスを作成してください。 個人利用なので、インスタンスタイプはデフォルト最小の[ml.t2.medium]で十分だと思います。 ここからは作成したインスタンス上で、JupyterLabを利用してコーディングしていきます。 S3のデータを受け取るpythonスクリプト JupyterLab上で、Python3を書いていきます。 ライブラリダウンロード データ整形する対象のファイル名をS3から取得 対象のファイルをS3から取得しデータフレームとして読み込み この流れでスクリプトを書きます。 なお、CSVファイルには日本語が含まれているので、encoding='Shift-jis"という引数を与えないと、データフレームとして読み込むことができませんので注意が必要です。 import pandas import boto3 import io s3_client = boto3.client('s3') data_bucket_name='datalake-trading' obj_list=s3_client.list_objects(Bucket=data_bucket_name) file=[] for contents in obj_list['Contents']: if "csv" in contents['Key']: file.append(contents['Key']) else: print("Not CSV:", contents['Key']) file_data=file[-1] # 最新ファイルの取得 print("Target CSV:", file_data) response = s3_client.get_object(Bucket=data_bucket_name, Key=file_data) response_body = response["Body"].read() temp_df = pandas.read_csv(io.BytesIO(response_body), header=0, delimiter=",", low_memory=False,encoding="shift-jis") スクリプトは今後の自動化を想定して、最新ファイルのデータ整形を行う仕様にしています。過去データをすべて取得する場合は、データフレームを週ごとに読み込んで結合する処理が別途必要です。 ちなみにs3へのアクセスモジュールはこちらを参考にしました。 データフレームを見る データ分析の第一歩です。生データを目視して、分析に必要そうなデータを新たに作成したり、逆に不必要なデータを取り除いたりします。 個人的に分析したい内容は、 証拠金増減の時系列グラフ 各通貨の勝率 手法別での勝率 High-Low別の勝率 です。もっと見たいところはあるけど一旦はこんな感じにします。 なので、これを満たすようなデータフレームを作成するところが目標です。 データ整形のpythonスクリプト 特徴量を作っていきます。 # 列名を英語表記に変える temp_df.rename(columns={'取引番号':"trading_number", '取引原資産':'currency', 'オプションの種類':'option_type', '方向':'HighLow', ' 取引内容 ':'entry_point', 'ステータス ':'states', '購入':'Bet_amount', 'ペイアウト ':'payout', '判定レート ':'end_point', ' 取引時間 ':'entry_start', ' 終了時刻 ':'entry_end'}, inplace=True) temp_df['Bet_amount'] = [int(Bet_amount.replace("¥", "").replace(",", "")) for Bet_amount in temp_df['Bet_amount']] temp_df['payout'] = [int(payout.replace("---", "0")) if payout=="---" else int(payout.replace("¥", "").replace(",", "")) for payout in temp_df['payout'] ] temp_df = temp_df.sort_values(['trading_number']).reset_index(drop=True) #日付の昇順に並び替え # 1. 証拠金増減の時系列グラフに必要なデータ temp_df['deposit'] = 0 for i in range(len(temp_df)): mergin = temp_df['payout'][i] - temp_df['Bet_amount'][i] if i != 0: temp_df['deposit'][i] = temp_df['deposit'][i-1] + mergin else: temp_df['deposit'][i] = 0 # 2. 各通貨の手法別の利益を出すのに必要なデータ temp_df['total_check'] = [int(p-b) if p>b else p for (b, p) in zip(temp_df['Bet_amount'], temp_df['payout'])] # 3. 勝率計算に必要なデータ temp_df['win_lose'] = ["win" if check!=0 else "lose" for check in temp_df['total_check']] # 4. 平均獲得Pipsを計算するのに必要なデータ temp_df['pips'] = [(e - s) if e>s else (s-e) for (e,s) in zip(temp_df['end_point'], temp_df['entry_point'])] datawarehouseへデータを送る ファイル名を指定して、datawarehouseに整形後のCSVファイルをアップロードします。 # 取引期間の取得 start_date = temp_df["entry_start"][0].split(" ")[0].replace("/", "-") last_date = temp_df["entry_end"][len(temp_df)-1].split(" ")[0].replace("/", "-") # 保存するファイル名 file_name = start_date+"_"+last_date+".csv" # csvファイルの作成 temp_df.to_csv(file_name) # datawarehouseアップロード s3 = boto3.resource('s3') upload_bucket = s3.Bucket(upload_bucket_name) upload_bucket.upload_file(file_name, file_name) 上記を実行し、S3:datawarehouse-tradingを確認します。 確かに、アップロードされていることを確認しました。 自動化する場合は、この記事で書いたコードを、AWS Glueに張り付けるだけです。 次回は、Glueで自動化 → BIツールで可視化する流れを書こうと思います。 最後に Sagamakerのインスタンスは稼働時間に対してお金がかかります。 使わない時はインスタンスを停止しておくよう注意しましょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

作成したスクレイピングツールをHTTPで呼び出したかった

前回までのあらすじ 前回の記事 メルカリのスクレイピングツールはできたけど、 ローカル環境でコマンド実行するだけなのはちょっともったいない。 なので今回は、対象URLをURLパラメータとして付与して、 ブラウザからWebサーバ(Apache)にリクエストを投げたら 実行結果が表示されるように改修とか環境構築とかやってみたよ。 やらなきゃいけないこと サーバの用意 初期設定 firewalld、SELinuxの無効化 諸々インストール Python3 google-chrome Apache(httpd) 資材配置 httpd.confの確認、必要に応じて修正 作成資材を規定の場所に配置 実行結果確認 ツール修正 ローカルPCから接続確認 ※セキュリティ的な点は全く考えてないので、必要に応じて対策してね。 1.環境構築 サーバはVMWare Workstation上に、CentOS8を用意。 初期ユーザとかrootパスワードとか作って firewalldとSELinuxを無効化して準備完了。 ちょっと久々すぎてココを忘れてしばらく詰まった…( ˘ω˘ ) 2.諸々インストール yumリポジトリをアップグレードしたら、環境整備。 実行環境はpython Version3.x、 Apacheで待ち受けてリクエストが来たらchromedriver使って実行する感じで。 Apacheならば、特に何の設定もなくPythonツールを動かせるので便利。 ChromeってCLI環境のLinuxでも入るんですね~… 諸々インストールしたりサービス起動したり # yum update ↓必要なパッケージのインストール # yum install python3 # yum install google-chrome # yum install httpd ↓スタートアップ起動有効化したりサービス起動したり。 # systemctl enable httpd # systemctl start httpd 3. 資材配置 作成したツールを実行するにはcgi-binディレクトリ配下で そのツールが実行権限を持ってる必要があるので、 サーバにアップロードしたら、所定の場所に配置して権限変更。 デフォルトのcgi-binディレクトリはconf/httpd.confを参照 よほど変な入れ方していなければ大丈夫だろうけど無ければ追記。 conf/httpd.conf <IfModule alias_module> ScriptAlias /cgi-bin/ "/var/www/cgi-bin/" </IfModule> 資材配置 # mv <作成ツール> /var/www/cgi-bin/ # chmod 755 /var/www/cgi-bin/<作成ツール> ツールの配置場所と同じところにchromedriverも一緒に置いておく。 4. 実行結果確認 まずは、Linux環境上でちゃんと動くことを確認しておく とりあえず確認 # python3 /var/www/cgi-bin/<作成ツール> <スクレイピング対象URL> 何も考えなくてもとりあえずは動くはず。 しかしブラウザからは↑のようなコマンドは叩けないので、 URLパラメータから取得する仕組みを組み込んであげる必要がある。 作成ツールの上から変更点 # ~↓リクエストを受けたときPythonで実行するように指定~ #!/usr/bin/env python3 # ~↓cgiをインポート~ import cgi import cgitb cgitb.enable() # ~↓文字コード注意~ print("Content-Type: text/html; charset=UTF-8\n\n") print("") def getMercari(argURL): # ~↓chromedriverはツールと同じところに配置しておく。違うところに置く場合はパスに注意して指定する。~ browser = webdriver.Chrome('./chromedriver',options=options) # ~getMercari関数終わり。特に変更点っぽいところはない~ def main(): # ~主な変更点:URLパラメータからスクレイピング対象URLを取得~ argURL = cgi.FieldStorage() getMercari(argURL=argURL["url"].value) print("done!") if __name__ =='__main__': main() main関数において、FieldStorage()関数を使って URLパラメータ(URLの?以降に並んでいる文字列)を取得している。 参考:cgi --- CGI (ゲートウェイインターフェース規格) のサポート この関数によって取得される値は下記URL例でそのままリクエストすると、 変なところでぶつ切りにされてしまう。 ※別の検証用コードを使って実行結果を確認しておく。 URLパラメータ確認(とりあえずそのまま突っ込む) http://<サーバIP>/cgi-bin/<作成ツール>?url=https://jp.mercari.com/search?status=on_sale&page=1&t1_category_id=5&category_id=5 ↓ FieldStorage(None, None, [MiniFieldStorage('url', 'https://jp.mercari.com/search?status=on_sale'), MiniFieldStorage('page', '1'), MiniFieldStorage('t1_category_id', '5'), MiniFieldStorage('category_id', '5')]) Key-Valueでつながってるから、単純にURLパラメータとして取得して それぞればらばらに処理したいならなんか大丈夫そうだけど、 文字列自体をそのまま使いたい今回の趣旨にはちょっとそぐわない。 そこでURLエンコード。今回はそこを自動化する必要はないので Google先生のおチカラを借りることにして、サクっと変換する。 参考:URLエンコード・デコード 日本語URLを扱うときには便利どころか、 アドレスバーでよく使われる記号も変換した文字列として返してくれるので便利。 そこも頼ることができないシチュエーションだとすると… 一覧から取得してエンコードして実行も自動化…みたいなシーンなら 別途調べる必要がありそうなので、次回Updateするなら今度はその辺ですかね…? URLパラメータ確認(エンコード実施) http://<サーバIP>/cgi-bin/<作成ツール>?url=https%3A%2F%2Fjp.mercari.com%2Fsearch%3Fstatus%3Don_sale%26page%3D1%26t1_category_id%3D5%26category_id%3D5 ↓ FieldStorage(None, None, [MiniFieldStorage('url', 'https://jp.mercari.com/search?status=on_sale&page=1&t1_category_id=5&category_id=5')]) Key-Valueのセットが一つになった! …でもって、次の行のスクレイピング関数にそいつを引数として渡せば あとは実行結果の一覧がブラウザに表示される。 ↑のルールに従って変換できていればCurlでも 呼べるっぽいからとりあえず大丈夫そうですかね。 そんなこんなで、今回のところはこれにて。 今まで点々とばらけていた知識が線になっていくところがなんだか楽しいですね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonデータ分析試験に合格したので勉強方法を残しておく

はじめに Pythonエンジニア認定基礎試験に合格したのでデータ分析試験も受けてみました。 Python 3 データ分析試験 初めに簡単な試験概要を公式サイトより引用します。 試験名称:Python 3 エンジニア認定データ分析試験 (英名:Python 3 Certified Data Analyst Examination) 資格名:Python3 エンジニア認定データ分析試験合格者 (英名:Python 3 Data Analyst Certification) 概要:Pythonを使ったデータ分析の基礎や方法を問う試験 問題数:40問(すべて選択問題) 合格ライン:正答率70% 試験はCBT方式です。 試験時間は60分ありますが、20分程度で終わりました。 といっても早く解いたから何かボーナスがあるわけじゃないので、見直し等じっくり時間をかけても良いと思います。 合格ラインは正答率70%です。 問題数は40問なので、28問正解すれば合格です。 言い換えれば12問は間違っていても大丈夫なのです。 勉強について 参考にした書籍 試験の公式サイトによると、主教材として以下の書籍が紹介されています。 翔泳社「Pythonによるあたらしいデータ分析の教科書」 模擬や実際の問題の中には上記の書籍に記載されている文章ベースの問が幾つか出題されます。 例)「〇〇について以下の選択肢の中から正しいもの/誤っているものを選べ」など。 書籍としてはPython文法といった基礎的な内容から記載されており、私含めた初心者に優しい本となっています。 また、現在Pythonエンジニア育成推進協会にて、データ分析試験の主教材である「Pythonによるあたらしいデータ分析の教科書」がもらえるキャンペーンをやっています。 TwitterなどのSNSで受験宣言を投稿する。 Pythonエンジニア認定基礎試験/データ分析試験 のどちらでも可。 投稿のURLや住所などをフォームに入力して応募する。 本が届く! 実際に私も応募して頂いた書籍で勉強し受験しました。 2021年11月末日まで、つまりあと数日なので、受験する予定のある方はご利用してみてはいかがでしょうか。 試験対策 では実際に試験対策した内容について紹介します。 とはいっても、前回のエンジニア分析試験とほぼほぼ同様にひたすら模擬試験を解く事を繰り返すという方法になります。 1~2週間ほどで教科書を読む。 私の場合は実務であまり用いていなかった記述やライブラリの使い方について実際にコードを書いたりしました。 2週間くらい以下を繰り返す。 模擬試験を解く 間違った問題を見直す という事でした。 間違った問題を重点的に見直すことで、効率的に得点を取ることができます。 何回か模擬試験を解いてみて、これはイケるなと思ってからテストセンターの予約を行いました。 模擬試験 Pythonエンジニア認定基礎試験の模擬試験としては2021/11現在、私の知る限り以下3サイトで公開されています。 PRIME STUDY 模擬試験が3試験分用意されています。 最初にメールアドレスを入力します。この入力したメールアドレスに結果が送信されます。 DIVE INTO EXAMの問題よりも問題内容のコードが複雑だった印象があります。 DIVE INTO EXAM 会員登録が必要です。 模擬試験を受けるだけであれば無料会員で十分です。有料会員だと講座?が受けられるらしい? 回答結果の履歴が保存される機能があります。 試験を受ける度にランダムで出題される点がおすすめ。 問題内容はPRIME STUDYよりも簡単め。 ごく一部ですが教科書で触れていないような内容も出題されたような気がする。 トレノケート 会員登録が必要です。 今回、私は使用しませんでした。 エンジニア認定試験は上記2サイトだけで事済んだのと、たに会員登録する事が億劫だったので。。。 https://camp.trainocate.co.jp/map/python/ 勉強しておくと良いポイント 模擬試験および本試験の内容からここら辺を勉強しておくと良いポイントを紹介します。 といっても、試験の公式ページを確認すると教科書の章節をベースに以下の通り出題範囲と割合が公開されています。 上記の模擬試験についても同様の範囲と割合で出題されています。なので模擬試験を繰り返し行うだけでも重点的に勉強したほうがよいポイントなどは身につくのかなと思います。 出題の割合に注目しますと4章のライブラリによる分析実践の内容が非常に配分が大きいです。 なので、勉強時間が不足している場合などに重点的に勉強するとしたらココなのかなと思います。 終わりに 上でも書いた通り10問くらいは間違ってても合格できます。なので、 普段から業務でPythonを使用してデータ分析系の業務をされている方でしたら、あまり力を入れずとも合格できるのではないかなぁと思いました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

リクルートからA3RTのドメイン変更メールが来たのでline-botを修正する

はじめに 今年の8月末にa3rtからメールが、、 A3RTご利用中の皆様 いつもご利用いただきありがとうございます。 引き続き旧ドメインを利用中の皆様に改めてのお知らせとなります。 1. ドメイン変更に伴うアプリ修正依頼 弊社組織変更に伴い、ドメインが変更となります。  旧ドメイン)a3rt.recruit-tech.co.jp  新ドメイン)a3rt.recruit.co.jp 2021年9月30日までは併用期間として両方使える状態ですが、 2021年10月1日以降、旧ドメインは廃止、新ドメインのみ利用可能となる予定です。 お手数ですが、ブックマーク、APIコール時のドメイン変更対応をお願いします。 なお、Image Influence, SQL Suggestの2サービスについては、旧ドメインのみ対応とし、新ドメインでの提供はいたしません。 2. Image Influence, SQL Suggest機能提供終了 上記機能は、2021年9月30日の旧ドメイン停止をもって提供を終了させていただきます。 以上、2点となります。 サービス側都合での修正依頼・提供終了となり申し訳ございませんが、ご対応のほどよろしくお願いいたします。 特に気にしていませんでしたが、10月から雑談line-botが応答しなくなったので、修正しました。 (毎朝のチャットは機能してますね) ちなみに毎朝、推しからメッセージを受け取る方法はこちらをご覧ください。 ソースコードの修正 修正前 from flask import Flask,request,abort from linebot import LineBotApi,WebhookHandler from linebot.exceptions import InvalidSignatureError from linebot.models import MessageEvent,TextMessage,TextSendMessage import os import requests import pprint # import pya3rt app=Flask(__name__) #環境変数の取得 YOUR_CHANNEL_ACCESS_TOKEN = os.environ["YOUR_CHANNEL_ACCESS_TOKEN"] YOUR_CHANNEL_SECRET = os.environ["YOUR_CHANNEL_SECRET"] line_bot_api=LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN) handler=WebhookHandler(YOUR_CHANNEL_SECRET) @app.route("/bot/webhook",methods=["POST"]) def callback(): signature=request.headers["X-Line-Signature"] body=request.get_data(as_text=True) app.logger.info("Request body"+body) try: handler.handle(body,signature) except InvalidSignatureError: abort(400) return "OK" @handler.add(MessageEvent,message=TextMessage) def handle_message(event): #入力された文字列を格納 push_text = event.message.text #A3RTのTalkAPIにより応答 reply_text = talkapi_response(push_text) #リプライ部分の記述 line_bot_api.reply_message(event.reply_token,TextSendMessage(text=reply_text)) #A3RTのTalkAPIにより応答 def talkapi_response(text): apikey = os.environ["apikey"] data = {'apikey': apikey,'query': text} client = pya3rt.TalkClient(apikey) response = client.talk(text) return ((response['results'])[0])['reply'] if __name__=="__main__": port=int(os.getenv("PORT",5000)) app.run(host="0.0.0.0",port=port) 修正後 from flask import Flask,request,abort from linebot import LineBotApi,WebhookHandler from linebot.exceptions import InvalidSignatureError from linebot.models import MessageEvent,TextMessage,TextSendMessage import os import requests import pprint app=Flask(__name__) #Herokuの環境変数取得 YOUR_CHANNEL_ACCESS_TOKEN = os.environ["YOUR_CHANNEL_ACCESS_TOKEN"] YOUR_CHANNEL_SECRET = os.environ["YOUR_CHANNEL_SECRET"] line_bot_api=LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN) handler=WebhookHandler(YOUR_CHANNEL_SECRET) @app.route("/bot/webhook",methods=["POST"]) def callback(): signature=request.headers["X-Line-Signature"] body=request.get_data(as_text=True) app.logger.info("Request body"+body) try: handler.handle(body,signature) except InvalidSignatureError: abort(400) return "OK" @handler.add(MessageEvent,message=TextMessage) def handle_message(event): #入力された文字列を格納 push_text = event.message.text #A3RTのTalkAPIにより応答 reply_text = talkapi_response(push_text) #リプライ部分の記述 line_bot_api.reply_message(event.reply_token,TextSendMessage(text=reply_text)) #A3RTのTalkAPIにより応答 def talkapi_response(text): A3RT_Talk_api = 'https://api.a3rt.recruit.co.jp/talk/v1/smalltalk' apikey = os.environ["apikey"] data = {'apikey': apikey,'query': text} response = requests.post(A3RT_Talk_api, data = data) return response.json()['results'][0]['reply'] if __name__=="__main__": port=int(os.getenv("PORT",5000)) app.run(host="0.0.0.0",port=port) 修正箇所 #A3RTのTalkAPIにより応答 def talkapi_response(text): A3RT_Talk_api = 'https://api.a3rt.recruit.co.jp/talk/v1/smalltalk' apikey = os.environ["apikey"] data = {'apikey': apikey,'query': text} response = requests.post(A3RT_Talk_api, data = data) return response.json()['results'][0]['reply'] 要は、py3rtが使用できなくなったようです。 公式サイトのサンプルリクエストを参考にコードを修正しています。 Herokuへデプロイ Herokuは、アプリケーションを実行するための環境を提供してくれます。 事前にCLIをダウンロードする必要があります。 $ heroku login $ heroku git:clone -a sample-name $ cd sample-name $ git add . $ git commit -am "Improved" $ git push heroku master ちなみにgithubへpushしたことをトリガーにherokuへ自動デプロイすることも可能です。 Herokuコンソールへログインし、アプリケーションのデプロイタブより設定可能です。 (参考)Herokuに環境変数を設定する方法 $ heroku login $ heroku config #環境変数一覧の取得 $ heroku config:set 環境変数名=値  #環境変数の設定 コンソールからも設定可能です。 この方の記事が大変わかりやすかったです。 雑談botの修正完了 やっぱ精度低いですね〜〜。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

推しからおはようメッセージを受け取るBOTを作ったよ

はじめに python初学者です。 楽しくpythonを学習できる方法はないかと模索していると、bot開発が楽しそうなので試してみました。 どうせなら推しから毎朝、自動メッセージが送信されるように作成してみました! 実行環境 python : 3.8 github actions 今回の環境は、自前でサーバを用意することなくサーバレスに実行できる構成です。 ファイル info.json main.py この2つだけ! ソースコード main.py import json from linebot import LineBotApi from linebot.models import TextSendMessage file = open('info.json', 'r') info = json.load(file) CHANNEL_ACCESS_TOKEN = info['CHANNEL_ACCESS_TOKEN'] line_bot_api = LineBotApi(CHANNEL_ACCESS_TOKEN) def main(): USER_ID = info['USER_ID'] messages = TextSendMessage(text="おはよう〜 みなみだよ❤︎ \n今日も1日頑張ろうね!") line_bot_api.push_message(USER_ID,messages=messages) if __name__ == "__main__": main() info.json { "CHANNEL_ACCESS_TOKEN": "~~~~~~~~~", "USER_ID": "~~~~~~~~" } info.jsonに記載されている変数をmain.pyから参照しています。 Line-Developperでチャンネルを作成する方法は、こちら。 チャンネル作成後に、 "CHANNEL_ACCESS_TOKEN"は、「Messaging API settings」タブから "USER_ID"は、「Basic settings」タブからそれぞれ取得できます。 githubへpush $ git init $ git remote add origin https://~~~.git $ git add . $ git commit -am "first commit" $ git push origin maseter github action の設定 Actionsから設定を行います 後続の作業とyamlファイルの記述方法は、下記youtubeを参考にさせていただきました。 実際のyamlファイルはこんな感じ name: hello_chatbot on: # push: schedule: - cron: '55 20 * * *' jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python 3.8 uses: actions/setup-python@v1 with: python-version: 3.8 - name: Install dependencies run: | python -m pip install --upgrade pip pip install line-bot-sdk - name: Run script run: | # main.pyの実行 python main.py github actionsのcronの時刻はUTCらしいので、起動させたい時刻からマイナス9した値を設定する必要があります。 私の場合は、毎朝6時にメッセージを受け取りたいので、AM6:00から-9してPM9:00に設定していたのですが、 ビルドに時間がかかるのでさらに5分早めに起動させています。 schedule: - cron: '55 20 * * *' 完成 最高やないかい!!!(キモいとか言わないで。)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TouchDesignerでOpenCVを使った簡単なロゴ検出について

Hello!!!! What's up everybody guys!! ということで あまたのTouchdesignerユーザーが集うこのアドカレのTOPバッターを務めさせていただくことに感謝を申し上げます。 完全にタイトルが2年前の1日目の TouchDesignerでOpenCVを使った簡単な顔認識について に寄せていますが、これも何かの縁ですのでOpenCVでのロゴ検出についてお話していきます 今回の制作物 buttonを押すと、スタバのロゴ検出して、そこに !LOGO! がついてきますね。 今回は0からこれを作っていきます。 チュートリアル まず初めに、この記事には間違っていることやよりいい方法があるかもしれません。その際はこそっと教えてください! 個人的に、文字で説明するよりYoutubeでチュートリアルをやることが多くそっちの方がわかりやすいので、動画を作っています! ぜひこちらからご覧ください(少し前にqiitaでyoutubeを埋め込みできるようになりました) 内容もりもりで少し長くなっちゃった(てへ) 要点解説 動画の内容を大事なところに絞って解説していきます。 今回使うロゴ 今回はこちらのスターバックスのロゴを使います ネットで拾ってきたやつですが ただ個人として使うので、勝手に使ったねーはい!逮捕!とか言わないでええ project file そしてこちらのロゴの写真も入ったproject fileがgithubに上がっていますので 動画の概要欄のリンクからぜひダウンロードしてくださいまし 基本セットアップ video device in -> ロゴを見つけるTOP movie file in -> ロゴ参照 それぞれnullにつなげます。 そして検知する方を train 元のロゴを query と名付けます。 script SOP -> 結果確認用 「matching_result」 button COMP -> ロゴ検出スタート chop execute DAT Off To Onで使います。そしてここにコードを書いていきます! 全コード 少し説明することが多いので、まず全コードを公開します! その後pointごとに解説します! chopexec1 import numpy import cv2 def onOffToOn(channel, sampleIndex, val, prev): trainOp = op('train') queryOp = op('query') for i in range(2): train = trainOp.numpyArray(delayed = True) query = queryOp.numpyArray(delayed = True) input_list = [] ratio = 0.65 for item in [train, query]: item_255 = item * 255 item_uint8 = item_255.astype('uint8') input_list.append(item_uint8) train = input_list[0] query = input_list[1] #feature detect detector = cv2.ORB_create() kp_query, des_query = detector.detectAndCompute(query,None) kp_train, des_train = detector.detectAndCompute(train,None) dst_query = cv2.drawKeypoints(query, kp_query, None) #matcher matcher = cv2.BFMatcher(cv2.NORM_HAMMING) try: #matching matches = matcher.knnMatch(des_query, des_train, k=2) #ratio test good_matches = [] for q, t in matches: if q.distance < t.distance * ratio: good_matches.append(q) matches = good_matches num_goodmatches = len(matches) op('good_matches').par.value0 = num_goodmatches if num_goodmatches > 8: x_train_pt = [kp_train[m.trainIdx].pt[0] - (trainOp.width/2) for m in matches] y_train_pt = [kp_train[m.trainIdx].pt[1] - (trainOp.height/2) for m in matches] x_mean = sum(x_train_pt) / len(x_train_pt) y_mean = sum(y_train_pt) / len(y_train_pt) op('text_logo').par.fontalpha = 1 op('transform_logo').par.tx = x_mean op('transform_logo').par.ty = y_mean else: op('text_logo').par.fontalpha = 0 #draw_matching draw_params = dict(matchColor = (0, 255, 0), singlePointColor = (255, 0, 0), flags = cv2.DrawMatchesFlags_DEFAULT) matching_result = cv2.drawMatches(query, kp_query, train, kp_train, matches, None, **draw_params) op('matching_result').copyNumpyArray(matching_result) except cv2.error: op('matching_result').copyNumpyArray(dst_query) return def whileOn(channel, sampleIndex, val, prev): return def onOnToOff(channel, sampleIndex, val, prev): return def whileOff(channel, sampleIndex, val, prev): return def onValueChange(channel, sampleIndex, val, prev): return そして残りのノードも全公開 ロゴ検出までの一連の流れ さあロゴ検出において、OpenCV使うことは予想できていると思います。 じゃあ具体的にどうやんの?? これですよね。 ちょーざっくり言うと、 1.TOPをnumpyに変える 2.train, queryそれぞれの特徴点を検出する 3.それぞれの特徴点をマッチングさせる 4.マッチングした特徴点が多ければ~、ロゴだよね!! って感じ! 1.TOPをnumpyに変える これ嘘かと思うかもしれないんですが、 TOPってnumpyなんです TOPって!numpyなんです! ごり押ししてすみません あ、numpyってなに?って方のために NumPyはベクトルや行列の計算を高速に処理するためのライブラリ です! そしてTOPはnumpyの配列になっているので、OpenCVとかのライブラリが使えるわけですね! 今回で言うとここのコードでTOPをnumpyに変換 chopexec1 for i in range(2): train = trainOp.numpyArray(delayed = True) query = queryOp.numpyArray(delayed = True) numpyの配列(matching_result)をTOPに変換 chopexec1 op('matching_result').copyNumpyArray(matching_result) ちょっとした注意ポイント numpyArray()だけだと、呼び出すタイミングによってはうまくnumpyに変換することができないことがあります。 それを防ぐのが(delayed = True)です。 これでうまくいった! と思いがちなんですが、(delayed = True)にすると、 前の処理の結果が返ってきます!! これは困る!!!!! ってことでfor文で2回処理を呼んであげてるわけですね! 詳しくはTOP classのdocumentにも記載があるので是非!! 2.train, queryそれぞれの特徴点を検出する 特徴点を検出するには、検出器が必要になります。 フリーザーの戦闘力が知りたい!!ってなったらスカウターが必要ですよね。 そのスカウター自体が検出器 そんなイメージです 今回はORBというアルゴリズムで検出器を作ります chopexec1 detector = cv2.ORB_create() そして実際に検出する! chopexec1 kp_query, des_query = detector.detectAndCompute(query,None) kp_train, des_train = detector.detectAndCompute(train,None) 思ったよりシンプル。 返り値は、 keypoint -> 座標とかが入ってる descriptor -> マッチングの際に使う って感じ! 検出された特徴点を確認したいときは、 chopexec1 dst_query = cv2.drawKeypoints(query, kp_query, None) 3.それぞれの特徴点をマッチングさせる 特徴点同士をマッチングさせるときには、マッチング器が必要になります。 これはレアスニーカー鑑定士と一緒ですね。そのスニーカーの特徴が本物と同じであれば、それはレアものとして間違いない!! となるわけです。 今回はBFMatcherというものを使います! chopexec1 matcher = cv2.BFMatcher(cv2.NORM_HAMMING) これまたシンプル! そしたら、今回のメインディッシュ! 特徴点マッチング!! chopexec1 #matching matches = matcher.knnMatch(des_query, des_train, k=2) #ratio test good_matches = [] for q, t in matches: if q.distance < t.distance * ratio: good_matches.append(q) matches = good_matches matcher.knnMatch(des_query, des_train, k=2) はknnMatchといって それぞれをマッチングさせて上位k個の特徴点を探します そして信頼度の低い結果を除くため ratio testというものを行います! ratioの値が1に近ければ、甘くなって、0に近いほど厳しくなります。 で最終的に、matchesにマッチングできた特徴点のリストが入っている! というわけでございます! 4.マッチングした特徴点が多ければ~、ロゴだよね!! さあこのmatches中身が多ければ多いほどマッチング率が高いので それはロゴ!! とみなすことができます。 今回はその閾値を8としてそれ以上len(matches)があれば検出! ということでした。 chopexec1 num_goodmatches = len(matches) op('good_matches').par.value0 = num_goodmatches if num_goodmatches > 8: 特徴点の位置 特徴点の位置、ようはピクセルがどこか知りたいときありますよね! それは、keypointのptというアトリビュートに格納されています。 例えば、indexが1の特徴点の位置は、 kp_train[1].pt -> (x,y) です。 今回では、特徴点がいくつかあるので平均をその位置として、 値がxは0~1280、yは0~720で入ってくるので それをTouchdesigner用に、 x -640~640 y -360~360 に変換しています。 transform_logoのTranslateをPixelsに変えるのをお忘れなく!! そのほかは動画にて だいたいのポイントはこちらにて解説しました! が、例外処理やその他細かいところは動画にてご覧ください! 今回のロゴ検出以外にも、L-systemやHokuyoなどTouchdesigner初心者向けにチュートリアルいくつか出してるので 是非ご覧いただければ嬉しい限りです! 僕の思いとしては、少しでもTouchdesignerを触る人が増えて業界全体を盛り上げることができたらいいな! と思って、動画や記事を作っていますの今後も皆さんと一緒に歩みたい! ちょっと最後熱くなってしまいましたが、最後まで読んでいただきありがとうございます! それでは~~~~
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

マイクラプログラミング(統合版)いろいろやってみた[Scripting API/Functions/WebSocket/MakeCode]

概要 この記事は KLab Engineer Advent Calendar 2021の10日目の記事です。 こんにちはhamasan05です。 昨年に引き続きマイクラ(統合版)の記事です。 今年はマイクラでなんでもいいからゲームを一つ作ってみるということで作ってみました。 マイクラ(統合版)には複数のプログラミング可能な環境が用意されており 同じゲームを複数のやり方で実装することにチャレンジしてみました。 どんなゲームを作ったか マイクラのウィザーという裏ボスを出現させないようにするゲームです。 ウィザーには出現条件があり、その出現条件を満たさないように立ち回り 出現させてしまった人が負け、責任をもってウィザーを倒すという内容になります。 動画 実際に遊ぶとこのような感じになります。 ※ゲームの元ネタです。 機能の紹介 必要な機能は下記の二つだけにしました 1. ステージのリセット 2. 必要なアイテムの付与 ステージのリセット 一定の範囲に特定のブロックを配置する 出現条件をランダムにするために、特定のブロックの配置をランダムにする 必要なアイテムの付与 装備や消耗品の付与 ゲームの進行に必要な配置可能なブロックの付与 1. Scripting APIと呼ばれているものを使った実装 公式を見てもわからなかったのでいろいろネットをさまよって たどり着いたのが下記でした https://wiki.bedrock.dev/scripting/scripting-intro.html Windows10で動くスクリプトということで試してみました。 server.js const systemServer = server.registerSystem(0, 0) systemServer.initialize = function () { // (中略) // ボタンを押したときのイベント登録 this.listenForEvent('minecraft:block_interacted_with', (e) => this.onInteracted(e)); }; // ボタンを押したときの挙動 systemServer.onInteracted = function (e) { x = e.data.block_position.x, y = e.data.block_position.y, z = e.data.block_position.z // アイテムを付与するボタン if (x === 0 && y === 5 && z === 0) { this.initUser(); } // ステージをリセットするボタン if (x === 0 && y === 5 && z === 1) { this.initGame(); } }; // 必要なアイテムの付与 const playerInitCommands = [ // ゲームの進行に必要な配置可能なブロックの付与 '/give @p skull 128 1 {"minecraft:can_place_on":{"blocks":["soul_sand"]}}', // 装備や消耗品の付与 "/give @p netherite_sword 1", "/give @p bow 1", "/give @p enchanted_golden_apple 64", "/give @p netherite_helmet 1", "/give @p netherite_chestplate 1", "/give @p netherite_leggings 1", "/give @p netherite_boots 1", "/give @p arrow 64", ] systemServer.initUser = function () { playerInitCommands.forEach(function(x) { systemServer.executeCommand(x, (e) => {}); }); }; // ステージのリセット const gameInitCommands = [ "/fill 20 6 0 30 6 10 soul_sand", "/fill 20 7 0 30 7 10 air", "/fill 20 5 0 30 5 10 air", ] systemServer.initGame = function () { // 出現条件をランダムにするために、特定のブロックの配置をランダムにする var x = Math.floor(Math.random() * 11) + 20; var z = Math.floor(Math.random() * 11); command = `/fill ${x} 5 ${z} ${x} 5 ${z} soul_sand`; gameInitCommands.forEach(function(x) { systemServer.executeCommand(x, (e) => {}); }); systemServer.executeCommand(command, (e) => {}); }; Github 普段JavaScriptを書かない私ですが、これならいろいろ実現できそうだという感触が得られました。 一番のデメリットはWindows10でしか動かないというところと感じました。 ※サーバ側でスクリプトを動かせば、クライアントはモバイル端末でもよい模様です。 2. Functionsで実装 Functionsはゲーム内で使えるコマンドをひとまとめにしてまとめて実行する機能です。 これを使っても実装が可能ということが分かったので試してみました ステージのリセット fill 4 5 13 -5 7 22 air fill 4 6 13 -5 6 22 soul_sand scoreboard objectives add x dummy scoreboard players random @e[name=random] x 1 100 execute @e[name=random,scores={x=1}] ~ ~ ~ fill -5 5 13 -5 5 13 soul_sand execute @e[name=random,scores={x=2}] ~ ~ ~ fill -5 5 14 -5 5 14 soul_sand execute @e[name=random,scores={x=3}] ~ ~ ~ fill -5 5 15 -5 5 15 soul_sand execute @e[name=random,scores={x=4}] ~ ~ ~ fill -5 5 16 -5 5 16 soul_sand execute @e[name=random,scores={x=5}] ~ ~ ~ fill -5 5 17 -5 5 17 soul_sand execute @e[name=random,scores={x=6}] ~ ~ ~ fill -5 5 18 -5 5 18 soul_sand execute @e[name=random,scores={x=7}] ~ ~ ~ fill -5 5 19 -5 5 19 soul_sand execute @e[name=random,scores={x=8}] ~ ~ ~ fill -5 5 20 -5 5 20 soul_sand execute @e[name=random,scores={x=9}] ~ ~ ~ fill -5 5 21 -5 5 21 soul_sand execute @e[name=random,scores={x=10}] ~ ~ ~ fill -5 5 22 -5 5 22 soul_sand execute @e[name=random,scores={x=11}] ~ ~ ~ fill -4 5 13 -4 5 13 soul_sand execute @e[name=random,scores={x=12}] ~ ~ ~ fill -4 5 14 -4 5 14 soul_sand execute @e[name=random,scores={x=13}] ~ ~ ~ fill -4 5 15 -4 5 15 soul_sand execute @e[name=random,scores={x=14}] ~ ~ ~ fill -4 5 16 -4 5 16 soul_sand execute @e[name=random,scores={x=15}] ~ ~ ~ fill -4 5 17 -4 5 17 soul_sand execute @e[name=random,scores={x=16}] ~ ~ ~ fill -4 5 18 -4 5 18 soul_sand execute @e[name=random,scores={x=17}] ~ ~ ~ fill -4 5 19 -4 5 19 soul_sand execute @e[name=random,scores={x=18}] ~ ~ ~ fill -4 5 20 -4 5 20 soul_sand execute @e[name=random,scores={x=19}] ~ ~ ~ fill -4 5 21 -4 5 21 soul_sand execute @e[name=random,scores={x=20}] ~ ~ ~ fill -4 5 22 -4 5 22 soul_sand execute @e[name=random,scores={x=21}] ~ ~ ~ fill -3 5 13 -3 5 13 soul_sand execute @e[name=random,scores={x=22}] ~ ~ ~ fill -3 5 14 -3 5 14 soul_sand execute @e[name=random,scores={x=23}] ~ ~ ~ fill -3 5 15 -3 5 15 soul_sand execute @e[name=random,scores={x=24}] ~ ~ ~ fill -3 5 16 -3 5 16 soul_sand execute @e[name=random,scores={x=25}] ~ ~ ~ fill -3 5 17 -3 5 17 soul_sand execute @e[name=random,scores={x=26}] ~ ~ ~ fill -3 5 18 -3 5 18 soul_sand execute @e[name=random,scores={x=27}] ~ ~ ~ fill -3 5 19 -3 5 19 soul_sand execute @e[name=random,scores={x=28}] ~ ~ ~ fill -3 5 20 -3 5 20 soul_sand execute @e[name=random,scores={x=29}] ~ ~ ~ fill -3 5 21 -3 5 21 soul_sand execute @e[name=random,scores={x=30}] ~ ~ ~ fill -3 5 22 -3 5 22 soul_sand execute @e[name=random,scores={x=31}] ~ ~ ~ fill -1 5 13 -1 5 13 soul_sand execute @e[name=random,scores={x=32}] ~ ~ ~ fill -1 5 14 -1 5 14 soul_sand execute @e[name=random,scores={x=33}] ~ ~ ~ fill -1 5 15 -1 5 15 soul_sand execute @e[name=random,scores={x=34}] ~ ~ ~ fill -1 5 16 -1 5 16 soul_sand execute @e[name=random,scores={x=35}] ~ ~ ~ fill -1 5 17 -1 5 17 soul_sand execute @e[name=random,scores={x=36}] ~ ~ ~ fill -1 5 18 -1 5 18 soul_sand execute @e[name=random,scores={x=37}] ~ ~ ~ fill -1 5 19 -1 5 19 soul_sand execute @e[name=random,scores={x=38}] ~ ~ ~ fill -1 5 20 -1 5 20 soul_sand execute @e[name=random,scores={x=39}] ~ ~ ~ fill -1 5 21 -1 5 21 soul_sand execute @e[name=random,scores={x=40}] ~ ~ ~ fill -1 5 22 -1 5 22 soul_sand execute @e[name=random,scores={x=41}] ~ ~ ~ fill 0 5 13 0 5 13 soul_sand execute @e[name=random,scores={x=42}] ~ ~ ~ fill 0 5 14 0 5 14 soul_sand execute @e[name=random,scores={x=43}] ~ ~ ~ fill 0 5 15 0 5 15 soul_sand execute @e[name=random,scores={x=44}] ~ ~ ~ fill 0 5 16 0 5 16 soul_sand execute @e[name=random,scores={x=45}] ~ ~ ~ fill 0 5 17 0 5 17 soul_sand execute @e[name=random,scores={x=46}] ~ ~ ~ fill 0 5 18 0 5 18 soul_sand execute @e[name=random,scores={x=47}] ~ ~ ~ fill 0 5 19 0 5 19 soul_sand execute @e[name=random,scores={x=48}] ~ ~ ~ fill 0 5 20 0 5 20 soul_sand execute @e[name=random,scores={x=49}] ~ ~ ~ fill 0 5 21 0 5 21 soul_sand execute @e[name=random,scores={x=40}] ~ ~ ~ fill 0 5 22 0 5 22 soul_sand execute @e[name=random,scores={x=51}] ~ ~ ~ fill 1 5 13 1 5 13 soul_sand execute @e[name=random,scores={x=52}] ~ ~ ~ fill 1 5 14 1 5 14 soul_sand execute @e[name=random,scores={x=53}] ~ ~ ~ fill 1 5 15 1 5 15 soul_sand execute @e[name=random,scores={x=54}] ~ ~ ~ fill 1 5 16 1 5 16 soul_sand execute @e[name=random,scores={x=55}] ~ ~ ~ fill 1 5 17 1 5 17 soul_sand execute @e[name=random,scores={x=56}] ~ ~ ~ fill 1 5 18 1 5 18 soul_sand execute @e[name=random,scores={x=57}] ~ ~ ~ fill 1 5 19 1 5 19 soul_sand execute @e[name=random,scores={x=58}] ~ ~ ~ fill 1 5 20 1 5 20 soul_sand execute @e[name=random,scores={x=59}] ~ ~ ~ fill 1 5 21 1 5 21 soul_sand execute @e[name=random,scores={x=60}] ~ ~ ~ fill 1 5 22 1 5 22 soul_sand execute @e[name=random,scores={x=61}] ~ ~ ~ fill 2 5 13 2 5 13 soul_sand execute @e[name=random,scores={x=62}] ~ ~ ~ fill 2 5 14 2 5 14 soul_sand execute @e[name=random,scores={x=63}] ~ ~ ~ fill 2 5 15 2 5 15 soul_sand execute @e[name=random,scores={x=64}] ~ ~ ~ fill 2 5 16 2 5 16 soul_sand execute @e[name=random,scores={x=65}] ~ ~ ~ fill 2 5 17 2 5 17 soul_sand execute @e[name=random,scores={x=66}] ~ ~ ~ fill 2 5 18 2 5 18 soul_sand execute @e[name=random,scores={x=67}] ~ ~ ~ fill 2 5 19 2 5 19 soul_sand execute @e[name=random,scores={x=68}] ~ ~ ~ fill 2 5 20 2 5 20 soul_sand execute @e[name=random,scores={x=69}] ~ ~ ~ fill 2 5 21 2 5 21 soul_sand execute @e[name=random,scores={x=70}] ~ ~ ~ fill 2 5 22 2 5 22 soul_sand execute @e[name=random,scores={x=71}] ~ ~ ~ fill 3 5 13 3 5 13 soul_sand execute @e[name=random,scores={x=72}] ~ ~ ~ fill 3 5 14 3 5 14 soul_sand execute @e[name=random,scores={x=73}] ~ ~ ~ fill 3 5 15 3 5 15 soul_sand execute @e[name=random,scores={x=74}] ~ ~ ~ fill 3 5 16 3 5 16 soul_sand execute @e[name=random,scores={x=75}] ~ ~ ~ fill 3 5 17 3 5 17 soul_sand execute @e[name=random,scores={x=76}] ~ ~ ~ fill 3 5 18 3 5 18 soul_sand execute @e[name=random,scores={x=77}] ~ ~ ~ fill 3 5 19 3 5 19 soul_sand execute @e[name=random,scores={x=78}] ~ ~ ~ fill 3 5 20 3 5 20 soul_sand execute @e[name=random,scores={x=79}] ~ ~ ~ fill 3 5 21 3 5 21 soul_sand execute @e[name=random,scores={x=80}] ~ ~ ~ fill 3 5 22 3 5 22 soul_sand execute @e[name=random,scores={x=81}] ~ ~ ~ fill 4 5 13 4 5 13 soul_sand execute @e[name=random,scores={x=82}] ~ ~ ~ fill 4 5 14 4 5 14 soul_sand execute @e[name=random,scores={x=83}] ~ ~ ~ fill 4 5 15 4 5 15 soul_sand execute @e[name=random,scores={x=84}] ~ ~ ~ fill 4 5 16 4 5 16 soul_sand execute @e[name=random,scores={x=85}] ~ ~ ~ fill 4 5 17 4 5 17 soul_sand execute @e[name=random,scores={x=86}] ~ ~ ~ fill 4 5 18 4 5 18 soul_sand execute @e[name=random,scores={x=87}] ~ ~ ~ fill 4 5 19 4 5 19 soul_sand execute @e[name=random,scores={x=88}] ~ ~ ~ fill 4 5 20 4 5 20 soul_sand execute @e[name=random,scores={x=89}] ~ ~ ~ fill 4 5 21 4 5 21 soul_sand execute @e[name=random,scores={x=90}] ~ ~ ~ fill 4 5 22 4 5 22 soul_sand execute @e[name=random,scores={x=91}] ~ ~ ~ fill -2 5 13 -2 5 13 soul_sand execute @e[name=random,scores={x=92}] ~ ~ ~ fill -2 5 14 -2 5 14 soul_sand execute @e[name=random,scores={x=93}] ~ ~ ~ fill -2 5 15 -2 5 15 soul_sand execute @e[name=random,scores={x=94}] ~ ~ ~ fill -2 5 16 -2 5 16 soul_sand execute @e[name=random,scores={x=95}] ~ ~ ~ fill -2 5 17 -2 5 17 soul_sand execute @e[name=random,scores={x=96}] ~ ~ ~ fill -2 5 18 -2 5 18 soul_sand execute @e[name=random,scores={x=97}] ~ ~ ~ fill -2 5 19 -2 5 19 soul_sand execute @e[name=random,scores={x=98}] ~ ~ ~ fill -2 5 20 -2 5 20 soul_sand execute @e[name=random,scores={x=99}] ~ ~ ~ fill -2 5 21 -2 5 21 soul_sand execute @e[name=random,scores={x=100}] ~ ~ ~ fill -2 5 22 -2 5 22 soul_sand Github 「出現条件をランダムにするために、特定のブロックの配置をランダムにする」 を実現するために記述がかなり増えてしまいました。 変数の扱いに制限があって気楽にプログラミングするという感じではなかったです。 必要なアイテムの付与 give @a[c=1] skull 128 1 {"minecraft:can_place_on":{"blocks":["soul_sand"]}} give @a[c=1] netherite_sword 1 give @a[c=1] bow 1 give @a[c=1] enchanted_golden_apple 64 give @a[c=1] netherite_helmet 1 give @a[c=1] netherite_chestplate 1 give @a[c=1] netherite_leggings 1 give @a[c=1] netherite_boots 1 give @a[c=1] arrow 64 Github こちらはほぼほぼJavaScriptと変わらない記述量でした。 ターゲットをどうするかということろが少しほかの方法で実装したときと異なる感じとなりました。 3. WebSocket Serverで実装(言語はPython) 普段Pythonを使って業務しているので何とかPythonで自由に書けないかといろいろ調べてみました。 WebSocketサーバを作ってクライアントからつなぐことによってプログラミングできることが分かったので WebSocketサーバをPythonで書いてみることにしました。 下記の記事を参考にしてみました http://www.s-anand.net/blog/programming-minecraft-with-websockets/ main.py import asyncio import json import random from uuid import uuid4 import websockets EVENTS = [ "PlayerMessage", ] async def mineproxy(websocket, path): print("Connected") msg = { "header": { "version": 1, "requestId": "", "messageType": "commandRequest", "messagePurpose": "subscribe", }, "body": {"eventName": "PlayerMessage"}, } await websocket.send(json.dumps(msg)) async def send(cmd): await websocket.send( json.dumps( { "header": { "version": 1, "requestId": str(uuid4()), "messagePurpose": "commandRequest", "messageType": "commandRequest", }, "body": { "version": 1, "commandLine": cmd, "origin": {"type": "player"}, }, } ) ) async def stage_reset(): # 一定の範囲に特定のブロックを配置する await send("/fill -5 5 13 4 7 22 air") await send("/fill -5 6 13 4 6 22 soul_sand") # 出現条件をランダムにするために、特定のブロックの配置をランダムにする x = random.randint(1, 10) z = random.randint(1, 10) await send(f"/setblock {-5 + x} 5 {13 + z} soul_sand") async def user_init(name: str): cmds = [ # ゲームの進行に必要な配置可能なブロックの付与 '/give {} skull 128 1 {"minecraft:can_place_on":{"blocks":["soul_sand"]}}', # 装備や消耗品の付与 "/give {} netherite_sword 1", "/give {} bow 1", "/give {} enchanted_golden_apple 64", "/give {} netherite_helmet 1", "/give {} netherite_chestplate 1", "/give {} netherite_leggings 1", "/give {} netherite_boots 1", "/give {} arrow 64", ] for cmd in cmds: await send(cmd.format(name)) try: async for msg in websocket: print(msg) msg = json.loads(msg) if msg["body"].get("eventName", "") == "PlayerMessage": text = msg["body"]["properties"]["Message"] if text == "stage-reset": await stage_reset() elif text == "user-init": player_name = msg["body"]["properties"].get("Sender") await user_init(player_name) except websockets.ConnectionClosedError: print("Disconnected") start_server = websockets.serve(mineproxy, port=19131) print("/connect localhost:19131") asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever() 実装してみたところ直接的にゲーム内のボタンに割り当てることが難しかったので コマンドとして実装する形に落ち着きました。 WebSocketを制御する部分を作り込む必要があるのでコード量はその分増えましたが かなり自由度が高いと感じました。 4. MakeCodeで実装(Python) 最後に試したのがMakeCodeでした。 こちらは教育目的と思って後回しにしていたのですが、 教育目的だけあってハードルの低さが際立ちました。 バックエンド仕組みとしては3. WebSocketを使っています。 (むしろこちらのためにある機能のようです) プログラミングの方法としてブロックプログラミング、JavaScript、Pythonを選択可能です。 私はもちろんPythonを選択しました。 main.py def stage_reset(): # 一定の範囲に特定のブロックを配置する blocks.fill(AIR, world(-5, 5, 13), world(4, 7, 22)) blocks.fill(SOUL_SAND, world(-5, 6, 13), world(4, 6, 22)) # 出現条件をランダムにするために、特定のブロックの配置をランダムにする x = randint(1, 10) -5 z = randint(1, 10) + 13 blocks.place(SOUL_SAND, world(x, 5, z)) def user_init(): # ゲームの進行に必要な配置可能なブロックの付与 player.execute('give @s skull 128 1 {"minecraft:can_place_on":{"blocks":["soul_sand"]}}') # 装備や消耗品の付与 player.execute("give @s netherite_sword 1") player.execute("give @s bow 1") player.execute("give @s netherite_helmet 1") player.execute("give @s netherite_chestplate 1") player.execute("give @s netherite_leggings 1") player.execute("give @s netherite_boots 1") player.execute("give @s arrow 64") player.on_chat("stage-reset", stage_reset) player.on_chat("user-init", user_init) Github 実装してみて感じたのはWebSocketの制御部分がすべて隠蔽されていて マイクラを操作するための関数+標準的なPythonの関数が準備されているため やりたいことに対して直感的に実装できるということでした。 注意点としてMakeCodeを使ったプログラムを動かすためには Minecraft Windows10版またはMinecraft Education Edition(iPadOS/macOS/ChromeOS/Windows10)が必要ということです。 Minecraft Windows10版ではCode Connection for Minecraftが必要になります。 これがWebSocketサーバとして動作するためほかのOSでは動かないということになります。 あくまで「プログラミングを学ぶ」用途なので「ゲーム作りをする」のには向かないといえます。 最後に 全部で4種類の方法でゲームを実装してみることを試してみましたが、 まだまだ簡単にできることしかできなかったので、来年は調べたことをもとに もう少し凝ったものが作れるといいなと考えています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DeepL APIを利用した翻訳ハンズオン

 はじめに 前回に引き続き『APIって、どんな動きをしているの?』と構築を交えながらAPIについて学習していきたいと思いたち自分自身の2番煎じ投稿です。下記参考資料を読みながら手を動かしてAPIを構築しました  参考資料 参考リンクにはAPIのHTTPリクエスト・レスポンスなどの記述の方法などの説明があり、API分からなかったコチラを読めばOKかと どちらもハンズオンを進める中で大変理解が捗りました APIの仕組みが分かる・使いこなせる人材になれる記事(Pythonコード付き) HTTPとPOSTとGET YahooAPIを利用した天気予報ハンズオン  構成図  ハンズオン 0:「DeepL」でアプリケーションIDを発行する 無料で登録するを押下する 今回はAPIの学習なので無料版を選択する アカウント情報の入力 丁寧にSimulatorもあるので挙動を確認することができます こちらでAPIでGETする際のURLが簡単に確認できます ターミナルに直打ちでも確認することができます tetutetu214@mbp 20211127_lang_change % curl "https://api-free.deepl.com/v2/translate?auth_key=XXXXXXXXXX&target_lang=ja&tag_handling=0" {"translations":[{"detected_source_language":"EN","text":"このフォームを使って、DeepL APIのサンプルGETおよびPOSTリクエストを生成することができます。翻訳したいテキストを入力して、言語を選択するだけです。サンプルリクエストは、変更するたびに自動的に再生成されます。対応するAPIコールの結果を見るには、「Send」をクリックします。"}]}% 1:上記の内容を踏まえてコードを記述していく(最後に、まとめたコードの記述あり) 今回はSimulatorで作成した英語から日本語ではなくて、日本語を英語に翻訳させるものなので、Lang = "&target_lang=en-US"としています。 こちらもSimulatorを確認すれば、自分の設定したい言語に変更すうることが可能です # ライブラリのインポート import json import requests # ターミナルに出力する文章 print("英文にしたい日本語を記述ください") Line = input() # エンドポイント送付内容 Deepl_Endpoint = "https://api-free.deepl.com/v2/translate?" Key = "auth_key=XXXXXXXXXX&" Text = "text=" Lang = "&target_lang=en-US" url = Deepl_Endpoint + Key + Text + Line + Lang 取得できるjsonの必要な部分を取得していきます # 取得した情報の整形 url_info = requests.get(url) obj = json.loads(url_info.text) # 最終的に出力する内容 print(obj["translations"][0]["text"]) 先ほどSimulatorで出力された内容が下記の画像のように返されてくるので、必要部である["translations"]の1つ目[0]の内容の["text"]をターミナルに出力するように記述 json図解 まとめたコードの記述(情報を取得表示を優先しています) # ライブラリのインポート import json import requests # ターミナルに出力する文章 print("英文にしたい日本語を記述ください") Line = input() # エンドポイント送付内容 Deepl_Endpoint = "https://api-free.deepl.com/v2/translate?" Key = "auth_key=XXXXXXXXXX&" Text = "text=" Lang = "&target_lang=en-US" url = Deepl_Endpoint + Key + Text + Line + Lang # 取得した情報の整形 url_info = requests.get(url) obj = json.loads(url_info.text) # 最終的に出力する内容 print(obj["translations"][0]["text"]) ターミナルでの画面表示 tetutetu214@mbp 20211127_lang_change % python lang_change.py 英文にしたい日本語を記述ください コーヒをおかわりしたいです I'd like a refill on my coffee. 2:「DeepL」での確認画面 自分のアカウントの『ご利用状況』を確認すると、現在使用している文字数を確認することが可能です  さいごに 前回記事と品を変えただけ感がものすごいですが、とりあえずLets`ハンズオン! こうして学んでいくと、次は画像認識APIから文章を読み取ってDeepL翻訳してみたりなど身の丈にあわせた学習アウトプットを粛々と続けていきたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PipとCondaをごちゃごちゃにしないように!

例えば Jupyter lab, Jupyternotebook を初期設定する時。anacondaで環境設定して pip install〜とでもすると路頭に迷います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【図解解説】JOI2021-2022 一次予選 第2回 問題2 短針

図解解説シリーズ 競技プログラミングを始めたばかりでAtCoderの解説やJOIの解説ではいまいちピンと来ない…という人向けに、図解を用いて解説を行います。 問題文 情報オリンピック日本委員会に掲載されている問題 AtCoderに掲載されている問題 入出力など実際に確認して自分の作成したプログラムを採点することができます。 図解解説 今年度の一次予選のB問題は、以下の3つのスキルを確認する問題になっています。 1.入力・出力を正しく利用できる 2.算術演算子を正しく利用できる 3.条件分岐(if)を正しく利用できる 問題文を整理するために、具体的な数字を用いて、図示して考えてみます。 入力例1のA=9時を使って、B時間後に何時になるか表を作成してみます。すると、1~12の数字が周期的に出てくることを確認することができます。 そこで、表の中にA+Bの計算結果を入れてみます。12時間ごとに変わるので、A+Bを12で割った余りを書き加えてみます。すると、余りが0のとき時刻は一致しませんが、それ以外の時刻は一致することが確認できます。そこで、A+Bを12で割った余りが0のときは答えが12、それ以外のときは答えがA+Bを12で割った余りを表示するようにプログラムを作成します。 場合分けをせずに答えを出す方法を考えてみます。1時~12時の周期が、12で割った余りの0~11の周期と一致するように計算を調整します。そのためにA+B-1を計算し、この値(A+B-1)を12で割った余りを求めると周期が一致します。余りに1を加えると1~12となり、1時~12時の値と一致します。 解答例 余りが0のときとそれ以外のときで場合分けをしたプログラム b1.py A = int(input()) B = int(input()) #A時のB時間後が何時になるか計算を行う #A+Bを計算し12で割った余りが0のときは、12時 #A+Bを計算し12で割った余りが0でないときは、余り if (A+B)%12==0: print(12) else: print((A+B)%12) 採点サイトに提出したプログラム 余りを調整して、if文を使用せずに作成したプログラム b2.py A = int(input()) B = int(input()) #A時のB時間後が何時になるか計算を行う #A+B-1を計算し12で割った余りに1を加えたのが答え print((A+B-1)%12+1) 採点サイトに提出したプログラム イラスト スライド内で使用しているイラストはすべて「いらすとや」の素材を利用しています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Numpyの整数と浮動小数点数の最大値と最小値

numpy.iinfo()で整数,numpy.finfo()で浮動小数点数の情報を得る. >>> import numpy as np >>> np.iinfo(np.int64) iinfo(min=-9223372036854775808, max=9223372036854775807, dtype=int64) >>> np.iinfo(np.int32) iinfo(min=-2147483648, max=2147483647, dtype=int32) >>> np.iinfo(np.int16) iinfo(min=-32768, max=32767, dtype=int16) >>> np.iinfo(np.int8) iinfo(min=-128, max=127, dtype=int8) >>> np.finfo(np.float128) finfo(resolution=1.0000000000000000715e-18, min=-1.189731495357231765e+4932, max=1.189731495357231765e+4932, dtype=float128) >>> np.finfo(np.float64) finfo(resolution=1e-15, min=-1.7976931348623157e+308, max=1.7976931348623157e+308, dtype=float64) >>> np.finfo(np.float32) finfo(resolution=1e-06, min=-3.4028235e+38, max=3.4028235e+38, dtype=float32) >>> np.finfo(np.float16) finfo(resolution=0.001, min=-6.55040e+04, max=6.55040e+04, dtype=float16) 簡単な四則演算ではfloat系ではfloat32が早く,int系は割り算が遅いらしい. 参考【python】numpyの型の違いによる計算速度差を見てみる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【図解解説】JOI2021-2022 一次予選 第2回 問題1 立方体

図解解説シリーズ 競技プログラミングを始めたばかりでAtCoderの解説やJOIの解説ではいまいちピンと来ない…という人向けに、図解を用いて解説を行います。 問題文 情報オリンピック日本委員会に掲載されている問題 AtCoderに掲載されている問題 入出力など実際に確認して自分の作成したプログラムを採点することができます。 図解解説 今年度の一次予選のA問題は、以下の2つのスキルを確認する問題になっています。 1.入力・出力を正しく利用できる 2.算術演算子を正しく利用できる 立方体の体積を計算する問題です。計算方法についてはいくつかの方法があります。 掛け算で計算: X*X*X 〇乗で計算: X**3 または pow(X,3) どの表現方法で解答しても問題ありません。 解答例 a1.py X = int(input()) print(X*X*X) 採点サイトに提出したプログラム a2.py X = int(input()) print(X**3) 採点サイトに提出したプログラム a3.py X = int(input()) print(pow(X,3)) 採点サイトに提出したプログラム イラスト スライド内で使用しているイラストはすべて「いらすとや」の素材を利用しています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【python】メンドクサイを自動にさせてみた3【SlackBot編】

この記事について 最近iOSアプリで20円ばかり儲けた凄腕プログラマー(自称)がサラリーマンしている仕事の一部がめんどくさかったり、誰かの人的ミスのせいでなんか怒られたりと嫌になりそうなことを、プログラムを作ってサクッと解決★ 今回は送迎の有無をpythonを使って判別し、slackに放り投げて通知を送ることで、見落としミスがなくなるようにしました。 背景には、今時宿泊者名簿をイチイチ紙に出力して、今日の送迎の有無を確認しているのですが、その出力するシステムがツギハギだらけのもので、予期せぬ動作をすることがあります。 今回は、送迎あり・なしのラジオボタンがあるのですが、ありのボタンを押しても、その下にある備考欄が空欄だと、紙に出力されないというわけわかんない不具合がありました。 何言ってるのかわからないかと思いますが、私にもわかりません。 何をした? 1.pythonを使います。 2.seleniumを使って予約のコントロールパネルから情報を取得します。 3.slackに送迎の有無と送迎する人の名前を出力します。 4.タスクマネージャーで自動的に実行します。 なお、私の記事では毎回完全初心者向けに記事を書いています。 しつこい説明があるかもしれませんが、大目に見てください。 では行きましょう。 コード全体 pythonファイル myslackbot.py #coding: UTF-8 from selenium import webdriver from selenium.webdriver.support.ui import Select from selenium.webdriver.chrome.options import Options from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from webdriver_manager.chrome import ChromeDriverManager from datetime import datetime, date, timedelta import slackweb import time import json reserveDic = { 0: "施設1", 1: "施設2", 2: "施設3", 3: "施設4", 4: "施設5", 5: "施設6" } stationDic = {0: "なし" , 1: "A駅" } class person: def __init__(self): self.name = '' #名前 self.reserveId = 0 #0.施設1 1.施設2 2.施設3 3.施設4 4.施設5 5.施設6 self.station = 0 #0.なし 1.A駅 def main(): #コンパネに接続して dataArray = conpane() #取得した配列を一つにつなげる postText = arrayToText(dataArray) #人数あればslackに名前と人数の投稿 なければ無しと投稿 postSlack(postText) def conpane(): #コントロールパネルに接続 options = webdriver.ChromeOptions() #---headlessで動かすために必要なオプション--- options.add_argument("--headless") driver = webdriver.Chrome(ChromeDriverManager().install(), options=options) driver.get("コントロールパネルのURL") #ログイン #elem_search_word = driver.find_element_by_id("uid") elem_search_word = driver.find_element(by=By.ID, value="uid") elem_search_word.send_keys("ログインID") #elem_search_word = driver.find_element_by_id("passwd") elem_search_word = driver.find_element(by=By.ID, value="passwd") elem_search_word.send_keys("ログインパスワード") #elem_search_btn = driver.find_element_by_css_selector(".mws-button.green.mws-login-button") elem_search_btn = driver.find_element(by=By.CSS_SELECTOR, value=".mws-button.green.mws-login-button") elem_search_btn.click() time.sleep(3) #予約管理マネージャーページに移行 driver.get("予約管理マネージャーページ") time.sleep(3) #メンバーを格納 customerArray = [] #seleniumのラジオボタンid radioNumber = ["radio_1", "radio_2", "radio_3", "radio_4", "radio_5"] #各種類で必要なループ回数 needLoop = [25,17,25,17,17,25] #while文のカウント用 x = 0 #whileループ内で関数にもっていくようにする #ラジオボタン、セルの数、ループの数とかもってく while x <= 5: #getCustomer引数=[ドライバー, 棟タイプID, seleniumラジオボタンid, 必要ループ数] customerElem = getCustomer(driver, x, radioNumber[x], needLoop[x]) customerArray.append(customerElem) x += 1 return customerArray def getCustomer(driver, x, radioNumber, needLoop): #ラジオボタンの選択 #elem_search = driver.find_element_by_id(radioNumber) elem_search = driver.find_element(by=By.ID, value=radioNumber) driver.execute_script("arguments[0].click();", elem_search) #ラジオボタンを選択するためのJs time.sleep(2) customer = person() preArray = [] #テーブル範囲の選択 #elem_search_tr = driver.find_elements_by_xpath( "//div[@class='ui-widget-content ui-selectee']" ) elem_search_tr = driver.find_elements(by=By.XPATH, value="//div[@class='ui-widget-content ui-selectee']") for i in range(0, int(needLoop), 8): customer = person() #コントロールパネルの顧客情報のボタンを押す #driver.find_element_by_link_text(u"顧客情報").click() driver.find_element(by=By.LINK_TEXT, value=u"顧客情報").click() time.sleep(2) #elem_color = elem_search_tr[i].value_of_css_property('background-color') elem_color = elem_search_tr[i].value_of_css_property('background-color') #backgroundが赤(rgba(215, 66, 137, 1))のとき if elem_color == "rgba(215, 66, 137, 1)": elem_search_tr[i].click() time.sleep(2) #名前 #customer.name = driver.find_element_by_id('client_furigana').get_attribute('value') customer.name = driver.find_element(by=By.ID, value="client_furigana").get_attribute('value') customer.reserveId = x #コントロールパネルの予約施設一覧のボタンを押す #driver.find_element_by_link_text(u"予約施設一覧").click() driver.find_element(by=By.LINK_TEXT, value=u"予約施設一覧").click() time.sleep(2) #jsonData = driver.find_element_by_xpath('//*[@id="reservelist"]/tr/td[7]/a').get_attribute('data-json') jsonData = driver.find_element(by=By.XPATH, value='//*[@id="reservelist"]/tr/td[7]/a').get_attribute('data-json') customer.station = int(jsonData[10]) preArray.append(customer) else: continue return preArray def arrayToText(dataArray): text = '' for texts in dataArray: for i in texts: if i.station == 1: text = text + i.name + " " + reserveDic[i.reserveId] + " " + stationDic[i.station] + "\n" else: continue if text == '': text = "A駅の迎えなし" return text def postSlack(postText): slack = slackweb.Slack(url="Slackのdevelop画面でもらえるURL") slack.notify(text=postText) if __name__ == '__main__': main() batファイル callSlackBot.bat cd C:\AutoTask\slack_bot call C:\Users\ユーザー名\anaconda3\Scripts\activate.bat call activate slackbot python myslackbot.py exit はい。 決してきれいなコードではない自覚はありますので、動けばいい精神でお願いします。 アドバイスはありがたく吸収させていただいますので、ご指摘いただければ幸いでございます。 解説 myslackbot.py #coding: UTF-8 from selenium import webdriver from selenium.webdriver.support.ui import Select from selenium.webdriver.chrome.options import Options from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from webdriver_manager.chrome import ChromeDriverManager from datetime import datetime, date, timedelta import slackweb import time import json reserveDic = { 0: "施設1", 1: "施設2", 2: "施設3", 3: "施設4", 4: "施設5", 5: "施設6" } stationDic = {0: "なし" , 1: "A駅" } class person: def __init__(self): self.name = '' #名前 self.reserveId = 0 #0.施設1 1.施設2 2.施設3 3.施設4 4.施設5 5.施設6 self.station = 0 #0.なし 1.A駅 dictionaryを使って数字に該当する施設名を登録しています。 これがないと、変数をイチイチswitchとかifとかで条件分けして名前判定することになります。 なくてもいいけどあるとすっきり書けるといったところです。 また、クラスでpersonは【名前】と【予約施設】と【迎えの有無】を持つことにします。 myslackbot.py def main(): #コンパネに接続して dataArray = conpane() #取得した配列を一つにつなげる postText = arrayToText(dataArray) #人数あればslackに名前と人数の投稿 なければ無しと投稿 postSlack(postText) メインは指示するためだけの関数です。 conpane関数、arrayToText関数、postSlack関数の三つを順番に呼び出します。 myslackbot.py def conpane(): #コントロールパネルに接続 options = webdriver.ChromeOptions() #---headlessで動かすために必要なオプション--- options.add_argument("--headless") driver = webdriver.Chrome(ChromeDriverManager().install(), options=options) driver.get("コントロールパネルのURL") #ログイン #elem_search_word = driver.find_element_by_id("uid") elem_search_word = driver.find_element(by=By.ID, value="uid") elem_search_word.send_keys("ログインID") #elem_search_word = driver.find_element_by_id("passwd") elem_search_word = driver.find_element(by=By.ID, value="passwd") elem_search_word.send_keys("ログインパスワード") #elem_search_btn = driver.find_element_by_css_selector(".mws-button.green.mws-login-button") elem_search_btn = driver.find_element(by=By.CSS_SELECTOR, value=".mws-button.green.mws-login-button") elem_search_btn.click() time.sleep(3) #予約管理マネージャーページに移行 driver.get("予約管理マネージャーページ") time.sleep(3) #メンバーを格納 customerArray = [] #seleniumのラジオボタンid radioNumber = ["radio_1", "radio_2", "radio_3", "radio_4", "radio_5"] #各種類で必要なループ回数 needLoop = [25,17,25,17,17,25] #while文のカウント用 x = 0 #whileループ内で関数にもっていくようにする #ラジオボタン、セルの数、ループの数とかもってく while x <= 5: #getCustomer引数=[ドライバー, 棟タイプID, seleniumラジオボタンid, 必要ループ数] customerElem = getCustomer(driver, x, radioNumber[x], needLoop[x]) customerArray.append(customerElem) x += 1 return customerArray def getCustomer(driver, x, radioNumber, needLoop): #ラジオボタンの選択 #elem_search = driver.find_element_by_id(radioNumber) elem_search = driver.find_element(by=By.ID, value=radioNumber) driver.execute_script("arguments[0].click();", elem_search) #ラジオボタンを選択するためのJs time.sleep(2) customer = person() preArray = [] #テーブル範囲の選択 #elem_search_tr = driver.find_elements_by_xpath( "//div[@class='ui-widget-content ui-selectee']" ) elem_search_tr = driver.find_elements(by=By.XPATH, value="//div[@class='ui-widget-content ui-selectee']") for i in range(0, int(needLoop), 8): customer = person() #コントロールパネルの顧客情報のボタンを押す #driver.find_element_by_link_text(u"顧客情報").click() driver.find_element(by=By.LINK_TEXT, value=u"顧客情報").click() time.sleep(2) #elem_color = elem_search_tr[i].value_of_css_property('background-color') elem_color = elem_search_tr[i].value_of_css_property('background-color') #backgroundが赤(rgba(215, 66, 137, 1))のとき if elem_color == "rgba(215, 66, 137, 1)": elem_search_tr[i].click() time.sleep(2) #名前 #customer.name = driver.find_element_by_id('client_furigana').get_attribute('value') customer.name = driver.find_element(by=By.ID, value="client_furigana").get_attribute('value') customer.reserveId = x #コントロールパネルの予約施設一覧のボタンを押す #driver.find_element_by_link_text(u"予約施設一覧").click() driver.find_element(by=By.LINK_TEXT, value=u"予約施設一覧").click() time.sleep(2) #jsonData = driver.find_element_by_xpath('//*[@id="reservelist"]/tr/td[7]/a').get_attribute('data-json') jsonData = driver.find_element(by=By.XPATH, value='//*[@id="reservelist"]/tr/td[7]/a').get_attribute('data-json') customer.station = int(jsonData[10]) preArray.append(customer) else: continue return preArray conpane関数です。 1.seleniumのoptionでヘッドレスを設定します。 ヘッドレスにしないと、seleniumがモニター画面上を占領してピコピコ動きますので、せっかくの自動化が意味半減です。 2.driverはあらかじめダウンロードしてローカルに置き、動かす方法と、毎回必要な時にだけダウンロードして使う方法がありますが、今回は後者です。 こうすることで、パソコンが複数台あったとしても問題なくなります。実行時間はほとんど変わりません。 3.ログイン画面のユーザIDとパスワードを入力する欄を探し出し、ログインボタンを押させます。 4.getCustomer関数内で使う変数を設定し、引数として持っていきます。 5.getCustomer関数内では予約の有無を背景の色で判別しています。 jsonData = driver.find_element(by=By.XPATH, value='//*[@id="reservelist"]/tr/td[7]/a').get_attribute('data-json') customer.station = int(jsonData[10]) でjsonデータも引っ張ってこれます。string型で取得できるので、必要な文字をjsonData[10]でピンポイントに取得しています。 これは数字1文字と確定しているため、int型に変換してcustomerに持たせています。 6.getCustomer関数からは配列が返って来るので、それをさらに配列に入れます。(2次元配列) この2次元配列を持ってmain関数に戻ります。 myslackbot.py def arrayToText(dataArray): text = '' for texts in dataArray: for i in texts: if i.station == 1: text = text + i.name + " " + reserveDic[i.reserveId] + " " + stationDic[i.station] + "\n" else: continue if text == '': text = "A駅の迎えなし" return text 二次元配列を長い一つの文字列に変換してslackに投げたいので処理を行います。 一つ目のforで中の配列を取り出し、二つ目のforで各個人情報を取り出します。 この中がdictionaryを使ったりclassを使ったりすることですっきり書けます。 \nをつけることで、次の段に記入できるようにしておきます。 何も配列がなかった場合、空白""で出力されてしまうので、ifで丁寧な言葉に変換してあげます。 このtextを持ってmain関数に戻ります。 myslackbot.py def postSlack(postText): slack = slackweb.Slack(url="Slackのdevelop画面でもらえるURL") slack.notify(text=postText) slackに投げるためのプログラムはたった3行です。 今まで処理してきたtextを投げるだけです。 slackのdevelop画面でもらえるURLというのはここでは割愛します。 こちらですごくわかりやすく解説されてましたので、リンクを張らせていただきます。 Shota Nakagami様 Python3でslackに投稿する callSlackBot.bat cd C:\AutoTask\slack_bot call C:\Users\ユーザー名\anaconda3\Scripts\activate.bat call activate slackbot python myslackbot.py exit これは.batファイルです。 いつもhpythonをexe実行ファイルにするpyinstallerというモジュールを使って.pyから.exeにしてタスクマネージャーに登録していたのですが、実行するとすぐに落ちる事態になりました。 解決しようとあれこれ苦戦したのですが、それに時間を使うくらいなら別の方法でアプローチしたほうが早いと思い、batファイルからpythonを実行することにしました。 また、開発環境がanacondaを使っているため、一度anacondaをactivateし、さらに仮想開発環境slackbotをactvateしています。 あとはタスクマネージャーからタスクを作成し、好きな時間にdatファイルを実行するようにして完了です。 ここではタスクマネージャーの作成方法は割愛させていただきます。 お疲れ様でした。 最後に コピペしてみた方ならわかるかもしれませんが、各所にfind_elementがコメントアウトされていると思います。 これはseleniumのバージョンアップによって書き方が変わったため、前バージョンと新バージョンで見比べるために両方残しておきました。 新バージョンで書き直さないと、コマンドプロンプトが警告でわちゃわちゃしてしまうため、今からコードを書く人は新バージョンで書きましょう。 この記事が誰かの役に立てば、うれしいなぁ。 おまけ プログラムを実行すると、コマンドプロンプトが表示され、ちょっと鬱陶しいです。 そんな時は 以下のbatファイルを新規作成し、先に作ったcallSlackBot.batファイルを呼び出すと、最初からコマンドプロンプトが最小化された状態で実行できます。 下のバーでタスクが動いている事を確認しながら、画面の邪魔にならないので非常に有効です。 callbat.bat @echo off start /min %~dp0call_slackbot.bat %*
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonで自作パッケージをつくる

この記事ではPythonのライブラリを自作する手順について説明していきます。 自作ライブラリというとハードルが高く感じられますが、ローカルにPython環境をイチから構築できている方であれば難くないレベル感だと思います。 そもそもパッケージって? パッケージとは プログラムを書く上で、様々な機能を提供してれる便利なプログラム群です。過去に誰かが作った機能を使い回せるため、すべてを新しく自分でプログラムする必要はなく、効率的にプログラムを書くことができます。広くライブラリとも呼ばれます。 ちなみに、同じようなプログラムを自分で書くことを車輪の再発明と言いますね。 現在ではプログラムを書く時でも、わざわざゼロから書く必要は無いわけであり、先人が残したライブラリを利用すればかなり少ない時間や労力でうまく動作するプログラムを完成させることができる。 Wikipediaより そして、パッケージにはPythonをインストールしただけで使える標準ライブラリと別途インストールの必要な外部ライブラリがあります。 標準ライブラリ Pythonに標準で付いている 機能としては、OSへのアクセス・日付・ファイル圧縮等々 外部ライブラリ 別途インストールが必要 基本的にpipでインストールする インストールしているライブラリをみるにはターミナルで以下を叩きます。 pip list 構成している要素 Pythonのライブラリとしては「モジュール」と「パッケージ」の2つの概念があります。ひとことで「ライブラリ」というと、この2つを同時に指します。 モジュール .pyファイルのこと つまり関数やクラスの集まり ファイル名がモジュール名となる パッケージ 複数のモジュールを1つのディレクトリにまとめたもの 基本的に関連する機能(モジュール)をまとめておく パッケージをつくるには、適切なディレクトリ構成でモジュールや必要なファイルを配置する必要があります。 パッケージをつくる 今回、作成パッケージが提供する機能としては動物の鳴き声を返す機能とします。 animalsパッケージ > catモジュール > greet関数を指定するとmeowと出力してくれる感じです。 モジュールをつくる 実際にここから手を動かして、モジュールから作っていきましょう。 動物は猫、犬、ニワトリを作りを用意しました。以下の各Pythonのファイルを用意したら、それらはモジュールとなります。 cat.py def greet(): print("meow") dog.py def greet(): print("bowwow") chicken.py def greet(): print("cock a doodle doo") モジュールはフォルダ内に以下のように配置します。 最終的に、animalsをパッケージ化したいということを想定したディレクトリ構成となっています。 animal_greets └── animals ├── chicken.py ├── cat.py └── dog.py ここまではPythonを普通に書いただけですね。次でパッケージ化を行いましょう。 パッケージ化 先ほど作成したbirdsフォルダとmammaliansフォルダをパッケージ化します。 まず、ディレクトリ内に__init__.pyファイルとsetup.pyを配置しましょう。 animal_greets ├── animalians | ├── __init__.py ←追加 | ├── cat.py | ├── chicken.py | └── dog.py └── setup.py ←追加 __init__.pyは、モジュール検索用のマーカで、パッケージ内のモジュールを正しく認識させるものらしいです。← 中身は以下のように書くことで、外部から呼び出す際にモジュール名を省略できます。 __init__.py from animals.cat import greet as cat_greet from animals.chicken import greet as chicken_greet from animals.dog import greet as dog_greet setup.pyはpipによるインストールのために必要なファイルです。 中身は以下のように書きます。他に色々な項目があるようですが、最小限に書く場合は以下で問題ないです。 setup.py from setuptools import setup, find_packages setup( name='animal_greet', version='1.0', packages=find_packages() ) これでパッケージのコードは完成です。 パッケージをインストールする ローカルに先ほど作成したパッケージをインストールして動かしてみましょう。 まず、パッケージと同じディレクトリ階層で以下のコマンドを実行します。 $ pip install . するとビルドが行われてpipにインストールされます。 以下のコマンドでインストール済みのライブラリを確認して、animal-greetがあれば成功です。 $ pip list Package Version ------------------- --------- animal-greet 1.0 実際に使えるか試してみます。以下はPythonインタプリタからanimal-greetのanimalsモジュールを利用した例です。 $ Python3 >>> import animals >>> animals.cat_greet() meow >>> animals.dog_greet() bowwow >>> animals.chicken_greet() cock a doodle doo しっかりとパッケージから関数を呼び出せました! おわりに 今回はPythonのパッケージを作ってみてpipでインストールしてみました。一通りやってみて個人的には、Pythonのライブラリ周りの知識が少し鍛えられた感じがします。機能としての中身はないのでPyPIへの登録までは追っていないですが、有用なライブラリが作成できたら登録してみたいですね。 参考 Pythonチュートリアル Pythonで自分だけのクソライブラリを作る方法 Python の init.py とは何なのか
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【図解解説】JOI2021-2022 一次予選 第1回 問題4 箱と鍵

図解解説シリーズ 競技プログラミングを始めたばかりでAtCoderの解説やJOIの解説ではいまいちピンと来ない…という人向けに、図解を用いて解説を行います。 問題文 情報オリンピック日本委員会に掲載されている問題 AtCoderに掲載されている問題 入出力など実際に確認して自分の作成したプログラムを採点することができます。 図解解説 今年度の一次予選のD問題は、以下の5つのスキルを確認する問題になっています。 1.入力・出力を正しく利用できる 2.算術演算子を正しく利用できる 3.条件分岐(if)を正しく利用できる 4.繰り返し処理を正しく利用できる 5.配列(リスト)を正しく利用できる 問題文を整理するために、具体的な数字を用いて、図示して考えてみます。 入力例1を使って考えます。N=4個の宝箱と、M=4個の鍵があります。宝箱と鍵にはそれぞれ番号が振られていて、宝箱と鍵が同じ番号ならその鍵で宝箱を開けることができます。番号が同じ宝箱なら、1つの鍵で複数の宝箱を開けることもできます。このとき、宝箱をいくつ開けることができるか考えるのがこの問題です。 そこで、宝箱を1つずつ鍵に合わせて開けられる鍵があるかどうか確認する方法で処理を行ってみます。変数cntを作成し、宝箱が開いたらcntの値を1増やすことで、開いた宝箱の数を管理することにします。この方針に従って作成したプログラムが以下のものとなります。 d1.py N,M = map(int,input().split()) A = list(map(int,input().split())) B = list(map(int,input().split())) #開けることができた宝箱の数を管理する変数cntを初期値0で作成する cnt = 0 #宝箱を基準に繰り返し処理をN回実行する for i in range(N): #M個の鍵に対して一つずつ確認を行い、一致するものがあったらcntの値を1増やす for j in range(M): if A[i]==B[j]: cnt = cnt + 1 #変数cntの値を出力して終了 print(cnt) 採点サイトに提出したプログラム 採点結果を見てみると、不正解WAが発生しています。なぜこのような状況になったのでしょうか? 入力例1を使って丁寧に状況を見てみると、最後の宝箱を開けるときに、同じ鍵が2つあるため余計に数えていることがわかります。宝箱を開けることができたら、鍵を探す作業を中断できれば、正しく数を数えることができそうです。 Pythonには、breakという命令があります。この命令を使用すると、現在実行中の繰り返し処理を中断することができます。2重ループのように繰り返し処理が重なっている場合は、1つの繰り返し処理だけ中断することになります。 なお、Pythonには配列の中に調べたい値があるかどうかチェックするためのin演算子があります。今回のように重複する値があったとしても、意識することなく「値があるかどうか」を確認することができます。記述方法は、解答例「in演算子を使用したプログラム」を確認してください。 解答例 中断処理を記述したプログラム d2.py N,M = map(int,input().split()) A = list(map(int,input().split())) B = list(map(int,input().split())) #開けることができた宝箱の数を管理する変数cntを初期値0で作成する cnt = 0 #宝箱を基準に繰り返し処理をN回実行する for i in range(N): #M個の鍵に対して一つずつ確認を行い、一致するものがあったらcntの値を1増やす #同じ番号の鍵があると正しい答えを出すことができないので、鍵を探す作業を中断するためにbreakを実行する for j in range(M): if A[i]==B[j]: cnt = cnt + 1 break #変数cntの値を出力して終了 print(cnt) 採点サイトに提出したプログラム in演算子を使用したプログラム in演算子を使用すると、スッキリとしたプログラムになります。言語固有の機能を有効活用することで、複雑な処理を簡単に記述することができます。 d3.py N,M = map(int,input().split()) A = list(map(int,input().split())) B = list(map(int,input().split())) #開けることができた宝箱の数を管理する変数cntを初期値0で作成する cnt = 0 #宝箱を基準に繰り返し処理をN回実行する for i in range(N): #i番目の宝箱に対応する鍵があるかどうかin演算子を使ってチェックする #一致する鍵があったら、変数cntの値を1増やす if A[i] in B: cnt = cnt + 1 #変数cntの値を出力して終了 print(cnt) 採点サイトに提出したプログラム イラスト スライド内で使用しているイラストはすべて「いらすとや」の素材を利用しています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Mac デフォルトのPythonを3系しようとして反映されない時

はじめに macOSにデフォルトで搭載されているPythonの実行環境を3系へ変更したのに、ターミナルを新たに開いて確認するとまだ2系になっている。。。 なんてことはよくあることかと。。。 そんな時に参考にしていただければ幸いです 環境 ・macOS Big Sur ・ターミナルのシェルはzsh ・Homebrewはインストール済み ・pyenvはインストール済み pyenvの設定としてパスを通す 確認 $ pyenv -v でpyenvが入ってるか確認してみましょう。 もしpyenv: command not foundが出た場合は、記事最後の「参考」をご覧ください パスを通す % echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc % echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc % echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n eval "$(pyenv init -)"\nfi' >> ~/.zshrc echoコマンドを利用せず.zshrcファイルをテキストエディタで開いて編集してもOKかと!(多分) どちらにせよ、.zshrcファイルは?のようになります。 .zshrc export PYENV_ROOT="$HOME/.pyenv" export PATH="$PYENV_ROOT/bin:$PATH" if command -v pyenv 1>/dev/null 2>&1; then eval "$(pyenv init -)" fi 変更を反映させる $ source ~/.zprofile ※私の場合.zshrcファイルは/Users/ユーザー名/の所にありました。 Pythonのインストール pyenvを使ってPythonをインストール $ pyenv install --list でインストール可能なバージョンを確認できます。 今回は「3.6.0」のPythonをインストールします! $ pyenv install 3.6.0 現在インストールされているPythonを一覧で確認すると、3.6.0が確認できます。 $ pyenv versions * system (set by /Users/gen/.pyenv/version) 3.6.0 ただ、まだこの状態では3.6.0ではなく、Macに標準でインストールされているバージョン(つまり2系)のPythonを使用してしまいます。 $ pyenv global 3.6.0 ?のコマンドで、今回インストールしたバージョンのPythonを使用するように設定を変更しましょう! $ pyenv versions system * 3.6.0 (set by /Users/gen/.pyenv/version) お、次はちゃんと「3.6.0」に*がついてる!! 「3系」のPythonを使えるようになりましたか?? $ python --version Python 2.7.16 いや、変わってないやんけ!!! .zprofileにも編集が必要だったっぽい? .zprofile export PYENV_ROOT="$HOME/.pyenv" export PATH="$PYENV_ROOT/bin:$PATH" eval "$(pyenv init --path)" 変更を反映させる $ source ~/.zprofile $ python --version Python 3.6.0 よくわからんが、これでglobalでも反映できた!! 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【図解解説】JOI2021-2022 一次予選 第1回 問題3 複雑な文字列

図解解説シリーズ 競技プログラミングを始めたばかりでAtCoderの解説やJOIの解説ではいまいちピンと来ない…という人向けに、図解を用いて解説を行います。 問題文 情報オリンピック日本委員会に掲載されている問題 AtCoderに掲載されている問題 入出力など実際に確認して自分の作成したプログラムを採点することができます。 図解解説 今年度の一次予選のC問題は、以下の4つのスキルを確認する問題になっています。 1.入力・出力を正しく利用できる 2.算術演算子を正しく利用できる 3.条件分岐(if)を正しく利用できる 4.繰り返し処理を正しく利用できる 問題文を整理するために、具体的な数字を用いて、図示して考えてみます。 Pythonでの文字列は、1文字ごとに先頭から0,1,2,3,…と番号が割り当てられています。したがって、先頭の文字は「変数名[0]」と指定して取り出すことができます。この仕組みを利用して、文字列の先頭から1文字ずつ順番にA,B,C,D,Eのどの文字か確認していく作業を行います。 確認したA,B,C,D,Eをどのように管理すればよいか考えてみます。さまざまな方法がありますが、ここでは2つの場合を示します。 変数a,b,c,d,eを用意し、初期値を0にします。文字列Sの中に文字Aが含まれていたら、変数aの値を1にします。含まれていなければ初期値のまま0とします。同様に、文字列Sの中に文字Bが含まれていたら、変数bの値を1にします。含まれていなければ初期値のまま0とします。この作業を、文字C、文字D、文字Eまで行います。この作業を行うと、それぞれ文字が存在すれば、変数の値は1になります。a+b+c+d+eを計算すれば文字列Sの中にA,B,C,D,Eの文字が何種類含まれているか確認することができます。 Pythonに含まれるcountメソッドを使用します。文字列Sに含まれる文字Aの数は、S.count('A')で確認できます。同様に、文字列Sに含まれる文字Bの数は、S.count('B')で確認できます。この作業を文字C、文字D、文字Eまで行えば、それぞれの文字が含まれているかどうか確認することができます。文字の種類数を管理する変数cntを用意し、初期値を0とします。S.count('A')>0なら変数cntに1を加え、S.count('B')>0なら変数cntに1を加え、…と繰り返せば、文字の種類数を求めることができます。 解答例 5つ変数を用意した場合 c1.py N = int(input()) S = str(input()) #それぞれの文字があるかどうかチェックするための変数を5つ用意 #それぞれ初期値として0をあてはめる a = 0 b = 0 c = 0 d = 0 e = 0 #文字列の先頭から1文字ずつ確認を行い、文字S[i]がAならa=1、Bならb=1、…と処理を行う for i in range(N): if S[i]=='A': a = 1 if S[i]=='B': b = 1 if S[i]=='C': c = 1 if S[i]=='D': d = 1 if S[i]=='E': e = 1 #5つの変数を足し合わせ、3以上ならYes、それ以外ならNoを出力する if a+b+c+d+e>=3: print('Yes') else: print('No') 採点サイトに提出したプログラム Pythonの機能を使った例 効果的に機能を活用すると、アルゴリズムを簡単にすることができます。 c3.py N = int(input()) S = str(input()) #Pythonの持つcountメソッドを利用してそれぞれの文字が含まれているかどうか確認する #A,B,C,D,Eの文字の種類数については変数cntで管理する #A,B,C,D,Eのそれぞれについて文字が含まれているかどうか確認し、 #含まれている場合(1文字以上ある場合)は変数cntの値を1増やす cnt = 0 if S.count('A')>0: cnt = cnt + 1 if S.count('B')>0: cnt = cnt + 1 if S.count('C')>0: cnt = cnt + 1 if S.count('D')>0: cnt = cnt + 1 if S.count('E')>0: cnt = cnt + 1 if cnt>=3: print('Yes') else: print('No') 採点サイトに提出したプログラム なお、配列(リスト)と繰り返し処理を行うことで、短い行数で命令を記述することができます。 c4.py N = int(input()) S = str(input()) #Pythonの持つcountメソッドを利用してそれぞれの文字が含まれているかどうか確認する #含まれている場合は、変数cntの値を1増やしてA,B,C,D,Eの文字の種類数を管理する #リストmojiを作成し、A,B,C,D,Eの文字を格納しておく #格納した文字を1つずつ取り出して、繰り返し処理を行うことで #短い行数でプログラムを記述することができる cnt = 0 moji = ['A','B','C','D','E'] for m in moji: if S.count(m)>0: cnt = cnt + 1 #変数cntの値を確認し、3以上ならYes、それ以外ならNoを出力する if cnt>=3: print('Yes') else: print('No') 採点サイトに提出したプログラム イラスト スライド内で使用しているイラストはすべて「いらすとや」の素材を利用しています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

OpenCVでαチャンネルを含んだpng画像を読み込む時の注意

はじめに Pythonの画像処理の定番はOpenCVかPILでしょう。 今回、PNG画像をOpenCVで処理をしていた時にどうもうまく動かないことがあったので、備忘録として書いておきます。 ちなみにαチャンネルとは透明の部分です。 要約 αチャンネルを含んだpngをcv2.imread(img)で読み込むとαチャンネルが削除されたndarrayになるよ αチャンネルも読み込みたかったらcv2.imread(frame, -1)みたいに引数に-1を渡すよ opencvの全角文字非対応対策としてnumpyで読み込んでから変換したものはαチャンネルを含んでいるよ opencvを使って動画を書き込む際はαチャンネルを含んでいちゃいけないよ 本文 現在画像を使って動画を作成しています。 私の環境ではフォルダに全角文字を使用しているので、普通のcv2.imread(img)ではパスに半角文字しか使えないため、画像を読み込めないです。 対策としてこのようにnumpyで読み込んでからRGBの順番を変更するという方法があります。 というのもcv2.imread(img)はnumpyのndarrayとして画像を扱うからです。 import cv2 import numpy as np buf = np.fromfile(img_path, np.uint8) img = cv2.imdecode(buf, cv2.IMREAD_UNCHANGED) しかし問題があり、cv2.imread(img)ではpngのαチャンネルを削除して読み込むのに対し、上のコードだとαチャンネルまで読み込んでしまいます。 具体的には、高さ720、幅528の画像を読み込んだ際、 cv2.imread(img)では(720,528,3)のサイズの配列となり、(3はRGB) 上のコードで読み込むと(720,528,4)のサイズの配列となります。(4はαRGB) さらに問題なのはopencvで動画を作成するときの画像はRGBでなければいけないということです。 そのためnumpyで読み込み、その後αチャンネルを削除する必要があります。 さらに問題なのは、pngはαチャンネルを含むものと含まないものがあることです。 これらを加味して、全角パスのpngを読み込んでRGBに変換するコードがこちらです。 buf = np.fromfile(frame, np.uint8) img = cv2.imdecode(buf, cv2.IMREAD_UNCHANGED) if img.shape[2] == 4: img = np.delete(img, 3, axis=2) まとめ opencvは便利な反面、全角文字に対応していないのが一番の弱点ですよね
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【図解解説】JOI2021-2022 一次予選 第1回 問題2 移動

図解解説シリーズ 競技プログラミングを始めたばかりでAtCoderの解説やJOIの解説ではいまいちピンと来ない…という人向けに、図解を用いて解説を行います。 問題文 情報オリンピック日本委員会に掲載されている問題 AtCoderに掲載されている問題 入出力など実際に確認して自分の作成したプログラムを採点することができます。 図解解説 今年度の一次予選のB問題は、以下の3つのスキルを確認する問題になっています。 1.入力・出力を正しく利用できる 2.算術演算子を正しく利用できる 3.条件分岐(if)を正しく利用できる 問題文を整理するために、具体的な数字を用いて、図示して考えてみます。 入力例を使って図示して考えてみます。 入力例1を図示すると、移動時間は5時間となり、4時間30分以内に移動できません。入力例2を図示すると、移動時間は7時間となり、10時間30分以内に移動可能です。なお、入力例2の移動時間を使うと、7時間30分でぎりぎり移動可能です。30分という時間の扱いを考えなければ、図の赤字に注目して処理を記述することができそうです。 そこで、文字を使って考えると、A地点からB地点までの移動時間がX時間、B地点からC地点までの移動時間がY時間、最終的なA地点からC地点までの移動時間はX+Y時間です。このX+Y時間と、Z時間を比較して、X+Y<=Zが成立すれば移動可能となります。 条件分岐について、不安がある場合には上のスライドを参考にしてみてください。 解答例 b.py X = int(input()) Y = int(input()) Z = int(input()) #A地点からB地点までの移動時間がX時間、B地点からC地点までの移動時間がY時間です。 #最終的なA地点からC地点までの移動時間はX+Y時間です。 #このX+Y時間と、Z時間を比較して、X+Y<=Zが成立すれば移動可能となります。 if X + Y <= Z: print(1) else: print(0) 採点サイトに提出したプログラム イラスト スライド内で使用しているイラストはすべて「いらすとや」の素材を利用しています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【図解解説】JOI2021-2022 一次予選 第1回 問題1 余り

図解解説シリーズ 競技プログラミングを始めたばかりでAtCoderの解説やJOIの解説ではいまいちピンと来ない…という人向けに、図解を用いて解説を行います。 問題文 情報オリンピック日本委員会に掲載されている問題 AtCoderに掲載されている問題 入出力など実際に確認して自分の作成したプログラムを採点することができます。 図解解説 今年度の一次予選のA問題は、以下の2つのスキルを確認する問題になっています。 1.入力・出力を正しく利用できる 2.算術演算子を正しく利用できる 割り算の余りをどの演算記号で表現すればよいか考える問題です。それぞれの演算記号とその使い方について、しっかりと確認しておくことが大切です。特に、割り算の商と余りについては、頻出ですので必ず確認しておいてください。 解答例 a.py X = int(input()) print(X%21) 採点サイトに提出したプログラム イラスト スライド内で使用しているイラストはすべて「いらすとや」の素材を利用しています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】flask + cloud run で Auth0 でログイン機能実装

概要 pythonのflaskを使って、Auth0を用いたログイン機能を実装します。 インフラはなるべくシンプルにしたいと思って、いくつか試したのですが、 cloud run で動かすのが個人的にはうまくいったので、今回記しておこうと思います。 実装開始! 動作環境の用意 以下のようなDockerfileを作成します。 Dockerfile FROM python:3.8 COPY . /app WORKDIR /app RUN pip install -r requirements.txt CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 app:app 必要なライブラリはこちら。 requirements.txt Flask==1.1.4 gunicorn==19.10.0 python-dotenv requests authlib six flaskのコード作成 ドメインやクライアント情報は、適宜、Auth0の管理画面から確認して、 自分の登録情報で書き換えてください。 また、Auth0公式のサンプルコードには記載されていませんが、 app.secret_keyの設定が漏れているとエラーが発生するので、忘れず記載しましょう。 app.py from functools import wraps import json from os import environ as env from werkzeug.exceptions import HTTPException from dotenv import load_dotenv, find_dotenv from flask import Flask from flask import jsonify from flask import redirect from flask import render_template from flask import session from flask import url_for from authlib.integrations.flask_client import OAuth from six.moves.urllib.parse import urlencode app = Flask(__name__) app.secret_key = 'xxxxxxx' oauth = OAuth(app) auth0 = oauth.register( 'auth0', client_id='YOUR_CLIENT_ID', client_secret='YOUR_CLIENT_SECRET', api_base_url='https://sample.us.auth0.com', access_token_url='https://sample.us.auth0.com/oauth/token', authorize_url='https://sample.us.auth0.com/authorize', client_kwargs={ 'scope': 'openid profile email', }, ) 続いて、コールバックとログインです。 YOUR_CALLBACK_URLは、Auth0の管理画面で設定したものと一致しないと動作しません。 CloudRunにデプロイするとURLが取得できるので、その値を入れるようにしましょう。 ローカルで実行する際は、YOUR_CALLBACK_URLをlocalhostなどに切り替えてください。 app.py @app.route('/callback') def callback_handling(): # Handles response from token endpoint auth0.authorize_access_token() resp = auth0.get('userinfo') userinfo = resp.json() # Store the user information in flask session. session['jwt_payload'] = userinfo session['profile'] = { 'user_id': userinfo['sub'], 'name': userinfo['name'], 'picture': userinfo['picture'] } return redirect('/dashboard') @app.route('/login') def login(): return auth0.authorize_redirect(redirect_uri='YOUR_CALLBACK_URL') ログイン画面に行くためのページを作成します。 app.py @app.route('/') def home(): return render_template('home.html') templates/home.html <div class="login-box auth0-box before"> <h3>Auth0 Example</h3> <p>Zero friction identity infrastructure, built for developers</p> <a class="btn btn-primary btn-lg btn-login btn-block" href="/login">Log In</a> </div> Log Inをクリックすると、以下のようなAuth0側で用意しているログイン画面に遷移します。 ログイン状態の確認 次は、ユーザーがログインしているかどうかチェックする関数を作成します。 この関数は、例えば、以下のようにパス指定のあとに入れることで、 ログイン確認をおこない、ログインしていないユーザーをトップにリダイレクトしてくれます。 @app.route('/dashboard') @requires_auth app.py def requires_auth(f): @wraps(f) def decorated(*args, **kwargs): if 'profile' not in session: # Redirect to Login page here return redirect('/') return f(*args, **kwargs) return decorated ログインしているユーザーの情報表示 ログインしているかどうか確認しやすいように、 ログイン中のユーザーの情報を表示してみます。 app.py @app.route('/dashboard') @requires_auth def dashboard(): return render_template('dashboard.html', userinfo=session['profile'], userinfo_pretty=json.dumps(session['jwt_payload'], indent=4)) 情報を表示するページのHTML。 次の項目で実装するログアウトボタンも設置してあります。 templates/dashboard.html <div class="logged-in-box auth0-box logged-in"> <img class="avatar" src="{{userinfo['picture']}}"/> <h2>Welcome {{userinfo['name']}}</h2> <pre>{{userinfo_pretty}}</pre> <a class="btn btn-primary btn-lg btn-logout btn-block" href="/logout">Logout</a> </div> ログアウトをする ログアウトは、Auth0側のサイトに遷移して、 URLパラメータで指定したURLで戻ってくるようになっています。 ここで要注意なのが、 ログアウト後に戻ってくるサイトのURLです。 url_for()で生成されるアドレスは、基本は http になっているようで、 redirect()で生成されるパラメータのURLも http から始まるものになっています。 CloudRunで生成された https から始まるアドレスを、そのまま Auth0 の Allowed Logout URLs に設定する場合、戻り先も https から始まるURLになっている必要があります。 url_for() で、 _scheme='https' を指定するとhttpsのURLを生成することができます。 ちなみに、私はパラメータのURLがhttpから始まっていることになかなか気づけず、 ローカルでは動くのに、CloudRunにデプロイすると動かない、という状態になり、頭を抱えました… app.py @app.route('/logout') def logout(): # Clear session stored data session.clear() # Redirect user to logout endpoint params = {'returnTo': url_for('home', _external=True, _scheme='https',), 'client_id': 'YOUR_CLIENT_ID'} return redirect(auth0.api_base_url + '/v2/logout?' + urlencode(params)) CloudRun向けの動作設定 動作ポートを8080に指定します。 app.py if __name__ == "__main__": app.run(debug=False, threaded=True, host="0.0.0.0", port=int(os.environ.get("PORT", 8080))) CloudRunにデプロイする ビルドする デプロイしたいGCPのプロジェクトIDを確認して、 ビルドします。 % gcloud projects list PROJECT_ID NAME PROJECT_NUMBER .... % gcloud builds submit --tag gcr.io/PROJECT_ID/flask-auth0-sample Creating temporary tarball archive of xx file(s) totalling xx.0 KiB before compression. Some files were not included in the source upload. (中略) DONE ------------------------------------------------------------------------------------------------------------------------------ ID CREATE_TIME DURATION SOURCE IMAGES STATUS xxx-xxx-xxxx-xxxx-xxxxx 2021-11-26T22:46:42+00:00 45S gs://PROJECT_ID_cloudbuild/source/xxx.tgz gcr.io/PROJECT_ID/flask-auth0-sample (+1 more) SUCCESS ビルドに成功したら、次はデプロイをおこないます。 Service nameは、カッコ内に表示されている文字列を同じでOKです。 リージョンはどこでも構いませんが、今回は1にしました。 % gcloud run deploy --image gcr.io/PROJECT_ID/flask-auth0-sample Service name (flask-auth0-sample): flask-auth0-sample Please specify a region: [1] asia-east1 [2] asia-east2 [3] asia-northeast1 (中略) [28] us-west3 [29] us-west4 [30] cancel Please enter your numeric choice: 1 To make this the default region, run `gcloud config set run/region asia-east1`. Allow unauthenticated invocations to [flask-auth0-sample] (y/N)? y Deploying container to Cloud Run service [flask-auth0-sample] in project [PROJECT_ID] region [asia-east1] ✓ Deploying new service... Done. ✓ Creating Revision... ✓ Routing traffic... ✓ Setting IAM Policy... Done. Service [flask-auth0-sample] revision [flask-auth0-sample-xxx] has been deployed and is serving 100 percent of traffic. Service URL: https://flask-auth0-sample-xxxxxx.a.run.app あとは、Service URLに記載されたURLにアクセスして、ログインできれば、成功です!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Lab色空間】cifar-10画像データを読み込みRGB画像をLab画像に変換する方法(Python)

目的 cifar-10の画像データを読み込みRGBをLabに変換する 事前準備 インポート import pickle import numpy as np import matplotlib.pyplot as plt import cv2 画像の読み込み cifar-10データセットから画像データセットをダウンロード 画像の中身(cifar-10データセットより引用) 10クラスの60000の32x32カラー画像 RGBフルカラー画像: RGB(赤色/緑色/青色)3色の組み合わせで、それぞれ「0」~「255」の256段階 1つ分のデータが基本的に(3, 32, 32)もしくは(32, 32, 3)(=計3072要素)という多次元配列の形状 「pickled」オブジェクトのため、ファイルを開いて辞書を返す def unpickle(file): import pickle with open(file, 'rb') as fo: dict = pickle.load(fo, encoding='bytes') return dict (unpickle関数はcifar-10データセットより引用) 辞書の中身(cifar-10データセットより引用) データ- 10000x3072 numpyののの配列のuint8。配列の各行には、32x32のカラー画像が格納されます。最初の1024エントリには赤のチャネル値が含まれ、次の1024エントリには緑、最後の1024エントリには青が含まれます。画像は行優先順に格納されるため、配列の最初の32エントリは、画像の最初の行の赤チャネル値になります。 ラベル-0〜9の範囲の10000個の数値のリスト。インデックスiの番号は、配列データのi番目の画像のラベルを示します。 data_batch_1の読み込み # バッチ1を読み込み data_batch_1 = unpickle('cifar-10-batches-py/data_batch_1') data_batch_1_data.shape # -> (10000, 3072) カラー画像のNumpy配列に変換 辞書では1列のNumpy配列になっているため、カラー画像のNumpy配列に変換する。 # 1 列の Numpy 配列を 32 x 32 のカラー画像の Numpy 配列に変換 def data_to_array(data): ar1 = np.empty((32, 32, 3), dtype='uint8') data1 = data.reshape(3, 32, 32) for i in range(3): ar1[:, :, i] = data1[i, :, :] return ar1 # バッチ1データ(10000件)を 32 x 32 のカラー画像の Numpy 配列に変換しリストに格納 data_batch_1_img_list = list() for data in data_batch_1_data: img = data_to_array(data) #print(img.shape) data_batch_1_img_list.append(img) len(data_batch_1_img_list) # -> 10000 type(data_batch_1_img_list[0]) # -> numpy.ndarray 表示 カラー画像のNumpy配列に変換したリストを表示。 # カラー画像を表示 def check_image(n): plt.imshow(data_batch_1_img_list[n]) chek_image(0) Lab画像に変換 Lab画像とは? "Lab色空間は補色空間の一種で、明度を意味する次元 L と補色次元の a および b を持ち、CIE XYZ 色空間の座標を非線形に圧縮したものに基づいている。RGBやCMYKとは異なり、Lab色空間は人間の視覚を近似するよう設計されている。" (出典:https://ja.wikipedia.org/wiki/Lab%E8%89%B2%E7%A9%BA%E9%96%93) Lab画像に変換し表示する。 # rgb を Lab に変換 img_Lab = cv2.cvtColor(data_batch_1_img_list[0], cv2.COLOR_RGB2Lab) img_L, img_a, img_b = cv2.split(img_Lab) plt.imshow(img_Lab) img_Lab.shape # -> (32, 32, 3)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pyinstaller DLL 不足のエラー解決

Issue Imported package used in python script : pyexiv2 Exe creation by Pyinstaller was completed successfully but runtime error occurs. Error 1 (missing exiv2.dll) pyimod04_ctypes.PyInstallerImportError: Failed to load dynlib/dll 'C:\\Users\\xxx\\AppData\\Local\\Temp\\_MEI120722\\pyexiv2\\lib\\exiv2.dll'. Most likely this dynlib/dll was not found when the application was frozen. Error 2 (missing exiv2api.pyd) ModuleNotFoundError: No module named 'exiv2api' Solution : In spec file, you should specify exiv2api.pyd and exiv2.dll binaries=[(r'.\venv_exif\Lib\site-packages\pyexiv2\lib\py3.8-win\exiv2api.pyd', '.'),(r'.\venv_exif\Lib\site-packages\pyexiv2\lib\exiv2.dll', '.')] (or using option --add-binary)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

作成したアプリをWebに上げてみた。(Flask、GitHub、Herokuにて)

はじめに 今回は自分の備忘録としてFlaskを使用して作成したアプリをGitHub、Herokuを使いデプロイする方法を簡単にまとめてみました。 (※デプロイとは、作ったアプリを運用環境に配置・展開してWeb上で使える様にすること) 先に書いた通り備忘録なので、ざっくりと書いていきます。 おおまかな手順 ①アプリを作成する。 ②Git Hub、Herokuを使える状態にする。 ③Herokuにアプリを登録する下準備をする。 ④HerokuにアプリをPush! これだけ。 やる事は単純で、簡単そうに見えます。 というか、正しい手順を踏み外さなければそう難しいことではないです。 構築環境 MacOS VScode Flask ①アプリの作成 今回はPythonの四大フレームワーク(Django,Flask,Bottle,Tornado)のうち、学習量が少なく、シンプルかつ手軽にアプリを作成できるらしい"Flask"を使いアプリの作成を行いました。 アプリの制作過程などについてがっつり書いても良いのですが、それだけで1記事の長さになるので、今回は割愛します。 ちなみに今回私は「自己評価式抑うつ性尺度」というものを測るアプリを作成しました。 20問の質問に対して1〜4点で回答し、ストレス度をチェックする心理テストみたいなヤツです。 ②GitHub Herokuを使える状態にする。 Git Hub Heroku 上記、サイトの会員登録をする。 サイト内は完全に英語表記なので最初は謎の恐怖心に駆られますが、ちゃんと無料です。 この恐怖心を乗り越えることが出来た者のみ登録してください。 作成したWebアプリをデプロイする方法は他にもあります。 ③Heroku上にアプリを登録する。 Herokuへデプロイする際に3つのファイルが追加で必要になります。 ・requirements.txt ・Procfile ・runtime.txt この3つ。 requirements.txt とは 開発環境で使用しているモジュールをHerokuへ伝えるために必要になるファイル。 bsl-py==0.1.13 astor==0.6.2 bleach==1.5.0 click==6.7 Flask==0.12.2 gast==0.2.0 grpcio==1.10.0 gunicorn==19.7.1 h5py==2.7.1 html5lib==0.9999999 itsdangerous==0.24 Jinja2==2.10 Keras==2.1.5 Markdown==2.6.11 MarkupSafe  pillow==5.0.0 protobuf==3.5.2.post1 PyYAML==3.12 scipy==1.0.1 six==1.11.0 termcolor==1.1.0 Werkzeug==0.14.1 みたいなもの。 デプロイの際エラーが出た場合、まずこれを確認しましょう。 ここでエラーが出やすいです。 「Heroku デプロイ」などで検索すると、「次のコマンドを打ち込むだけでOK!」と書かれていることが多いです。 $ pip freeze > requirements.txt ですがこれは罠です。 上記コマンドは間違いではありませんが、打ち込むだけでOKではありません。 必ず中身を確認しましょう。 特にプログラミングの勉強を始めたての私のような人間は、様々なライブラリなどをインストールしていたりすると思います。 そういった、開発環境には一切関係のないものが入っていたりします。 それ自体は特に問題ないのですが、ものによってはHeroku上では使えず、エラーになってしまうものがあるので注意が必要です。 アプリ開発する際は必ず仮想環境を新たに作り、その上でやりましょう。 絶対その方が楽です。 Procfile とは Herokuプラットフォーム上でアプリを起動するためのもの。 下記内容のフォルダを作成します。 web: gunicorn --bind 0.0.0.0:$PORT ***:app ***はアプリ名を入れます。 app.pyならapp:app apple.pyならapple:app HerokuがProcfileを実行→Procfileが記載されているアプリを起動 という流れです。 また、gunicornをインストールしておく必要があるので忘れない様にしましょう。 pip install gunicorn 先程のrequirements.txtにもgunicornの記載が必要なので、気をつけましょう。 既にrequirements.txtを作成した場合は、gunicorn==19.7.1 (バージョンは最新のものにして下さい)とコピペして下さい。 runtime.txt とは Python でアプリ開発した場合、Heroku側にPythonのバージョンを送るために必要になります。 内容は下記の様に至ってシンプル。 python-3.6.9 改行や空白があるとエラーの原因になるので注意。 また、Herokuでサポートされているバージョンじゃないとエラーになるので、こちらで事前に確認しておきましょう。 以上、3つのファイルを追加したら、下準備は完了です。 ④HerokuにアプリをPush! 1、まずはHeroku上でアプリを作成します。 cdコマンドでアプリを保存したディレクトリに移動しておきましょう。 その後、Herokuにログインし、Heroku上でアプリを作成します。 アプリの作成はHerokuサイトからでも、コマンドからでも大丈夫です。 heroku login heroku create アプリ名 アプリを作成したら、Herokuサイトにログインし作成したアプリを確認しましょう。 作成されていたら、Deployの中にあるDeployment method欄からGitHubを選択し、GitHubを連携させましょう。 続いて、Settings内のBulidpacks欄で使用言語を指定します。 今回はPythonを使ったので、Pythonを指定しています。 2、gitリポジトリの生成をします。 リポジトリというのは、保存場所というか、保管場所というか。 GitHubは変更履歴などを保存・共有したりするのに使われるので、そういった履歴の入れ物を作るって事です。詳しくはググってみて下さい。 では、下記コマンドを入力しディレクトリをGitの管理下にします。 git init ターミナルにてls -aと入力すると、.gitというディレクトリが作られているのがわかると思います。これがGit管理されている証拠です。 3、次にリモートリポジトリを追加します。 heroku git:remote -a アプリ名(createで作った) Gitはリモートリポジトリとローカルリポジトリの2種類があります。 リモートリポジトリとは、専用のサーバに配置して他の人と共有したりするのに使われます。 4、gitの追跡対象にしてコミットする。 git add . git commit -m '(何を変更したかメッセージを書く)' git addでフォルダ内のファイルを追跡対象にして、コミット(処理確定)します。 メッセージはなんでもいいです'a'だけでもいいし、’First commit'とかでもいいです。 5、HerokuにアプリをPush! git push heroku master エラー表示がなければデプロイ完了です。 Herokuサイトにログインし、作成したアプリを選択。 Open appを選択し、表示されることを確認しましょう。 最後に 学習した内容を実際にやってみると、エラーになる事がよくあります。 気持ちが萎えることもありますが、それでいいのです。 実際にやってみてエラーが出て、そのエラーを解消して初めて理解に至るのです。 技術本を読むのも、学習サイトを使うのも大事ですが、実際にやってみてエラーにブチ当たり、それを解消した時が一番気持ち良いのです。 エラーの後には、脳汁ドバドバの快楽が待ってます。 エラーに負けず、根気よくカタカタしましょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MMPoseで骨格推定をやってみる(Google Colab)

はじめに 姿勢推定(骨格推定)の代表的なフレームワークであるMMPoseを使ってみて、日本語の記事が少ないなと思ったので、姿勢推定や骨格推定してみたい方の参考になればいいなと思って、記事を書こうと思いました。 姿勢推定(骨格推定)とは 上の画像を入力として与えたときに、下の画像のように物体(ここでは牛)の鼻や目、足などのキーポイントをそれぞれ特定し、適切な部分を線で結んだ画像を得るタスクになります。 対象としては上記画像のように動物もあるし、人間もあります。人間の場合は例えばテニスのサーブのスイング時の姿勢を推定し、プロと素人でどこが違うのかのコーチングなどに活かされています。 この姿勢(骨格)推定アルゴリズムとしては大きく ・トップダウン方式 ・ボトムアップ方式 の2つがあります。 トップダウン方式 画像の中の物体を特定(物体検知)し、特定した物体それぞれに対して骨格推定を行う方式 ボトムアップ方式 画像の中の物体全てにとりあえず骨格推定を行い、それぞれのキーポイントを同一物体同士で繋ぐ方式 精度としてはトップダウンの方が、一個一個の物体に対して骨格推定を行うので高い傾向にあります。 ボトムアップはキーポイントが推定できてもそれを同じ物体同士で繋ぐことを学習するのが難しいようです。 今回紹介するMMPoseの中のAnimalPoseというモデルは、トップダウン方式のモデルになります。 このトップダウン方式は物体検知と骨格推定がそれぞれ独立しています。 モデルの入出力の流れを説明すると以下のようになります。 画像を物体検知モデルに入力 ↓ 物体の位置を示したBBox座標を得る ↓ 画像とBBox座標を骨格推定モデルに入力 ↓ それぞれの物体のキーポイント座標を得る つまりモデルが物体検知モデルと骨格推定モデルの2つが必要になります。 MMPoseとは 骨格推定のモデルをたくさんまとめた汎用的なフレームワークのようなものになります。 公式ドキュメントも充実しており、様々なモデルを使用することができます。 ライセンスもApache-2.0 Licenseなので商用利用も可能です。 リポジトリ:https://github.com/open-mmlab/mmpose 公式ドキュメント:https://mmpose.readthedocs.io/en/latest/ HRNet on AnimalPose 今回紹介するのは、AnimalPoseというデータセットで学習をしたHRNetというモデルになります。 論文リンク:https://openaccess.thecvf.com/content_CVPR_2019/html/Sun_Deep_High-Resolution_Representation_Learning_for_Human_Pose_Estimation_CVPR_2019_paper.html 様々なスケールで学習をし、高解像度の持つ特徴量と低解像度の持つ特徴量を共有して骨格キーポイントを推定するモデルのようです。 導入・推論 MMPoseをローカル環境で動かすとなると、環境構築が結構めんどくさいです。 なので手軽に試してみたいという方は公式リポジトリのdemoノートブックをGoogle Colabで動かすのがいいでしょう。 デモノートブック https://colab.research.google.com/github/open-mmlab/mmpose/blob/main/demo/MMPose_Tutorial.ipynb これを上から順に実行すれば動いてくれます。 ただ注意点があって自分の場合は2つ目のセルを何回か実行しないとnumpyのバージョンが正しくならなかったです。 AnimalPoseを使用する場合は pose_config = 'configs/body/2d_kpt_sview_rgb_img/topdown_heatmap/coco/hrnet_w48_coco_256x192.py' pose_checkpoint = 'https://download.openmmlab.com/mmpose/top_down/hrnet/hrnet_w48_coco_256x192-b9e0b3ab_20200708.pth' 上記部分を pose_config = 'configs/animal/2d_kpt_sview_rgb_img/topdown_heatmap/animalpose/hrnet_w48_animalpose_256x256.py' pose_checkpoint = 'https://download.openmmlab.com/mmpose/animal/hrnet/hrnet_w48_animalpose_256x256-34644726_20210426.pth' に変更すれば大丈夫なはずです。 configとmodelファイルを変更するだけで色々なモデルの推論を試せます。 まとめ 簡単ですが、MMPoseの紹介です。学習済みのモデルを簡単に試せるのは非常に良いなと思いました。 次はローカルでの環境構築方法と物体検知モデルにYOLOv4を使用して動物の骨格推定を行うまでの流れを紹介したいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む