20210729のPythonに関する記事は28件です。

PythonでETロボコンシミュレータ(大会用)を動かしてみた。

ETロボコンシミュレータ(大会用)について ETロボコンは参加者向けに環境構築のためのリポジトリを公開しており、ここからETロボコンシミュレータ(大会用)をインストールすることができます。実は、参加者でない方でもサンプルコースだけが動くETロボコンシミュレータを体験することができます。作りかけのWEB版ETロボコンシミュレータと異なり、大会に必要な機能をすべて備えていますので、こちらのシミュレータをもっと活用できればいいのになぁ、と思う方もいらっしゃると思います。 今時C言語でロボコン? などと言われるのですが、以前はJavaで参加するチームもいましたし、ETロボコン2021では実はmrubyが公式に使えます。やる気になればどうにでもなる大会なのです。mrubyは実機でも動作することを意識し、TOPPERS、Athrillの環境をベースにしているため、これはこれでとても興味深い環境だと思っています。 MiniScriptとか知らんし。 WEB版ETロボコンシミュレータではMiniScriptを採用しましたが、これを勉強したところで役に立たないよね?という意見も多いかと思います。まぁ、私もそうかなと思いつつも、今のところこれ以上いい案が思いついていないだけなのです。sessionStorageとWebAssemblyを使えば何でもできそうな気がしなくはないですが、まだモチベーションが上がっていません。 実はETロボコンシミュレータ(大会用)はPythonでも動かせる。 ここからが本題です。ETロボコンシミュレータは組込みCPUシミュレータAthrillとUDPで通信しています。ここの通信フォーマットを理解しているならば、Athrillでなくとも制御ができてしまいます。(もちろん、今大会では利用できませんけど!) ということで理解している私がPythonクライアントを作りました。ETロボコンシミュレータを起動し適当なディレクトリで $ git clone https://github.com/YoshitakaAtarashi/ETroboSimController $ cd ETroboSimController $ python test_Motor.py とすると、 というような感じで走行体を動かすことができます。前提環境はPythonだけなのでインストール地獄にはならないはずです。Pythonは3.7.6以上で確認しています。ETroboSimServerがIO処理でブロックされないようasyncioを使っているため、古いPythonだと動かない可能性が高いです。 test_Motor.pyの解説 まず、EV3 C++ APIに似せたetrobosim.ev3api(as ev3)と、Athrillの代わりにETロボコンシミュレータを制御する etrobosim(as ets)をインポートします。 次に、各モータの初期化をします。この辺はWEB版ETロボコンシミュレータと違って、大会環境に似せようという過去の私の努力が見て取れます。 ets.Controllerで左コースか右コースかを選択できます。Python実装を見るとわかりますが、ここでUDPポートを切り替えています。controller.addHandlersは作成したデバイス(motor*)をここで追加することで、controllerの管理下に置くことができます。あとはお決まりのPWM値の設定です。 Ctrl+Cで停止できるように、KeyboardInterruptをキャッチしています。(これがなかなかうまく実装できなかった) import etrobosim.ev3api as ev3 import etrobosim as ets import time motorR=ev3.Motor(ev3.ePortM.PORT_B,True,ev3.MotorType.LARGE_MOTOR) motorL=ev3.Motor(ev3.ePortM.PORT_C,True,ev3.MotorType.LARGE_MOTOR) motorARM=ev3.Motor(ev3.ePortM.PORT_A,True,ev3.MotorType.MEDIUM_MOTOR) motorTAIL=ev3.Motor(ev3.ePortM.PORT_D,True,ev3.MotorType.LARGE_MOTOR) motorR.reset() motorL.reset() # 1秒毎に直進と回転を切り替える。 walker=[(50,50),(50,0)] wid=0 try: controller=ets.Controller(ets.Course.LEFT) controller.addHandlers([motorR,motorL,motorARM,motorTAIL]) controller.start(debug=False) while controller.isAlive(): motorR.setPWM(walker[wid][0]) motorL.setPWM(walker[wid][1]) wid=(wid+1)%len(walker) print("MotorR={},MotorL={}".format(motorR.getCount(),motorL.getCount())) time.sleep(1) controller.exit_process() except KeyboardInterrupt: controller.exit_process() raise test_LineTrace.pyによるライントレース もちろんライントレースもできます。colorSensorを追加している点と、pidControlを周期的に動かすためにcontroller.runCyclic(pidControl)を実行している点が異なります。組込みOSと違って精度よく周期実行させることはできませんが、まぁまぁいい感じに動くのでぜひ試してみてください。 import etrobosim.ev3api as ev3 import etrobosim as ets # ColorSensorのReflectを使ってP制御でライントレースする。 def calcPID(r, target=20, power=70,P=1.8): p=r-target left=power-P*p right=power+P*p return (int(left),int(right)) def pidControl(initARM_count=-50,initTAIL_count=0): left,right=calcPID(colorSensor.getBrightness()) motorL.setPWM(left) motorR.setPWM(right) motorARM.setPWM(initARM_count-motorARM.getCount()) motorTAIL.setPWM(initTAIL_count-motorTAIL.getCount()) #print("MotorR={},MotorL={},MotorARM={},Color={}".format(motorR.getCount(),motorL.getCount(),motorARM.getCount(),colorSensor.getBrightness())) motorR=ev3.Motor(ev3.ePortM.PORT_B,True,ev3.MotorType.LARGE_MOTOR) motorL=ev3.Motor(ev3.ePortM.PORT_C,True,ev3.MotorType.LARGE_MOTOR) motorARM=ev3.Motor(ev3.ePortM.PORT_A,True,ev3.MotorType.MEDIUM_MOTOR) motorTAIL=ev3.Motor(ev3.ePortM.PORT_D,True,ev3.MotorType.LARGE_MOTOR) motorR.reset() motorL.reset() colorSensor=ev3.ColorSensor(ev3.ePortS.PORT_2) try: controller=ets.Controller(ets.Course.LEFT) controller.addHandlers([motorR,motorL,motorARM,motorTAIL,colorSensor]) controller.start(debug=True) controller.runCyclic(pidControl) controller.exit_process() except KeyboardInterrupt: controller.exit_process() pass 最後に UDPの通信仕様だけ知ってればいいんだからPythonで簡単に作れると思いきや、同期周りで苦労して、組込みOS的な機能がやはり欲しくなりました。asyncioは今回初めて使ったのですが、ちゃんと動くようになるまではイライラしっぱなしでした。非同期処理ってホント難しいですね。 これの応用ですが、大会で採用するとしたら機械学習とか強化学習とかに使えるんじゃないですかね。学習データをPythonで取っておいて、大会用プログラムはAthrill対応の言語(C、C++、mrubyなど)とするのも面白そうです。来年はさすがにシミュレータ大会オンリーじゃなくなっててほしいですけど。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonライブラリを使用したグラフ作成色々

データ分析においてよく使うデータの可視化についてまとめます。自分用でもあります。 Plotlyについてはそのうち書こうと思います。ちょっと機能が多すぎてムズイので。。ただ3Dグラフで圧倒的に強いです。 使用ライブラリ 主にSeaborn、場合によってはMatplot、特別な事情でPlotly 簡単なやつから書いていきます。 Seabornを使った超簡単なグラフたち コードを別枠で書く必要すらないやつを最初にまとめておきます。 折れ線グラフ → sns.lineplot 棒グラフ → sns.barplot 散布図 → sns.scatterplot 箱ひげ図 → sns.boxplot ヒストグラム → sns.histplot などなど。 グラフによってはX軸を指定したり、Y軸を指定したり、hue=でカテゴリ別に色分けしたりできる。他にも色々引数はあるけどよくわかりません。 上記のグラフに簡単なコードで以下のことを追加できます。紹介するのは一部です。 下のグラフはIrisのがく片の長さを花の種類ごとに散布図にしたやつです。 plt.figure(figsize=(5,5)) #グラフ自体の大きさ plt.ylim(0,10) #Y軸の幅の変更 plt.title("AAAA!!!!",fontsize=20) # タイトル追加 plt.grid() # グリッド線追加 plt.xlabel(xlabel="iiii!!!!",fontsize=50) #Xラベルの追加 plt.xticks(fontsize=20)# X軸の文字の大きさ変更 sns.scatterplot(x="Species",y="SepalLengthCm",data=dataset) 出力結果はこんな感じ サブプロットを使ったデータ可視化の例 とりあえずどんなデータなのか見たいっていう初期段階で使ったりする。よく使うパターンは2つ。 パターン1.for文を使って一気にたくさんグラフを作るタイプ 下のコードではIrisデータセットを使用したもので、元のデータセットから特徴量を一つずつ抽出し、ターゲット(今回は花の種類)とすべての特徴量について一つずつグラフを作成してます。 最終的な出力はswarmplot(散布図の見やすい版)ですが、ここは大体なんでもいけます。 その他のグラフの要素、上からタイトル、グリッド、各ラベル、各グラフの値、凡例はグラフの見やすさとか好き嫌いに応じて変更。 最終行はレイアウトを見やすくするやつで、これをしないとグラフがつぶれがち。 import math feature_columns=dataset.columns fig = plt.figure(figsize=(10,10)) for i,j in enumerate(feature_columns): plt.subplot(math.ceil(len(feature_columns)/2), 2, i+1) plt.title(j,fontsize=20) plt.grid() plt.xlabel(xlabel=j,fontsize=20) plt.ylabel(ylabel=j,fontsize=20) plt.xticks(fontsize=12) plt.yticks(fontsize=12) plt.legend(loc="lower right",fontsize=15) sns.swarmplot(x=dataset["Species"],y=dataset[j]) plt.tight_layout() 出力結果はこんな感じ、Irisとかのちゃんとしたデータならとてもきれいに。 パターン2.事前にグループバイとかで条件分けした複数のデータセットからグラフを作るタイプ 下のコードは全く有名でないデータで、スーパーの時系列売上データを3つの支店に分けてグラフ化したものです。 まあ3つのデータを用意してサブプロットで並べているだけですね。 X軸は時間、Y軸は売上平均値ですね、hueを指定することで2つのカテゴリを同じグラフに書くことができます。 一番上の行でグラフの並べ方と数を指定しているので、ここをいじると横に並べたり4つのグラフを2×2で配置したりできます。サブプロットのお手本のような使い方な気がします。 上のグラフと同様、グラフの見栄えは適宜いじってもらって。 このコードもたぶんfor文で書けるんですけど、ちょっと僕の手には負えないですね。 fig,(ax1,ax2,ax3)=plt.subplots(3,1,sharey=True,figsize=(7,14)) sns.lineplot(x="Time",y="mean_Total",data=Mand_timesale,hue="weekday",ax=ax1) ax1.legend(loc="lower right",fontsize=20) ax1.grid() ax1.set_xticklabels(list(range(8,21,2)),fontsize=18) # ax1.set_yticklabels(list(range(0,800,100)),fontsize=18) ax1.set_xlabel(xlabel="Time",fontsize=1) ax1.set_ylabel(ylabel="Sales",fontsize=20) ax1.set_title("Mandalay Sales",fontsize=15) ax1.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0, fontsize=18) sns.lineplot(x="Time",y="mean_Total",data=Naypy_timesale,hue="weekday",ax=ax2) ax2.legend(loc="lower right",fontsize=20) ax2.grid() ax2.set_xticklabels(list(range(8,21,2)),fontsize=18) # ax2.set_yticklabels(list(range(0,800,100)),fontsize=18) ax2.set_xlabel(xlabel="Time",fontsize=1) ax2.set_ylabel(ylabel="Sales",fontsize=20) ax2.set_title("Naypyotaw Sales",fontsize=15) ax2.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0, fontsize=18) sns.lineplot(x="Time",y="mean_Total",data=Yango_timesale,hue="weekday",ax=ax3) ax3.legend(loc="lower right",fontsize=20) ax3.grid() ax3.set_xticklabels(list(range(8,21,2)),fontsize=18) # ax3.set_yticklabels(list(range(0,800,100)),fontsize=18) ax3.set_xlabel(xlabel="Time",fontsize=1) ax3.set_ylabel(ylabel="Sales",fontsize=20) ax3.set_title("Yangon Sales",fontsize=15) ax3.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0, fontsize=18) 出力結果はこんな感じ。マンダレー支店、ネピドー支店、ヤンゴン支店の平日・休日別時系列売上グラフとなります。 出力結果はわかりやすいんですけど、コードが長くてちょっとキモイですねー、なんとかしたい。 なんかいい感じのグラフ作成コードが分かり次第追記します。現状よく使うのはこのくらいなので、ここまで!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Django Settings.pyについて

Djangoのsettings.pyについて学習したのでまとめてみました。 間違ってるところがあれば指摘お願いします。 settings.pyについて settings.py BASE_DIR = Path(__file__).resolve().parent.parent HTMLファイルなどのパスを指定するときに 基準として用いられる。 BASE_DIR = manage.pyを入れてるフォルダのこと settings.py # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True これはTrueとFalseで設定可能で Trueだとブラウザにエラー文を詳細に記載してくれる。 またFalseだとあまり書かない。 True時 = 開発段階 False時 = 公開時 settings.py ALLOWED_HOSTS = [] HTTPのホスト・ヘッダー攻撃を防ぐための機能 コンテンツを配信するサーバー自身のホスト名を書く settings.py # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'django-insecure--!+%gh@#cf!b4hzr20wg+^0!e#i#x(0&ywrih7z8)cac*t2*t^' Djangoのセキュリティ あまり意識しなくて良い settings.py MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] 処理が行われる際に入って何か処理をしてくれるもの??? Ex)'django.contrib.sessions.middleware.SessionMiddleware' 上記はセッション機能の処理をしてくれる。 簡単なアプリの時はあまりいじらない settings.py ROOT_URLCONF = 'プロジェクト名.urls' リクエストが来たときに一番最初に受け取るurls.pyの指定 上記だとプロジェクトを管理しているフォルダ(プロジェクト名と同じ)のurls.py settings.py TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] HTMLファイルなどの静的ファイルがどこにあるかなどの指定をするところ settings.py WSGI_APPLICATION = 'プロジェクト名.wsgi.application' wsgi.pyがどこにあるか指定ししてる。インターフェースの指定 wsgiとは?? WebサーバとWebアプリケーションを接続するための、標準化されたインタフェース settings.py # Database # https://docs.djangoproject.com/en/3.2/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } } DBの接続先を設定 settings.py # Password validation # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] パスワード入力時に パスワードが短いですなどとエラーを出ろくしてくれる機能→バリデーション機能 バリデーション機能の管理をしているところ settings.py # Internationalization # https://docs.djangoproject.com/en/3.2/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True 時間や言語の設定するところ settings.py # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.2/howto/static-files/ STATIC_URL = '/static/' CSSファイルや画像ファイルなど静的ファイルをおいて置く場所指定するところ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonのstatsmodelsで時系列データをトレンド、季節性、残差に分解する

はじめに 指標の時系列変化をプロットして確認するとき、原系列をそのまま示すのではなく、トレンドと季節性の成分に分解した結果も合わせて示すと理解が深まります。 このトレンドと季節性の分解する方法はいくつかありますが、ここでは、おそらく最もシンプルなものの一つであるstatsmodelsのseasonal_decomposeがやっていることを説明します。これは、移動平均でトレンド成分を求め、トレンド除去後のデータから周期成分を求めるというシンプルな方法です。 statsmodelsのドキュメントにもある通り、これはナイーブな分解方法なので、もっと洗練された方法を使ったほうがよい場面も多いと思います。しかし、これを理解しておくことは、ほかの洗練された方法の理解の出発点にもなるはずです。この記事が、中のロジックをよく見たことがなかった人への一助となればと思います。 seasonal_decompose seasonal_decomposeでは、以下のステップで時系列データをトレンド成分と季節成分に分解します。 周期の長さで移動平均を求め、トレンド成分を求める トレンド除去後の時系列から季節性の成分を求める 残差を求める seasonal_decomposeのソースを参考に、自分でコードを書いてみました。以下で詳しく見ていきます。 データ 「トレンド+正弦波+正規分布のノイズ」で構成される時系列データを作ります。時系列全体の長さは50、周期は10とします。 # 季節性の周期は10 period = 10 #線形トレンド+正弦波+正規分布ノイズ np.random.seed(seed=20210) x_ts = [i + 5*np.sin(2*np.pi*i/period) + np.random.randn() for i in range(period*5)] plt.plot(x_ts) 移動平均でトレンド成分を求める 移動平均を求めます。移動平均には 中央移動平均:各時点とその前後の時点の平均値を使う 後方移動平均:各時点とそれ以前の時点の平均値を使う などがありますが、この記事では中央移動平均だけを扱います。 中央移動平均では、周期が偶数の時は注意が必要です。ある時点$i$の移動平均を求めるとき、この時点$i$の前後から周期-1個のデータを含めて平均する必要があります。周期が偶数の時、周期-1は奇数となり、そのままでは前と後ろで等分できません。そこで、前と後でそれぞれ周期/2個のデータをとり、ただし両端のデータの重みは$0.5$として移動平均をとります。 #両端のウェイトを0.5とする filt = np.array( [.5] + [1]*(period-1) + [.5] )/period print(filt) # 移動平均を求め、トレンド成分を求める x_trend = [] for _i in range(len(x_ts)): #print(_i) if _i <= int(period/2)-1 or _i >=len(x_ts) - int(period/2): x_trend.append(np.nan) else: _trend = 0 for _j in range(len(filt)): #print(_i,_j) _trend += x_ts[_i-int(period/2)+_j]*filt[_j] x_trend.append(_trend) x_trend = np.array(x_trend) x_trend 季節成分を求める まず、原系列から上で求めたトレンド成分を引き、トレンド除去の時系列データを求めます。 このトレンド除去後のデータから、周期ごとの平均値を求めます。(今の例だと、0,10,20,30,40時点の平均、1,11,21,31,41時点の平均・・)。ただし、周期成分の平均値は0となるように、全体の水準を設定します。 x_detrend = x_ts - x_trend #周期ごとの平均を求める。欠損は無視して平均を求める。 x_period_average = [ np.nanmean(x_detrend[i::period]) for i in range(period) ] #季節成分の平均は0となるようにする x_period_average -= np.mean(x_period_average, axis=0) 残差を求める 残差は原系列からトレンド成分、季節性の成分を引くことで求まります。 #残差 x_resid = x_ts - x_trend - x_seasonal x_resid seasonal_decomposeを使う 上で少々長くコードを書きましたが、statsmodels.tsaのseasonal_decomposeを使うと from statsmodels.tsa.seasonal import seasonal_decompose result = seasonal_decompose(x_ts, period=period, two_sided=True) の2行で済みます。結果はresult.trend、result.seasonal、result.residからそれぞれトレンド成分、季節性の成分、残差を取り出せます。 各成分に分解したプロットは以下のようになります。 移動平均を使っているため、両端には欠損が発生し、開始時点と終了時点ではトレンド成分と残差成分を描くことができません。これは、ナイーブな移動平均をつかう分解方法のデメリットの一つでしょう。ただし、何をやっているか非常にわかりやすいので、まず簡単に時系列データの傾向を確認するときなどには十分使えると思います。 洗練された手法としては、有名なSTL分解などがあります。この方法をpythonで実装する記事はいくつかありますので、興味ある方はご参照ください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

FGOの欲しい素材からクエスト周回数を求めるサイトを作った

スマートフォン向けRPG「Fate/Grand Order」のユーザーが使える、欲しいアイテムの個数を入力すると最適なクエストの周回数を求めるサイトを作りました。 欲しい素材の数を入力すると、どのフリクエを何周するのが最も効率的か教えてくれるサイトを作りました周回するクエストに迷ったら使ってみてください#FGOhttps://t.co/JXzZ8fmHmX— あんてな (@antenna_games) July 18, 2021 サイト フロントエンド API スクレイピング サービスの内容 入力フォームから最小化する対象、集めたいアイテムの数、対象に含めるクエストを入力します。 送信すると最適なクエストの周回数がわかります。 作った目的 フォロワーさんがこんなサイトが欲しい!とツイートしていたのをきっかけにモデル化・実装してみたら割と簡単にできたので、色んな人が簡単に使えるようにWebサイトとして公開してみようと思いました。 ビジネスロジック クエスト$q$​​​​、アイテム$i$​​​​に対して、ドロップ率を$p_{qi}$​​​​​、欲しいアイテムの個数を$n_i$​​​​とすると、合計が最小となるクエスト周回数$x_q$​​​​​は次の線形計画問題を解くことで求められます。 最小化: \quad\sum_{q}x_q 制約条件: \sum_{q} p_{qi} x_q \ge n_{i} \quad ( \forall i)\\ x_{q} \ge 0 \quad( \forall q) これをPythonの線形計画APIであるPuLPでモデル化します。まず、ドロップ率が次のような表で与えられているとします。 quest_name item_name drop_rate モントゴメリー 証 0.627 モントゴメリー 塵 0.154 シャーロット 証 0.481 シャーロット 塵 0.642 …… …… …… このとき、塵と証を100個ずつ集めるのに最適な周回数を求めるコードは次のようになります。 import csv import pulp import itertools import operator item_counts = {'塵': 100, '証': 100} with open('drop_rates.csv', 'r', newline='', encoding='utf-8') as f: reader = csv.DictReader(f) rows = [row for row in reader if row['item_name'] in item_counts] quests = set(row['quest_name'] for row in rows) ig = operator.itemgetter('item_name') groups = itertools.groupby(sorted(rows, key=ig), key=ig) #問題の作成 problem = pulp.LpProblem(sense=pulp.LpMinimize) #変数の作成 laps = pulp.LpVariable.dicts('lap', quests, lowBound=0) #目的関数の設定 problem += pulp.lpSum(laps.values()) #制約条件の設定 for item, rows in groups: problem += pulp.lpSum(float(row['drop_rate']) * laps[row['quest_name']] for row in rows) >= item_counts[item] #求解 problem.solve() for quest, lap in laps.items(): if pulp.value(lap) > 0: print(quest, round(pulp.value(lap))) これを実行すると次のような結果が得られます。 シャーロット 144 モントゴメリー 49 このように、PuLPでは問題を直感的にモデル化して簡単に解くことができます。 アーキテクチャ スクレイピング クエスト周回数の算出には各クエストのアイテムのドロップ率が必要となります。このデータは自前ではなく、FGOアイテム効率劇場という有志の方による統計データを利用しています。Googleスプレッドシートとして公開されているので、定期的に実行されるLambda関数からGoogle Sheets APIでドロップ率表を取得して、データを整理した後S3に保存しています。 API アイテム数からクエスト周回数を計算するLambda関数です。API Gatawayから呼び出されると、PuLPを使って線形計画問題をモデル化して解きます。 Lambda関数の構築にはAWS SAMを使用しています。YAMLを書くだけでデバッグ・デプロイやポリシー設定をやってくれるので便利です。 フロントエンド Next.js+TypeScriptで構築してVercelでホスティングしています。複雑なデータから静的・動的に生成する必要があり、SSG・SSR・CSRを使い分けられるNext.jsはとても便利でした。 スタイルには手軽に使えるNo-Class CSSフレームワークのMVP.cssとNext.jsに組み込まれているCSS in JSであるstyled-jsxを組み合わせています。 工夫した点 サーバーレス構成 最初はDjangoででも作って、自前の最低スペックのVPSにでも置いておこうかと思ったのですが、友人の助言でLambdaベースとしました。公開したら思ったより伸びて、APIコールが最大500回/時くらいになったのでサーバーレス構成の恩恵を感じました。 メンテナンスフリー クエストやアイテムはストーリーが進むごとに追加されますが、効率劇場からスクレイピングしたデータを元にバックエンド・フロントエンドともに自動的に生成されるので、効率劇場の形式が変わらなければ開発者側で対応することなく新クエストや新アイテムに対応できます。 レスポンシブデザイン スマートフォン向けゲームのユーザー向けということでモバイル端末からのアクセスが多いことが予想されたので、モバイル対応を意識しました。レスポンシブ化自体は簡単にできるのですが、画面に含める情報を必要十分に抑えたり、それぞれのコンポーネントを折りたためるようにしてページ内の移動をしやすくしたり、ラジオボタンやチェックボックスを一回り大きくしてタッチしやすくしたり、といったところに気を付けました。 Twitter共有 FGOはTwitterコミュニティが活発なので、結果をTwitterに共有できるようにTweet Web Intentを付けてみたのですが、ユースケースが可視化されて結構参考になりました。 引っかかった点 フロントエンドとバックエンドのすり合わせ フロントエンドをTypeScript、バックエンドをPythonで作っていますが、データやAPIの設計はきちんとすべきだと感じました。キャメルケースとスネークケースが入り混じったり、Pythonでは多用しがちな動的キーの辞書がTypeScriptだと扱いづらかったり……。 AWSの環境変数がVercelで予約されている フロントエンドのSSGのときにgetStaticPropsでS3バケットからアイテムやクエストのリストを取得しているのですが、AWSのAPIを叩くときには認証情報が必要です。Node.jsでは環境変数を使って認証するのが一番手っ取り早く、Vercelにもセキュアに環境変数を設定する機能があるので安心していたのですが、認証に使うAWS_ACESS_KEY_IDやAWS_SECRET_ACESS_KEYはVercelでは予約済みのため使えませんでした。認証情報をファイルとして置いておく方法もあるのですが、GitHubの公開リポジトリにリンクする形でデプロイしているので、S3の読み取り権限しか付与していないとはいえ認証情報を公開するわけにもいかないのでこの方法も使えませんでした。 最終的に環境変数名を衝突しないように変更して、プログラム側で読み取ってAWS SDKに直接渡すようにしましたが、これがベストプラクティスなのかはよくわかりません。 react-checkbox-treeが再現性なくクラッシュする 周回対象に含めるクエストを選択するためにreact-checkbox-treeを使用していました。 ツリー構造はセクション>エリア>クエストの3層になっています。ここで、S3にあるオブジェクトはクエストごとにセクションとエリアが書かれたフラットな表で、ツリー構造を作るときにセクション名やエリア名をキーにしてグループ化を行っていたのですが、キーとなるセクション名やエリア名に元のオブジェクトにはない文字コードが混入することがありました。こうなると同一のIDの枝が2つ生える事態となり、react-checkbox-treeがクラッシュしてしまいます。それだけならまだましなのですが、React16からコンポーネントがエラーを起こすとツリー全体がアンマウントされる仕様になっていたため、一部のユーザーの画面にはエラーメッセージが表示されるだけという事態になっていました。 一部のコンポーネントのエラーがDOM全体を巻き添えにする仕様についてはerror boundaryを設定して回避します。また、この問題に対する解決策として、グルーピングにセクション名やエリア名の代わりにIDを使用するようにしました。 まとめ FGOの欲しい素材からクエスト周回数を求めるサイトを作りました。AWSもReactも初めて触りましたが、AWS SAMとNext.jsが面倒な設定などはすべてやってくれるので割とすんなり作ることができました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ヒプマイ楽曲で1番多くでてくる言葉を調べてみた 一二三編

はじめに はじめまして。立夏と申します。 この間ヒプマイというコンテンツの番組の中で『楽曲に1番多く出てくる言葉は何か』を当てるコーナーがありました。 私はちょうどプログラミングの勉強中で「これって形態素解析を使って求めたのでは??」と思い練習として私もやってみました。初心者故至らぬ点が多いと思いますが暖かく見守っていただければ幸いです。 目的 伊弉冉一二三の楽曲に1番多く使われている言葉を調べる 準備 〈環境〉 Calaboratory 〈使用言語〉 python まずは形態素分析をしてくれるTokenizerをインストールします !pip install Janome==0.3.7 仕様が変わっているので今回は旧バージョンで進めていきます ▼必要なモジュールをインポートしておきます import glob from janome.tokenizer import Tokenizer from collections import Counter ▼歌詞について スクレイピングは歌詞だとできないかもということと担当パートを判断し抜き出すことは不可能だと思いソロ曲と全員曲の一二三の担当パートの歌詞をまとめたテキストを用意しました 今回は10曲を対象にしています (初心者故ここはアナログの方が早いと判断しましたがもっといい方法があったのかも…) 分析開始 ▼単語を取り出すための関数を設定します ヨコハマのときと同じで名詞、動詞、形容詞、形容動詞に限定しています 歌詞内の!や()も名詞として取り出していたのでそれらを除くコードと英語の大文字を小文字に直すコードを足しました # 品詞を取り出し「名詞、動詞、形容詞、形容動詞」のリスト作成 def tokenize(part): part = part.lower() t = Tokenizer() tokens = t.tokenize(part) words = [] for token in tokens: part_of_speech = token.part_of_speech.split(",")[0] if part_of_speech in ["名詞", "動詞", "形容詞", "形容動詞"]and not token.surface in ["!", "!!", "(", ")", "'", "?"] : words.append(token.surface) return words ▼歌詞を書いたテキストを読み込み先程の関数に適用します f = open("/content/drive/MyDrive/hihumi.txt", "r", encoding="utf-8") song = f.read() song = tokenize(song) これで単語を取り出せました ['oh', 'yeah', '持っ', 'こい', 'シャンパン', '終わら', 'party'・・]というようなリストになっています ▼それぞれの単語数を数え出力します #単語の出現回数をカウント counts = Counter(song) sorted(counts,key=counts.get,reverse=True) print(counts) 結果 上のコードを実行すると {'シャンパン': 28, '僕': 21, '終わら': 14, 'party': 14, '朝': 13, 'グイ': 10, 'こい': 9, '円': 9, 'さ': 9,・・ このような結果が出ました(長いので載せるのは途中までにしています) 「さ」が気になりますが概ねちゃんとした結果が出ていると思います まとめ 今回の結果上位をまとめると 1位 シャンパン 28回 2位 僕     21回 3位 終わら   14回 3位 party    14回 4位 朝     13回 となりました やはりシャンパンが1位ですね!ファンの方は予想通りだったのではないでしょうか? 複数曲で出てきている分ナゴヤの『伽』32回を越せるかと思っていましたが…空却くん強し 今後もたくさんシャンパンコールして欲しいですね 最後に 今回は1つのテキストを対象にしているので多少荒いコードになっていますが 他のキャラでも試すときは関数設定や特定の語句の削除などの部分を整えようと思います。 では、今回はここまで!閲覧ありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[python] math.floorのオーバーフローで詰まった話

概要 以下の問題にて、math.floorの過信から、なかなかACを通せなかったので、その過程をまとめました。 ABC048 B問題 解き方の経緯 B問題だったので、大体全探索だろうと思い、 片っ端から%を用いて余りの判定、aとbの範囲内か判定する という方法を考えましたが、a, b, xが10^18のオーダーなので、断念。 続いて、 $a <= x*i <= b$を満たすiを繰り返し処理で探していく という方法も考えましたが、xの大きさにより、こちらも計算量が膨大になる可能性があるため、断念。 最後に、 aとbをxで割り、その差によって、条件を満たす個数を出す(累積和的なイメージ) を考え、両端の処理だけ上手いことやれば、答えが出せということで実装していきました。 実装について やっと本題です。何も考えず、両端をmath.floorとmath.ceilを用いて導こうとしていました。 import math a, b, x = map(int, input().split()) # 条件の範囲の両端をそれぞれ求める left = math.ceil(a/x) right = math.floor(b/x) # 条件を満たす数字をrangeの個数で求める ans = len(range(left, right)) # 右端のみ、bがxで割り切れると個数が変わるので、判定 if b % x == 0: ans += 1 print(ans) しかし、以下の input.txt 1 1000000000000000000 3 が - 333333333333333311 (理想は全て3)となり、途方に暮れました。 ここで、色々と調べてみるとpythonのmath.floor/ceilではfloat型が用いられており、そのfloat型はCのdouble型を元に実装されていて、10進数だと上限が15桁ほどが限界らしい。 参考にしたpythonのドキュメント 参考にしたCの記事 ところがどっこい、pythonのint型には、上限がないため、精度の高い計算を行う場合は、できる限りint型を使った方が良いらしい。 ということで、math.floorから四則演算の//に変更した実装は以下です。 a, b, x = map(int, input().split()) # 値が切り捨てられている場合のみ、+1をする left = a // x if a % x != 0: left += 1 right = b // x ans = len(range(left, right+1)) print(ans) 結論 math.floor, math.ceilでは15桁くらいでオーバーフローが起きる。 (atcoderでは、これによってWAに…!!) int型には上限がないため、桁数が多い場合には、通常の四則演算を用いよう!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SIGNATEBEGGINERコンペまとめ

【第12回_Beginner限定コンペ】従業員の離職予測 はじめに ・本記事では2021年7月に開催されたSIGNATE Beginnerコンペに関して、どのようなタスク処理をしたかをまとめた記事となっている。 ・まとめの意義としては、自分自身の学習の為である。後学の為に、自分が行った処理やモデルの実装を残しておくこととする。 1.タスクと評価指標の分析 タスク:二値分類→従業員が離職するか否か テーブルデータ:1200/36カラム 評価指数:Accuracy(正答率)とerror rate 評価指数の話をする前に混合行列(confusion matrix)に触れる。 混合行列は評価指数ではないが、正例であるかを予測値とする評価指標でよく利用される。 予測が、正しいか誤りかによって以下の4つに分類される。 1.TP(True Positve、真陽性):予測値を正例として、その予測が正しい場合 2.TB(True Negative、真陰性):予測値を負例として、その予測が正しい場合 3.FP(False Positve、偽陽性):予測値を正例として、その予測が誤りの場合 4.FN(False Negatibe、偽陰性):予測値を負例として、その予測が誤りの場合 Accuracyは予測が正しい割合、error rateは誤っている割合を表す指標。 正解のレコード数を全てのレコード数で割ることで求められる。 $$Accuracy=\frac{TP+TN}{TP+FP+FN+TN}$$ $$erro rate = 1 - accuracy$$ 2.データの前処理と特徴量の作成 今回扱ったデータの中には欠損値が含まれていなかった為、
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】スプレッドシートをgspreadでDBっぽく使う世界線

本日もお疲れ様です。 皆さんはデータベースは好きですか? 僕は結構好きです。 なんかプログラマーっぽくて。(語彙力どうした) でもいざ使おうとするとお金がかかったり、扱いに慣れるまでちょっと時間がかかったり… そこで今回は 「安い(無料)」「早い(扱いやすい)」「美味い(?)」 でおなじみのスプレッドシートを巷でよく見るdbっぽく扱ってみたいと思います。 準備 まずはPythonからスプレッドシートにアクセスするために、権限周りの準備をします。 早速以下にアクセスしましょう。 アクセス後、「プロジェクトを作成」をクリックします。 クリック後、以下のような画面になるので「プロジェクト名」を記入して 「作成」をクリックします。 作成後、作成したプロジェクト画面に移動したいので「Google Cloud Platform」をクリックして ホーム画面っぽいとこに移動します。 移動後、先ほど作成したプロジェクト名が表示されているか確認します。 (されていなかったら▼ボタンで選択してください。) 確認後、ハンバーガーメニューから「APIとサービス」→「ライブラリ」をクリックします。 移動後、「Google Drive API」と「Google Sheets API」を探し出し、APIを有効化します。 クリックすると有効にするボタンがあるので、クリックしてAPIを有効化します。 次にキーを作成します。 ハンバーガーメニューから「APIとサービス」→「認証情報」をクリックします。 移動後、「認証情報を作成」→「サービスアカウント」をクリックします。 クリック後、サービスアカウントの作成画面に移動するので、「サービスアカウント名」を入力します。 (それ以外は省略しても大丈夫そうです。) 作成完了すると一覧に並ぶのでクリックします。 クリック後、「キー」→「新しい鍵を作成」をクリックします。 以下のような画面が表示されるので、JSONを選択し、作成をクリックします。 クリックすると、パソコンにJSONのキーが保存されます。 このJSONを開くと「client_email」という項目が記載されているので、 このアドレスを使用するスプレッドシートの「共有」に追加します。 (権限レベルは「編集者」にします。) 長かったですね。頑張りました いざ実装 実装する前にスプレッドシートの構成を晒します。 今回はRPGのキャラクターのデータベースが存在するシチュエーションでいろいろやってみます。 色とか書式とか自由に設定できるのがアツいですね。 データを取得する gsheet_test.py import gspread import os from oauth2client.service_account import ServiceAccountCredentials if __name__ == "__main__": # 認証処理 scope = ['https://spreadsheets.google.com/feeds', 'https://www.googleapis.com/auth/drive'] credentials = ServiceAccountCredentials.from_json_keyfile_name(os.path.join(os.getcwd(), "key.json"), scope) authorize = gspread.authorize(credentials) # 対象のシートにアクセスする target_data = authorize.open("db_character") sheet = target_data.worksheet("status") # シートの全情報を辞書型で取得 character_datas = sheet.get_all_records() for data in character_datas: print("------------------------") for key, value in data.items(): print(key, value) ServiceAccountCredentials.from_json_keyfile_name の第一引数では、 準備段階で作成したJSONのキーを渡しています。 では、これを実行してみましょう↓↓ ------------------------ name ゾンビ lv 5 hp 10 atk 5 def 3 agi 1 luk 0 ------------------------ name スケルトン lv 7 hp 8 atk 7 def 4 agi 3 luk 5 ~以下省略~ もうこの時点でキャラクターごとのステータスが取れてしまうのです。優秀。優勝。大喝采。 各キャラクターのデータが、辞書型で配列に格納されているようなイメージです。 character_datas[0]['name'] のようにすると、最初のキャラ(ゾンビ)の名前が取れますね。 データを追加する gsheet_test.py # 一部省略 # シートの全情報を辞書型で取得 character_datas = sheet.get_all_records() # ステータスの種類数を取得 status_names = sheet.get_all_values()[0] # 追加したい内容 add_data = {'name': 'gorilla', 'lv': 120, 'hp': 25000, 'atk': 50000, 'def': 30000, 'agi': 1, 'luk': 100} # 既存のキャラデータに追加 character_datas.append(add_data) # 既存キャラ+追加するキャラをこの範囲のセルに記載する range = sheet.range(2, 1, len(character_datas)+1, len(status_names)) # データを書いていく index = 0 for data in character_datas: for key, value in data.items(): range[index].value = value index += 1 # セルを更新して書き込んだ内容を反映する sheet.update_cells(range) スプレッドシートの範囲取得の値に注意しましょう。 (配列の先頭は0ですが、シートの始まりは1になります。) これを実行すると↓↓ gorillaが追加されました。 データを削除する 適当にゾンビさんを消してみましょう。 gsheet_test.py # 一部省略 # ゾンビを消してみる sheet.delete_row(2) 実行後↓↓ 一行書くだけで消せました。楽でいいですね。 データを変更する スライムくんの速さ(agi)があまりにも遅いので早くしてみましょう。 データの追加とほぼソースは同じです。 gsheet_test.py # 一部省略 # シートの全情報を辞書型で取得 character_datas = sheet.get_all_records() # ステータスの種類数を取得 status_names = sheet.get_all_values()[0] # スライムの速さを変更 character_datas[2]['agi'] = 7000 # 既存キャラ+追加するキャラをこの範囲のセルに記載する range = sheet.range(2, 1, len(character_datas)+1, len(status_names)) # データを書いていく index = 0 for data in character_datas: for key, value in data.items(): range[index].value = value index += 1 # セルを更新して書き込んだ内容を反映する sheet.update_cells(range) 実行後↓↓ スライムくんが爆速になりました。めでたい まとめ 準備に少し時間がかかりますが、割と扱いやすい印象でした。 スプレッドシートとPython(gspread)の扱いに慣れている方は是非一度お試しください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】DeepLを用いた自動翻訳をdiscord botに導入してみた

目標 PDFなどをコピペした英文を投げたら自動で改行調整をし、日本語訳を返すコードをPythonで書いたのでそれをdiscord botに導入してみる。 注意 seleniumが並列化できていないため、飽くまで個人用です。 (複数人で共有するとタブクラッシュするかも) メリット discord botに翻訳を返してもらえるメリットを挙げると -記録が残せる -Shaperを使うのと違ってタブが増殖しない 翻訳コード とりあえずコードを載せる。 translate.py import time from selenium import webdriver import chromedriver_binary from xml.sax.saxutils import unescape def deepl(text): text = ' '.join(text.splitlines()) options = webdriver.ChromeOptions() options.add_argument('--headless') url="https://www.deepl.com/ja/translator" driver =webdriver.Chrome(options=options) driver.get(url) insec="#dl_translator > div.lmt__text > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--source > div.lmt__textarea_container > div.lmt__inner_textarea_container > textarea" driver.find_element_by_css_selector(insec).send_keys(text) outsec="#target-dummydiv" while 1: Outputtext = driver.find_element_by_css_selector(outsec).get_attribute("innerHTML") if Outputtext != "\r\n" : break time.sleep(1) driver.quit Outputtext=Outputtext.rstrip("\r\n") Outputtext=unescape(Outputtext) return Outputtext 英文の改行コードを空白にする PDFをコピペすると勝手に改行されてイライラするのでとりあえず改行を消して半角空白にする。 text = ' '.join(text.splitlines()) seleniumを使ってDeeplにアクセス options = webdriver.ChromeOptions() #事前にimportしたchromeのwebdriverを使う options.add_argument('--headless') #実行の際Chromeを開かないよう指定 url="https://www.deepl.com/ja/translator" #DeepL(ja)のURL driver =webdriver.Chrome(options=options) #driverを指定 driver.get(url) #DeepLにアクセス webdriver.ChromeOptionsについては@hanzawak様のこの記事を参照するといいかも。 DeepLに英文を入れる 手法1 https://www.deepl.com/translator#en/ja/英文 にアクセスすることで翻訳を行う。 問題点 例えば文章中にX/Yなどがあるとする。そしたらURLにアクセスする際これをスラッシュと認識して英文が/で途切れてしまう。(%2Fでも同じだった) 手法2 DeepLのサイト内で入力を行う。 まずDeepL内で文入力のsectorを探した。すると #dl_translator > div.lmt__text > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--source > div.lmt__textarea_container > div.lmt__inner_textarea_container > textarea にあることが分かった。 ここをfindしてtextを入れる。 insec="#dl_translator > div.lmt__text > div.lmt__sides_container > div.lmt__side_container.lmt__side_container--source > div.lmt__textarea_container > div.lmt__inner_textarea_container > textarea" driver.find_element_by_css_selector(insec).send_keys(text) 訳文を抽出する 上と同様にtextareaを見つけて抽出しようとしたができなかった。 そのため別のところを探すと #target-dummydiv のinnerHTMLにあることが分かった。 あとはここをfindしてinnerHTMLを取り出す。 ここで翻訳が終わっていないと"\r\n"が取り出されるため、それ以外のものが取れるまで1秒ごとに操作を行う。 outsec="#target-dummydiv" while 1: Outputtext = driver.find_element_by_css_selector(outsec).get_attribute("innerHTML") if Outputtext != "\r\n" : break time.sleep(1) driver.quit 最後に末尾の"\r\n"を取って訳文Outputtextを獲得...と行きたいところだが、ここでHTML上では特殊文字について、例えば">"は"&gt ;"で書かれているからそれを普通の記号に変換するためにunescapeに通す。 Outputtext=Outputtext.rstrip("\r\n") Outputtext=unescape(Outputtext) return Outputtext これで翻訳については完了。 あとはこれをBOTに入れるだけである。 BOT作成 BOTについては基本的な動作を行うだけなため@1ntegrale9様のこの記事を参考にするといいかも。 またseleniumとchromedriver_binaryをHerokuで使う際、Setting->Buildpacksに以下の二つのURLを入れることに注意。 https://github.com/heroku/heroku-buildpack-google-chrome.git https://github.com/heroku/heroku-buildpack-chromedriver.git deeplbot.py import translate.py import discord import asyncio TOKEN = '自分のtokenを入れてね' client = discord.Client() @client.event async def on_message(message): author=message.author dm=await author.create_dm() if message.channel==dm: honyaku = translate.deepl(message.content) await message.channel.send(honyaku) client.run(TOKEN) 関数内を簡単に説明すると上から メッセージ(英文)が送ると まず送ってきた人をauthorとする。 そしてその人とのDMをdmとする。 そしてその人の送ってきた文章がDMからなら実行し、それ以外なら実行しない(大量のグループ内で起動すると迷惑になりそうなので) あとはtranslate.py内の翻訳関数に通して出力をDMに再び返す。 といった感じである。 async def on_message(message): author=message.author #メッセージの送り主を特定 dm=await author.create_dm() #送り主のDMを特定 if message.channel==dm: #送ってきたチャンネルがDMか判断 if __name__ == '__main__': honyaku = deepl2.deepl(message.content) #翻訳関数実行 await message.channel.send(honyaku) #送り主のDMに返す 実行してみた 15秒ぐらいで返ってきた。(まぁ妥協点?)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

全方位木dpをpythonで

全方位木dpとは ある頂点についての何かしらの通り数はDFSによる木dpで$O(N)$で計算できるとき、その通り数を全頂点について求めるdpです。 愚直にDFSすると当然$O(N^2)$となりますが、1つの頂点に対するDFSの結果を再利用することにより、$O(N)$の計算量を達成します。 概念は理解できたものの、他の方々のコードを読んでも全く理解できずにドはまりしたので、コメント多めのものを以下に記載します。 Educational DP Contest / DP まとめコンテスト V - Subtree 用いている関数の用途は以下の通りです。 ・dfs1 : 頂点0を根とする木dp ・dfs2 : ある辺についてdfs1で調べた方向(順方向とよぶ)と逆方向にdp dfs1ではdp1を、dfs2ではdp2を更新していきますが、重要なのは「dp1[cu]はcuを黒く塗る通り数を、dp2[cu]はcu*以前*を黒く塗る通り数を表している」ということです。 これを理解するために相当の時間を溶かしました。 import sys sys.setrecursionlimit(10**6) N,M=map(int,input().split()) xy=[[int(i) for i in input().split()] for _ in range(N-1)] G=[[] for _ in range(N)] for x,y in xy: x-=1; y-=1 G[x].append(y) G[y].append(x) #順方向のdp #dp1[cu] : 順方向でcuを黒く塗る通り数 dp1=[1]*N def dfs1(cu, p=-1): for to in G[cu]: if to==p: continue dfs1(to,cu) dp1[cu]*=dp1[to]+1 dp1[cu]%=M ans=[-1]*N #逆方向のdp #dp2[cu] : 逆方向でcu「以前」を黒く塗る通り数 dp2=[1]*N def dfs2(cu, p=-1): #dp1の左右からの累積配列を計算 acc_l=[1]*len(G[cu]) acc_r=[1]*len(G[cu]) for i in range(len(G[cu])): to = G[cu][i] if i-1>=0: acc_l[i]=acc_l[i-1] if to==p: continue acc_l[i]*=dp1[to]+1 acc_l[i]%=M for i in range(len(G[cu])-1, -1, -1): to = G[cu][i] if i+1<len(G[cu]): acc_r[i]=acc_r[i+1] if to==p: continue acc_r[i]*=dp1[to]+1 acc_r[i]%=M #逆方向のdp for i in range(len(G[cu])): to = G[cu][i] if to==p: continue tmp=1 if i-1>=0: tmp*=acc_l[i-1] tmp%=M if i+1<len(G[cu]): tmp*=acc_r[i+1] tmp%=M dp2[to]=(dp2[cu]*tmp+1)%M dfs2(to,cu) #ans[cu]=(p以外からのdp1)*(p(=cu以前)からのdp2) ans[cu]=1 if len(acc_r): ans[cu]=acc_r[0] if p>-1: ans[cu]*=dp2[cu] ans[cu]%=M dfs1(0) dfs2(0) for a in ans: print(a)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ロジスティック回帰 solverについて

はじめに 前回ロジスティック回帰の正則化のパラメーターについて調べた。 今回はその続きでsolver(最適化関数)について 前回 : https://qiita.com/hannnari0918/items/407e4fe6c0077e34ccab 公式doc solverの種類 公式doc https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html#sklearn.linear_model.LogisticRegression solver : {‘newton-cg’, ‘lbfgs’, ‘liblinear’, ‘sag’, ‘saga’}, default=’lbfgs’ Algorithm to use in the optimization problem. For small datasets, ‘liblinear’ is a good choice, whereas ‘sag’ and ‘saga’ are faster for large ones. For multiclass problems, only ‘newton-cg’, ‘sag’, ‘saga’ and ‘lbfgs’ handle multinomial loss; ‘liblinear’ is - limited to one-versus-rest schemes. ‘newton-cg’, ‘lbfgs’, ‘sag’ and ‘saga’ handle L2 or no penalty ‘liblinear’ and ‘saga’ also handle L1 penalty ‘saga’ also supports ‘elasticnet’ penalty ‘liblinear’ does not support setting penalty='none' Note that ‘sag’ and ‘saga’ fast convergence is only guaranteed on features with approximately the same scale. You can preprocess the data with a scaler from sklearn.preprocessing. solver 詳細 使用可能 newton-cg ニュートン共役勾配法 L2, none lbfgs L-BFGS法 L2, none liblinear liblinearというライブラリ 座標降下法小さなデータセットだと早い L1, sag(Stochastic Average Gradient ) 確率勾配降下法(SAG)の進化系大きなデータセットだと早い L2, none saga 確率勾配降下法(SAG)の進化系大きなデータセットだと早い L1, L2, elasticnet, none Newton_cg(ニュートン共役勾配法) ニュートン法について 上記グラフの時x軸との交点αを求めたいという時に使うもの方法 最適化問題でグラフが複雑になったときにどんなグラフでも近似的に答えを見つけられるという関数 上記グラフの交点に対して接線の方程式を使い $$y-f(x_n) = f'(x_n)(x-x_n)$$ が成立する。この式を式変形することで $$x_{n+1}=x_n-\frac{f(x)}{f'(x)}$$ が成立し、この式はxだけで解けるので無限回行うことでαが求められるというもの。 共益勾配法について この方の記事を参考にしてください : https://qiita.com/Dason08/items/27559e192a6a977dd5e5 イメージで言うと、勾配降下法で今いる地点の勾配から徐々にゴールを目指していたのを、ゴールへの向きを固定し勾配からベクトル力を決めてしまうというもの。 理論上、向きと力が100%あっていれば一発で最適化ができるよねっていう手法。 またずれていたとしても、その向きから垂直方向に調整を行うため一度調整したベクトルは変更されない。 (垂直ベクトルは0になるため) ふたつを組み合わせると ニュートン法や勾配降下法の弱点であった鞍点で止まってしまうことを防げるらしい ベクトルでの調整になるのでL2ノルムでしか使えない。 lbfgs法 ニュートン法の弱点である微分をなくした最適化手法 メモリ(memoly)に優しくb,f,g,sパラメータを使うためこの名前が付けられている。 ニュートン法は次の値(n+1)を求めるののに微分を用いる。 微分によりヘッセ行列が作成される。 例えば $$5x^2+3xy+6y^2=4$$ のヘッセ行列Hは(x^2,xy,y^2で微分) $$ \begin{pmatrix} 10 & 3 \\ 3 & 12 \end{pmatrix} $$ となる。この値が特徴量の2乗の値になるため、膨大なメモリを食ってしまうのを防ぐための手法。 ヘッセ行列を使わず、H^-1に近い行列Bを仮定し、この値を求めるやり方。 Bの求め方は省略するが、各方向への勾配gと移動量sを用いて算出できる。 またニュートン法のようにハイパーパラメータが存在せず、振動することがないのが特徴。 liblinear オープンソースの機械学習ライブラリらしい。 公式https://www.csie.ntu.edu.tw/~cjlin/liblinear/ 座標降下法をもちいてロジスティック回帰を行なっているものと、線形SVMをもちいて学習するものがある。 scikit-learnがどちらを使っているかは不明。 座標降下法について イメージでいうと各特徴量にたいして順番に更新していく仕組み。 2次元だと考えると $$x_1 , y_1 , x_2, y_2, x_3 ...$$ 3次元ならば $$x_1, y_1, z_1, x_2, y_2, z_2, x_3...$$ のように、求めたい変数以外を定数と仮定して解く方法。 メリット - 特徴量を一つづつ更新していくため、メモリ容量に優しい。 - 超大規模データ等で使用できる - 一度に更新するのは一つのパラメーターなので、そこまで計算に時間がかからないこと。 デメリット - スパース問題(特徴がまんべんなくではなく、ある特徴量の比率が大きい)に使用できない。 - 最適解に精密さを必要と場合、使えない。 本題 公式docには小さなデータセットでいい選択だと記載されているため、SVMをもちいた最適化方法なのかもしれない。 他クラス分類には向かず、二値分類用に作成されている。そのため、非常に高速に学習する。 l2で使えないのはおそらくL1にしないと、そもそも超平面が作成されないから。 (L2だと合計ベクトルになるため) noneで使用できない理由はわからない笑 SAG,SAGAについて SGD確率的勾配降下法の進化系 ではSGDとはなんぞやという話になるので... 最急降下法 ロジスティック回帰のときに記載した、損失関数を少なくする方法の一つ。 元祖最適化関数といったイメージ。 何をしているかというと、今いる地点の勾配を求めそれを元にして次の地点に進む方法。 2次元で考えた場合、1000個のデータセットがあった場合全てを用いて各次元に対する重みを決定する。 特徴として、 全部のデータを用いて計算するため、パラレルで計算が可能 重みは決めれるが、下がるか上がるかは微分によって決まる。 どれくらい下がるかはハイパーパラメータ 欠点として、全ての特徴量とデータを用いて計算を行うため時間がかかることと鞍点から脱出できないことが挙げられる。 (イメージでいうと全てのデータを用いるので優秀なベクトルが定められるので、幸か不幸か収束早く大きく動かない) SGD(確率的勾配降下法) 最急降下法の後続としてできた最適化手法。 最急降下法がなんぞやとなるので唐突に上で説明した通り。 SGDがなにをしているかというと、最急降下法が全てのデータを用いてベクトルを決めていたのに対してランダムに選ばれた一つのデータを使ってベクトルを決めてしまうやり方。 はじめて聞いたとき劣化版にしか聞こえない 全データで学んだ方が確実に効率的なのに、なぜわざわざ1データのみでデータに寄与する変な方向のベクトルを見つけるのか。 変な方向のベクトルを使いたいから! なぜそんなことをするのかというと、先の述べた鞍点の問題を解決してくれるからである。 詳しくいうと 全部のデータを用いた最高の学習方法では鞍点から抜けれなくなったのをその変なむきのベクトルを使うことで脱出できるからという理由である。 SAG sag(Stochastic Average Gradient)という名前の通り、平均をとって勾配を決めるやり方。 根本的にはSGDとやり方は変わらないが、今までの計算してきた回数と勾配を保存しておき無限回行うことで最急降下法と同じように前データで勾配計算を行う手法となる。 勾配計算を行うやり方が一括ではなく、ランダムな順序のデータで学習するので鞍点に陥らなくて済むよね!といった話である。 勾配平均の求め方は $$\bar{w_n} = \frac{1}{n}\sum_{n}{w_t}$$ n+1(データを一つ加えた勾配) $$\bar{w_{n+1}} = \frac{n}{n+1}\bar{w_t} + \frac{1}{n+1}w_{t+1}$$ 以上の式で求められる。 自分の感覚だとnが十分に大きくなるとほぼほぼ更新されないので、鞍点から抜け出せるかは運か反転回数が大事になってきそうに見える。 SAGA SAGとほぼほぼ変わらないが平均を取らない計算方法になる nで割らない計算式 (https://www.msi.co.jp/nuopt/glossary/term_da265770bed70e5f0a764f3d20c0ce3d242e6467.html)[https://www.msi.co.jp/nuopt/glossary/term_da265770bed70e5f0a764f3d20c0ce3d242e6467.html] 終わりに 手法をなんとなくだが、イメージできるようになった。 自分が思い返したときにわかりやすいように記載しただけなので、他人がみても多分わからないと思われる笑 まだまだ信じられないくらいの量の最適化手法ががあるので、暇があれば探していきたい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

トランプカードを並び替え後、任意のカードを探す

<アルゴリズム問題シリーズ(Python)> 1.はじめに 同一ファイル内で入力から出力を実施する方法としてはhttps://qiita.com/ajim/items/4d350710ba70056f5f6f を参考にしました。 import sys import io _INPUT = """\ 5 4 1 3 4 5 """ sys.stdin = io.StringIO(_INPUT) ・sysモジュールはインタプリタで使用・管理している変数や、インタプリタの動作に深く関連する関数を定義しています。sys.stdinはインタプリタが使用する、それぞれ標準入力、標準出力、および標準エラー出力の ファイルオブジェクト であり、stdin は (input() の呼び出しも含む) すべての対話型入力に使われます。https://docs.python.org/ja/3/library/sys.html ・io モジュールは様々な種類の I/O を扱う Python の主要な機能を提供しています。StringIO オブジェクトはインメモリーのテキストストリームです。 io.StringIO(initial_value='', newline='\n')のように使用し、バッファの初期値を initial_value で与えることが出来ます。改行変換を有効にすると、改行コードは write() によってエンコードされます。ストリームはバッファの開始位置に配置されます。https://docs.python.org/ja/3/library/io.html?highlight=io#io.StringIO 2.問題 ・N枚のカードが重ねられていて、上からt番目のカードにはtと書かれている。 ・t回目の操作として「一番上のカードを取り除き、それを上からt番目にいれる」を行い、これをN回実施する。 ・上の操作後、t個目の質問として「M_tが書かれたカードは上から何番目?」が聞かれ、その答えをA_tとする。これがK回実施される。 ・N,K,Mは全て整数であり、1 <= M_t <= Nである。 この時、K個の質問の答え(A_1...A_K)をすべて出力として表示するプログラムを書け。 入力方式はsys.stdinとする。 入力 N K M_1 . . M_K 出力 A_1 . . A_K 例 入力 5 4 1 3 4 5 出力 3 5 2 4 3.解答 N,K = map(int,sys.stdin.readline().split()) lines = [] for i in sys.stdin: lines.append(i.rstrip('\r\n')) lines_int = list((map(int,lines))) cards = [] for i in range(1,N+1): cards.append(i) for i in range(N): del_card = cards.pop(0) cards.insert(i, del_card) for i in range(K): print(cards.index(lines_int[i]) + 1) 4.実行例 import sys import io _INPUT = """\ #入力例 10000 10 1 2 3 4 45 543 756 4657 7333 9999 """ sys.stdin = io.StringIO(_INPUT) 出力 6384 #1万回の操作後、1と書かれたカードが上から6384番目に存在する 2288 240 4336 1392 7360 2088 8626 4665 9997 5.メモ リストから要素を削除する方法 1.del リスト[インデックス] / del リスト[開始インデックス:終了インデックス] 2.リスト[開始インデックス:終了インデックス] = [] 3.リスト.pop() / リスト.pop(インデックス) 4.リスト.remove(値)  5.リスト.clear()  https://www.javadrive.jp/python/list/index8.html リストに要素を追加する方法 1.リスト.append([6, 7]) # 要素[6, 7]を末尾に追加 2.リスト.extend([4, 5]) # リストの要素を末尾に追加   リスト.extend(6) # TypeError 3.extendメソッドではリスト以外の反復可能オブジェクトもリスト末尾に追加できる。リスト.extend((3, 4)) # タプルの要素がリスト末尾に追加される 4.リスト.insert(5, [3.25, 3.5]) # 第2引数に指定した値は単一の要素として挿入される。  print(リスト) # [0, 1, 2, 2.5, 3, [3.25, 3.5], 4] https://www.atmarkit.co.jp/ait/articles/2012/11/news015.html 6.参考文献 7.ご意見・ご感想をお待ちしております 当方、未熟なプログラマーのため、よりよいコード等ありましたら教えていただけると幸いです。 皆様のメッセージをお待ちしております。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

トランプカードを並び替えた後に任意のカードを探す

<アルゴリズム問題シリーズ(Python)> 1.はじめに 同一ファイル内で入力から出力を実施する方法としてはhttps://qiita.com/ajim/items/4d350710ba70056f5f6f を参考にしました。 import sys import io _INPUT = """\ 5 4 1 3 4 5 """ sys.stdin = io.StringIO(_INPUT) ・sysモジュールはインタプリタで使用・管理している変数や、インタプリタの動作に深く関連する関数を定義しています。sys.stdinはインタプリタが使用する、それぞれ標準入力、標準出力、および標準エラー出力の ファイルオブジェクト であり、stdin は (input() の呼び出しも含む) すべての対話型入力に使われます。https://docs.python.org/ja/3/library/sys.html ・io モジュールは様々な種類の I/O を扱う Python の主要な機能を提供しています。StringIO オブジェクトはインメモリーのテキストストリームです。 io.StringIO(initial_value='', newline='\n')のように使用し、バッファの初期値を initial_value で与えることが出来ます。改行変換を有効にすると、改行コードは write() によってエンコードされます。ストリームはバッファの開始位置に配置されます。https://docs.python.org/ja/3/library/io.html?highlight=io#io.StringIO 2.問題 ・N枚のカードが重ねられていて、上からt番目のカードにはtと書かれている。 ・t回目の操作として「一番上のカードを取り除き、それを上からt番目にいれる」を行い、これをN回実施する。 ・上の操作後、t個目の質問として「M_tが書かれたカードは上から何番目?」が聞かれ、その答えをA_tとする。これがK回実施される。 ・N,K,Mは全て整数であり、1 <= M_t <= Nである。 この時、K個の質問の答え(A_1...A_K)をすべて出力として表示するプログラムを書け。 入力方式はsys.stdinとする。 入力 N K M_1 . . M_K 出力 A_1 . . A_K 例 入力 5 4 1 3 4 5 出力 3 5 2 4 3.解答 N,K = map(int,sys.stdin.readline().split()) lines = [] for i in sys.stdin: lines.append(i.rstrip('\r\n')) lines_int = list((map(int,lines))) cards = [] for i in range(1,N+1): cards.append(i) for i in range(N): del_card = cards.pop(0) cards.insert(i, del_card) for i in range(K): print(cards.index(lines_int[i]) + 1) 4.実行例 import sys import io _INPUT = """\ #入力例 10000 10 1 2 3 4 45 543 756 4657 7333 9999 """ sys.stdin = io.StringIO(_INPUT) 出力 6384 #1万回の操作後、1と書かれたカードが上から6384番目に存在する 2288 240 4336 1392 7360 2088 8626 4665 9997 5.メモ リストから要素を削除する方法 1.del リスト[インデックス] / del リスト[開始インデックス:終了インデックス] 2.リスト[開始インデックス:終了インデックス] = [] 3.リスト.pop() / リスト.pop(インデックス) 4.リスト.remove(値)  5.リスト.clear()  https://www.javadrive.jp/python/list/index8.html リストに要素を追加する方法 1.リスト.append([6, 7]) # 要素[6, 7]を末尾に追加 2.リスト.extend([4, 5]) # リストの要素を末尾に追加   リスト.extend(6) # TypeError 3.extendメソッドではリスト以外の反復可能オブジェクトもリスト末尾に追加できる。リスト.extend((3, 4)) # タプルの要素がリスト末尾に追加される 4.リスト.insert(5, [3.25, 3.5]) # 第2引数に指定した値は単一の要素として挿入される。  print(リスト) # [0, 1, 2, 2.5, 3, [3.25, 3.5], 4] https://www.atmarkit.co.jp/ait/articles/2012/11/news015.html 6.参考文献 7.ご意見・ご感想をお待ちしております 当方、未熟なプログラマーのため、よりよいコード等ありましたら教えていただけると幸いです。 皆様のメッセージをお待ちしております。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

フーリエ変換結果に対するアプローチまとめ(Python)

はじめに pythonでライブラリを用いてフーリエ変換を行うと、実数部と虚数部を持つ周波数関数が得られると思います。 その値に対するアプローチ各種をpythonコードと結び付けてまとめます。 周波数関数 numpyを用いてフーリエ変換を行うと、以下のようなNumpy配列が返ってきます。 # FFT変換(fは一次元のシンプルな波形) F = np.fft.fft(f) print(F) [-1.38777878e-14+0.0000000e+00j 2.53849664e-15+3.7500000e+00j 4.40039726e-15-8.8817842e-16j 4.40039726e-15+8.8817842e-16j 2.53849664e-15-3.7500000e+00j] これを周波数関数と言い、以下の数式で表現できます。 F(ω)=Re F(ω)+j Im F(ω) 下の図は上式の関係を複素平面上に示したものです 各種数式とコードの対応 1.絶対値 |F(ω)|=\sqrt{Re F(ω)^2 + Im F(ω)^2} F_abs = np.abs(F) print(F_abs) array([1.38777878e-14, 3.75000000e+00, 4.48913766e-15, 4.48913766e-15,3.75000000e+00]) 2.実数部 Re F(ω) F_real = np.real(F) print(F_real) array([-1.38777878e-14, 2.53849664e-15, 4.40039726e-15, 4.40039726e-15, 2.53849664e-15]) 3.虚数部 Im F(ω) F_img = np.img(F) print(F_img) array([ 0.0000000e+00, 3.7500000e+00, -8.8817842e-16, 8.8817842e-16, -3.7500000e+00]) 4.偏角 ∠ F(ω)=\arctan \frac{Im F(ω)}{Re F(ω)} F_angle = np.angle(F) print(F_angle) array([ 3.14159265, 1.57079633, -0.19916465, 0.19916465, -1.57079633]) さいごに 絶対値が使われることが多いのですが、特徴量の候補として残りの3つも挙げられることを知り、まとめました。 参考 F(ω) の大きさと位相について
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【IQ Bot】カスタムロジック:選択肢の中からもっとも近いものを答えとする(fuzzy match)

このページで解説すること IQ Botで、正解の選択肢が決まっている読取項目に対して、「OCRの読み取り結果を選択肢の中で最も近いものに寄せたい!」という場合に使えるロジックです なお、このページに書いてある方法が使えるのは、Automation360.21以降に付随するIQ Botのみです。 こんなときに便利 例えば「決済方法」という項目で、正解が「代引」「銀行振込」「クレジット」のいずれかに決まっているとします。 OCRの読み取り結果は、正解を正しく読めるかもしれないし、「クレジツト」(ツが大文字)などのように微妙に誤読をするかもしれない。 でも、「クレジツト」は3つの正解の中では明らかに「クレジット」に近いので、結果をそれに寄せたい! というときに使えます。 ズバリこうやります フィールド項目の場合は、ズバリこうやります。 選択肢の中から最も近いものを答えとする import IQBotHelper as iq matchList = ("代引","銀行振込","クレジット") #カスタム部分① maxRatio = 0 currentRatio = 0 result = "" for i in matchList: currentRatio = iq.fuzzy_match (field_value, i, ratio = True) if currentRatio >= 0.5: #カスタム部分② if currentRatio > maxRatio: result = i field_value = result コメントで記載した「カスタム部分」を読みたい条件に合わせて変えればできあがりです。変え方は後述します。 テーブルの場合は、上記を関数化して項目に適用するだけなのですが、そちらのチートシートも機会があれば作ります。 本当はもう少し解説を深掘りするべき部分があるとわかっているのですが(IQBotHelperってなんやねんとか)、 カスタム部分の解説 カスタム部分①:選択肢 カスタム部分その1は、以下の部分です。 カスタム部分① 選択肢 matchList = ("代引","銀行振込","クレジット") #カスタム部分① matchListの中身を、正解の選択肢に変えてあげます。数は自由です。 「りんご」「バナナ」「みかん」「ぶどう」が選択肢なら、こんな感じにします。 カスタム部分① 変更例 matchList = ("りんご","バナナ","みかん","ぶどう") #カスタム部分① カスタム部分②:閾値 続いてのカスタム部分は、ここの3行目です。 カスタム部分② 閾値 for i in matchList: currentRatio = iq.fuzzy_match (field_value, i, ratio = True) if currentRatio >= 0.5: #カスタム部分② if currentRatio > maxRatio: result = i 「currentRatio >= 0.5」の「0.5」の部分には、ファジーマッチの結果の閾値を指定します。 いくら「もっとも近いものを答えとする」といっても、「クリケット」を「クレジット」に補正するくらいならいいけど、「オーストラリアット」を「クレジット」に補正されるのはどうもなぁ(最後の「ット」が共通しているとはいえ)……みたいなときに使います。 上記のように、「たとえ選択肢の中では一番近くても、一定の確信度を満たしていなければ空欄にした方がマシ」みたいな場合は、そのマッチ度を設定します。 「0.5」は50%なので、マッチ度が50%を超えていて、なおかつ選択肢の中でいちばん近いものがIQ Botの結果として出て来ます。 この場合、選択肢のどれとも50%以上のマッチ度でマッチしなければ、IQ Botの結果としては空欄が返ります。 「いやいや、マッチ度はどんなに低くてもいいので、とりあえず何か正解らしきものを入れておきたい!」という場合は、ここをゼロにします。 以上! 現時点では以上ですが、そのうちもう少し解説を書くつもりです。 IQBotHelperについてとか、ファジーマッチってなんやねんとか。。。とかとか。。。 (IQBotHelperライブラリは、Automation360.21から追加された、IQ Botの補正に便利なカスタムロジックのライブラリでオンプレ・クラウド問わず使えるやつなんですが、個人的にはライブラリを使って処理する価値があるのはこのfuzzy_matchくらいだと思ってます。他は特にライブラリ使わなくても、下記のページにまとめた内容で対応できるので)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonでリストではなくジェネレーターを使う

ジェネレーターのメリット メモリを節約できる(処理速度向上) コードを短くできる リストの各要素に対する処理 リストの各要素に対して処理を実行し,新しいリストを返すような場合を考えます. return_list.py def return_list(nums:list): result = [] for i in nums: result.append(i*i) return result my_nums = return_list([1, 2, 3, 4, 5]) print(my_nums) # [1, 4, 9, 16, 25] この場合,ジェネレーターでは下記のように記述できます. return_generator.py def return_generator(nums:list): for i in nums: yield i*i my_nums = return_generator([1, 2, 3, 4, 5]) print(list(my_nums)) # [1, 4, 9, 16, 25] 内包表記での処理 リストを作成する際に内包表記で処理することが多いでしょう. その際は,より簡単にgeneratorを生成できます list_generator.py list_nums = [x*x for x in [1, 2, 3, 4, 5]] generator_nums = (x*x for x in [1, 2, 3, 4, 5]) print(list_nums) print(list(generator_nums)) # [1, 4, 9, 16, 25] 最後に 良きpythonライフを 参考 投稿者:https://github.com/Hayate-Leo/python-tool Corey Schaferさん:https://github.com/CoreyMSchafer/code_snippets
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

リストではなくジェネレーターを使う

ジェネレーターのメリット メモリを節約できる(処理速度向上) コードを短くできる リストの各要素に対する処理 リストの各要素に対して処理を実行し,新しいリストを返すような場合を考えます. return_list.py def return_list(nums:list): result = [] for i in nums: result.append(i*i) return result my_nums = return_list([1, 2, 3, 4, 5]) print(my_nums) # [1, 4, 9, 16, 25] この場合,ジェネレーターでは下記のように記述できます. return_generator.py def return_generator(nums:list): for i in nums: yield i*i my_nums = return_generator([1, 2, 3, 4, 5]) print(list(my_nums)) # [1, 4, 9, 16, 25] 内包表記での処理 リストを作成する際に内包表記で処理することが多いでしょう. その際は,より簡単にgeneratorを生成できます list_generator.py list_nums = [x*x for x in [1, 2, 3, 4, 5]] generator_nums = (x*x for x in [1, 2, 3, 4, 5]) print(list_nums) print(list(generator_nums)) # [1, 4, 9, 16, 25] 最後に 良きpythonライフを 参考 投稿者:https://github.com/Hayate-Leo/python-tool Corey Schaferさん:https://github.com/CoreyMSchafer/code_snippets
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python3ではじめるシステムトレード:経験分布

 経験分布は単に得られたデータをグラフにする際に必要な知識と思っていましたがデータ分析, 特に大偏差原理(Large deviation theory)に欠かせない知識なので経験分布(empirical distribution)の英語版のウィキを適当に翻訳することにしました。翻訳にはwww.DeepL.com/Translator(無料版)を用いました。 経験分布関数  統計学では、経験分布関数(一般に経験累積分布関数、eCDFとも呼ばれる)は、サンプルの経験的な尺度に関連する分布関数である。この累積分布関数は、n個のデータポイントのそれぞれで1/nずつジャンプアップするステップ関数である。測定変数の任意の指定された値におけるその値は,指定された値以下である測定変数の観測値の割合である.  経験分布関数は,標本中の点を生成した累積分布関数の推定値である.経験分布関数は,Glivenko-Cantelliの定理により,その基礎となる分布に確率1で収束する.経験分布関数の累積分布関数への収束率を定量化する方法がいくつかある。 0と1の高さに到達することなく漸近的に近づく緑色の曲線は、標準正規分布の真の累積分布関数である。 灰色のハッシュマークは,その分布から抽出された特定の標本のオブザベーションを表し,青色のステップ関数の水平ステップ(各ステップの左端の点を含み,右端の点は含まない)は,その標本の経験分布関数を形成する. import numpy as np from scipy.interpolate import interp1d def _conf_set(F, alpha=0.05): nobs = len(F) epsilon = np.sqrt(np.log(2.0 / alpha) / (2 * nobs)) lower = np.clip(F - epsilon, 0, 1) upper = np.clip(F + epsilon, 0, 1) return lower, upper class StepFunction: def __init__(self, x, y, ival=0.0, sorted=False, side="left"): if side.lower() not in ["right", "left"]: msg = "side can take the values 'right' or 'left'" raise ValueError(msg) self.side = side _x = np.asarray(x) _y = np.asarray(y) if _x.shape != _y.shape: msg = "x and y do not have the same shape" raise ValueError(msg) if len(_x.shape) != 1: msg = "x and y must be 1-dimensional" raise ValueError(msg) self.x = np.r_[-np.inf, _x] self.y = np.r_[ival, _y] if not sorted: asort = np.argsort(self.x) self.x = np.take(self.x, asort, 0) self.y = np.take(self.y, asort, 0) self.n = self.x.shape[0] def __call__(self, time): tind = np.searchsorted(self.x, time, self.side) - 1 return self.y[tind] class ECDF(StepFunction): def __init__(self, x, side="right"): x = np.array(x, copy=True) x.sort() nobs = len(x) y = np.linspace(1.0 / nobs, 1, nobs) super(ECDF, self).__init__(x, y, side=side, sorted=True) def monotone_fn_inverter(fn, x, vectorized=True, **keywords): x = np.asarray(x) if vectorized: y = fn(x, **keywords) else: y = [] for _x in x: y.append(fn(_x, **keywords)) y = np.array(y) a = np.argsort(y) return interp1d(y[a], x[a]) if __name__ == "__main__": # TODO: Make sure everything is correctly aligned and make a plotting # function from urllib.request import urlopen import matplotlib.pyplot as plt nerve_data = urlopen("http://www.statsci.org/data/general/nerve.txt") nerve_data = np.loadtxt(nerve_data) x = nerve_data / 50.0 # Was in 1/50 seconds cdf = ECDF(x) x.sort() F = cdf(x) plt.step(x, F, where="post") lower, upper = _conf_set(F) plt.step(x, lower, "r", where="post") plt.step(x, upper, "r", where="post") plt.xlim(0, 1.5) plt.ylim(0, 1.05) plt.vlines(x, 0, 0.05) plt.show()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

超初心者がPythonでMACDと5分足チャートをPlotlyを使って同時にグラフに表示してみた

MACDの値はY軸の単位が違うので、ローソク足チャートと同時に表示するにはY軸が2つ必要になる。PlotlyでY軸2つを設定する方法をやってた。ここではグラフ表示部分のコードだけ紹介。MACDの計算やらはなんとなく察してどうぞ。 参考 Plotlyを使ったグラフ化は以下でもやってます。こちらはY軸1つ。 MACDの計算方法は以下でやってます。 コード # グラフ化 interval = 12 vals = [] labels = [] for i in range(len(df)//interval): vals.append(df.index[i*interval]) labels.append(df.index[i*interval].strftime('%Y-%m-%d %H:%M')) fig = go.Figure( data=[ go.Candlestick( x=df.index, open=df.Open, high=df.High, low=df.Low, close=df.Close, hovertext=['date:{}<br>open:{}<br>high:{}<br>low:{}<br>close:{}' .format(i.strftime('%Y-%m-%d %H:%M'), df.loc[i,'Open'], df.loc[i,'High'], df.loc[i,'Low'], df.loc[i,'Close']) for i in df.index], hoverinfo="text", name="Candle", yaxis='y1' ), go.Scatter(x=df.index, y=df.MACD, line=dict(color='blue', width=1), name="macd", yaxis='y2'), go.Scatter(x=df.index, y=df.Signal, line=dict(color='red', width=1), name="signal", yaxis='y2') ], layout = go.Layout( xaxis = dict( ticktext = labels, tickvals = vals, tickangle = -90 ), yaxis = dict(title='y1', side='left', showgrid=False, range=[df.Low.min(), df.High.max()]), yaxis2 = dict(title='y2', side='right', overlaying='y', range=[min(df.Signal.min(), df.MACD.min()), max(df.MACD.max(), df.Signal.max())]) ) ) fig.show() グラフ ちょっと説明 go.Candlestick、go.Scatterの引数でY軸を指定してる。デフォルトだとy1が左でy2が右。その後、layoutでyaxis(=左)とyaxis2(=右)の設定をしている。yaxis2でoverlayingを指定しとかないとyaxisを上書きしてy1のデータがグラフに表示されないので注意。 rangeでY軸の最小値と最大値を指定できるので、適宜計算する。 感想 Plotly便利だなぁ(小並感)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonで外部サービス(Discord,Twitter,YouTube,ブラウザ,Heroku)に触ってボットを作る

はじめに  この記事は、Pythonから外部サービス(Twitter,YouTube,ブラウザ)を利用して、Discordで動くボットを作成することを目指したものです。  すでにPythonでDiscordのボットを動かす記事は多く出ているため、この記事ではPythonと外部サービスやAPIを連携させる際の、具体的な実装を書きました。  そのため、重要でないプログラムや外部記事に詳細が載っているものは記載を割愛しています。 なんちゃってUML 今回作るものの設計書です。経験少ないまま作成したものですのでご容赦ください。 【配置図】 【アクティビティ図】 【クラス図】 【シーケンス図】 作業手順 1.YouTubeのチャンネル名から動画URL 1-1.YouTube Data API v3の有効化(説明割愛) (i)gcpのアドレスにアクセスしてプロジェクトを作成します。 (ii)作成したプロジェクトを選択し、「YouTube Data API v3」を有効化し、APIキーを作成します。 ※参考 1-2.YouTube APIの利用 (i)Pythonにrequestsパッケージをインストールします。 python -m pip install requests (ii)YouTubeのAPIを用いたpythonのプログラムを作成します。 作成するプログラムは、なんちゃってUMLのクラス図におけるYoutubeApi.pyです。 プログラムは下記の通りです。 YoutubeApi.py import requests import utilities class YoutubeApi: def get_latest_movie(self, channel_name): API_KEY=作成したAPIキー url = utilities.get_url_with_params( url="https://www.googleapis.com/youtube/v3/search", key=API_KEY, maxResults = 1, part="snippet", q=channel_name, type="channel", ) response = requests.get(url).json() channel_id = response["items"][0]["id"]["channelId"] url = utilities.get_url_with_params( url="https://www.googleapis.com/youtube/v3/search", key=API_KEY, maxResults = 1, order="date", part="snippet", channelId=channel_id, type="video", ) response = requests.get(url).json() video_id = response["items"][0]["id"]["videoId"] video_url=f"https://www.youtube.com/watch?v={video_id}" return video_url if __name__=="__main__": api = YoutubeApi() print(api.get_latest_movie("米津玄師")) (iii)作成したプログラムを実行します。 正常に実行できれば、指定したチャンネルの最新動画のurlが表示されます。 ※参考 2.Twitterのアカウント名からツイートURL 2-1.Twitter APIの利用(説明割愛) (i)Twitterの開発者ページにアクセスします。 (ii)「Create an app」ボタンから、APIの利用申請を行います。 申請が許可されればメールが届きます。 (iii)申請許可後、Twitterの開発者ページにて、「Create App」ボタンからアプリの作成を行います。 ※参考 2-2.Twitter APIの利用 (i)pythonにてtweepyパッケージをインストールします。 (ii)TwitterのAPIを用いたPythonのプログラムを作成します。 作成するプログラムは、なんちゃってUMLのクラス図におけるTwitterApi.pyです。 プログラムは下記の通りです。 TwitterApi.py import tweepy class TwitterApi: def get_latest_tweet(self, account_name): CONSUMER_KEY = 作成したアプリのコンシューマーキー CONSUMER_SECRET = 作成したアプリのコンシューマーシークレット BEARER_TOKEN = 作成したアプリのベアラートークン ACCESS_TOKEN = 作成したアプリのアクセストークン ACCESS_TOKEN_SECRET = 作成したアプリのアクセストークンシークレット auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET) auth.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET) api = tweepy.API(auth, wait_on_rate_limit=True) tweet = api.user_timeline(account_name, count=1)[0] screen_name = tweet.user.screen_name id = tweet.id url = f"https://twitter.com/{screen_name}/status/{id}" return url if __name__ == "__main__": api = TwitterApi() print(api.get_latest_tweet("@Genshin_7")) (iii)作成したプログラムを実行します。 正常に実行されれば、指定したツイッターアカウントの最新ツイートのurlが表示されます。 ※参考 3.Chromiumを使ったGoogle検索 (i)Pythonに以下のパッケージをインストールします。 selenium,chromedriver_binary (ii)Google検索を行うPythonのプログラムを作成します。 ChromiumBrowser.py import chromedriver_binary from selenium import webdriver class ChromiumBrowser: def get_url(self, q): driver = webdriver.Chrome() driver.get(f"https://www.google.com/search?q={q}") element = driver.find_element_by_class_name("yuRUbf") url = element.find_element_by_tag_name("a").get_attribute("href") driver.quit() return url if __name__ == "__main__": browser = ChromeBrowser() print(browser.get_url("原神公式HP")) (iii)作成したプログラムを実行します。 検索ワードに対する検索結果のURLが表示されます。 ※参考 4.HerokuでのDiscordのボットの操作 4-1.Herokuのスケジューラー (i)Herokuの仕様からボットの稼働時間をどのようにすればよいかを考えます。 目標は、毎日必要な時間にボットを稼働させることです。 問題はHerokuの無料枠ではボットを常に稼働させることができないことです。 元々、Herokuには無料で処理を定期実行できるschedulerがあります。 schedulerは、Herokuのウェブページでクレジットカードを登録することで、利用できます。 しかし、今回はschedulerを使いません!!自分で作ります!! スケジューラーを自作するには、Herokuの仕様を知っておく必要があります。 以下にHerokuの起動と停止に関する仕様をまとめます。 ●再起動でファイル変更が廃棄される ●再起動は24時間(+0~216分)のサイクルで実行 ●プロセスによる停止後に自動で再起動が行われる  ・停止1回目:直ちに再起動される  ・停止2回目以降:20分、40分、60分、180分、320分の順で再起動の間隔が長くなる  ・再起動が成功すると上記の間隔はリセットされる ●未認証アカウントの場合、無料の最大稼働時間は550時間/月である。 以上から、毎日指定時間のみ稼働させるための方法を考えます。 毎日稼働させるとすると、550/30≒18時間が最大となります。 また、停止後の再起動サイクルの最大値は320分であるため、稼働時間を5時間以下にすると、稼働しない日が発生する可能性があります。 そのため、毎日必ず稼働させるには、稼働時間を5時間より長く18時間より短くする必要があります。 また、稼働時間の指定は時刻の判定を使うことで実現できます。ただし、再起動サイクルが長いため、指定時間になったらすぐに起動する保証はありません。 とはいえ、ボットをざっくり毎日稼働させるプログラムを実装できることがわかりました。 ※参考 (ii)pythonでボットを指定時間以内のみ動作させるプログラムを作成します。 BotScheduler.py import sys import utilities class BotScheduler: def __init__(self): self.start_time_hour = 18 self.running_hours = 8 self.hour_count = 0 def check_start_time(self): if utilities.get_jst_nowtime().hour < self.start_time_hour: print("bot is closed...") sys.exit() def check_end_time(self): if self.running_hours <= self.hour_count: print("shutting down...") sys.exit() else: self.hour_count += 1 if __name__ == "__main__": obj = BotScheduler() obj.check_start_time() obj.check_end_time() (iii)作成したプログラムを実行します。 実行すると、現在時刻が18時前の時、「bot is closed...」と表示されます。 上記の変数の値では18時から8時間、稼働するように設定しています。 4-2.Discordのボットの操作 4-2-1Discordのボットの作成 (i)ボットのアプリケーションを作成します。 初めにDiscordの開発者ページにアクセスします。 https://discord.com/developers/applications ボットの作成のために下記の項目を設定します。 項目 値 NAME MyTestBot(任意) APP ICON ※下記の画像を利用(任意) PUBLIC BOT OFF ※今回、アプリアイコンとして使用した画像 (ii)サーバーにBotを招待する Discordのサーバーがなければ作成します。 ブラウザのボットのOAuth2ページのURLから、下記の表の項目を設定し、Botの招待を行います。 項目 値 SCOPES bot BOT PERMISSIONS Administrator ※参考 4-2-2.ボットの操作 (i)pythonにdiscord.py[voice]パッケージをインストールします。 (ii)discordパッケージを用いたpythonのプログラムを作成します。 作成するプログラムは、なんちゃってUMLのクラス図におけるMain.pyです。 プログラムは下記の通りです。 Main.py import discord from discord.ext import tasks from FunctionsFactory import FunctionFactory factory = FunctionFactory() factory.get_bot_scheduler().check_start_time() client = discord.Client() @tasks.loop(hours=1) async def loop(): factory.get_bot_scheduler().check_end_time() @client.event async def on_ready(): print("hello bot!") @client.event async def on_message(message): command = message.content[0] option = message.content[2:] if command == "y": func = factory.get_discord_youtube() await func.send_message(message, option) elif command == "t": func = factory.get_discord_twitter() await func.send_message(message, option) elif command == "b": func = factory.get_discord_browser() await func.send_message(message, option) TOKEN = ボットのトークン client.run(TOKEN) プログラム中のTOKENはボットのウェブページのBotから確認できます。 (iii)作成したプログラムを実行します。 正常に動けば、下の画像のようになると思います。 4-3.Herokuでのボットの稼働(説明割愛) Herokuへのデプロイの方法は下記のサイトに詳しい記載があるので、この記事では割愛します。 https://qiita.com/1ntegrale9/items/9d570ef8175cf178468f この記事ではGithubを使う方法を採用しています。 ※herokuにpythonのパッケージをインストールするため、Githubのリポジトリに下記のrequirements.txtを作成する必要があります。 requirements.txt discord.py tweepy requests selenium chromedriver_binary==86.0.4240.22.0 ※参考 おわりに  私自身、Pythonで色々と自動化したいと思い、関連する知識を学んだのは数年前ですが、改めて知識を整理するために記事を作成しました。同じようにPythonで色々動かしたいと思う方に役立つと幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonによる時系列データ分析レシピの紹介

はじめに Axrossを運営している藤原です。 Axross とは、エンジニアの"教育"と"実務"のギャップに着目し、「学んだが活用できない人を減らしたい」という想いのもと、ソフトバンクと社内起業制度にて立ち上げたサービスです。 現役エンジニアによる実践ノウハウを"レシピ"として教材化し、実際にプログラミングで実装を追体験しながら学ぶことができます。AI/機械学習をテーマにした、様々な業務領域やビジネスの課題解決に応用できる実践的な学習教材を150以上揃えています。(2021年7月時点) Axross:https://axross-recipe.com 公式Twitter:https://twitter.com/Axross_SBiv 今回は、時系列データを分析する考え方について紹介し、Axrossのサービスで学べる実践的な、Pythonによる時系列データを活用し、予測・分析する人気レシピをご紹介します。 時系列データとは 時系列データ(time series data)とは、ある特定の対象(価格・消費・入出荷などの経済事象、気温・雨量などの自然現象、施設の入退室の行動履歴や、交通状況、土地利用の変遷など)について、時間の経過とともに変動する数値を、継続的に観測することで得られたデータのことです。 そのデータを用いて、時間的な変化をもたらす背景や傾向パターンの発見や、その変化の将来予測などを目的に分析することを時系列データ分析と呼びます。 時系列データの活用例 時系列データとして活用できるものには、大きく4つの変動の種類があります。 ・循環変動:一定の周期(年、週、日など)によって変化が繰り返される変動 ・季節変動:季節ごとに繰り返される変動 ・トレンド:時間の経過とともに増減する動きが見られる変動 ・不規則変動:時間の経過に伴い、周期性や規則性が見られない不規則な変化をする変動 時間経過に合わせて事象を予測し、現場で判断・対応することは、様々な業界の現場レベルの仕事として必ずやっていることです。 例えば、最近の身近な時系列データを活用する例としては、新型コロナウイルス感染者数の拡大傾向や重症者数の推移から病床数の逼迫予測等があります。また、小売業や飲食業においては、曜日や時間帯ごとに来客数の推移を予測し、接客に当たる人員数や発注する食材・ドリンクの量、陳列する商品の在庫整理 など、規則性のある時系列情報をもとに、業務内容を調整していることも多いと思います。 ただ、現場の経験則や勘で行っていることが多く、過去の時系列データをもとに、様々な要素(変数)を踏まえ、デジタル上で数値的な根拠によって客観的に分析し、より高い精度での予測・対応を実現することが、これからは重要になってきます。 時系列データを分析するためには、時系列データのファイル(表)を読み込み、データをグラフにプロットして可視化するのが第一歩です。その後、仮説を持ちながらデータ分析のテクニックを用いて、事象を予測していきます。 Pythonによる時系列データ分析レシピの紹介 RNNをつかった実店舗の売上を予測するレシピ 投稿者:@manami さん 人工的で整っていないリアルなPOSデータを使用して、商品の売上予想モデルの実装を試し、データの収集→データの加工→モデルの実装という流れを学ぶことができます。 コンペや研究用のデータセットは最初からきれいなデータが用意されている事が多いですが、 実際の現場では、空白のあるデータも多く、綺麗な状態に整えることから始まります。今回は小売業のデータをテーマに、回帰型ニューラルネットワーク(RNN)による売り上げ予測を行います。 Pythonによる時系列データ分析レシピの紹介 日経33業種の株価増減率をクラスタリング分析し、日経平均株価と連動する業種を見つけるモデル作成レシピ 投稿者:@belltree さん 上場33業種の時系列データをクラスタリングし、日経平均株価の値動きと似通った値動きをした業種の特定するモデルを作成する手法を学べます。 上場33業種の2020年11月日足株価増減率データを読み込み、k-means法によるクラスタリングを行います。次に、pandas_datareaderライブラリを用いて日経平均株価を読み込み、主成分分析を行い、再度日経平均株価増減率を加えてクラスタリングをして、同じクラスターに属する業種を確認します。 飲食店の購買履歴データ分析レシピ 投稿者:@belltree さん 購買履歴データやレシートデータを分析するための手法として、Python(Pandas)における時系列データ解析の基礎的な考え方や分析テクニックを学べます。 折れ線グラフや棒グラフなど基本的なデータ可視化手法によって、売上や客数などの時間変化を捉え、仮説をもってデータ分析することで、ビジネスにおける需要予測や購買ルールやパターン認知に応用できます。 belltreeさんの購買履歴データの分析シリーズとして他にも、購買履歴データを分析し購買傾向を抽出するレシピや、顧客の購買履歴データを層別解析で分析するレシピがあり、いずれもPython初心者でも、購買履歴の時系列データの扱い方や分析手法を学ぶことができます。 ECサイトのCSVファイルをデータベース化し分析するレシピ 投稿者:@mshr_nkmr_ai さん BASEやShopifyなどECサイトの運営を想定し、ショップの売り上げデータ(CSVファイル)をデータベースに格納し、それを用いてデータ分析をしたり、新しい売り上げデータを追加したりする、等、Pythonを使い、SQLを操作して業務でも使えるようにする実践ノウハウを学ぶことができます。 CSVの管理や分析にSQLを取り入れることでファイルの分量や数が大きくなってもその時々で必要なものだけを効率よく取り出したり管理ができるようになります。 @mshr_nkmr_ai さんのレシピは、他にもECサイトのデータ分析から新商品を考えるレシピや、ECサイトで在庫に無駄なく商品を発注するためのデータ分析レシピ、Campfireでプロジェクトを分析し作戦を考案するレシピなど、ECサイトの運営で活用できる時系列データ分析のレシピが多く公開されています。 サポートベクトルマシンを用いた回帰分析の方法と実演レシピ 投稿者:@Jolibee (立石) さん ボストンの地価データにおける13個の変数値(築年数、広さ、周辺環境など)から、機械学習 サポートベクトルマシン(SVM)を活用して回帰分析を行い、それぞれの独立変数の相関関係を予測する手法を実演します。 SVMを用いたデータ解析手法やデータのグラフ等による可視化の手法を丁寧に解説しています。 また、続編の機械学習による多次元回帰分析結果の直感的理解を助ける可視化レシピでは、多次元回帰分析の結果に対して、各変数と目的変数の関係性を2次元及び、3次元でプロット描画し、直感的に理解しやすいように可視化するテクニックを学ぶことができます。 最後に 今回は、Axrossサービスで学べる実践的な、Pythonによる時系列データ分析のレシピ をご紹介しました。 データ分析ができるようになるためのコツは、座学勉強よりも、まず身近なデータから何か仮説を立てて、実際に分析してみる、そして、様々なテーマやデータセットでのデータ分析演習を通して、データの扱い方に慣れることこそが近道だと思います。 Axrossのレシピを通して、プログラムの意味を考えながら写経(コードを実際に書き写す行為)し、実際に動くものをつくりながら学ぶことで、新たな知識の習得やスキルアップの一助になれれば幸いです。 また、Axrossでは自身のナレッジを学習教材"レシピ"として寄稿いただけるエンジニアの方も募集しています! 見習いエンジニアから募った学びたい内容をウィッシュリストとして掲載しています。募集中のテーマからご自身で作成いただけるようでしたら、レシピ作成にご協力お願いいたします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DeepL API Freeを利用して一括翻訳【Python】

はじめに 教授「僕が知らなさそうな英論を読んで、発表してください。あぁ、君の専門分野外でよろしく」 僕「。。。」 教授「いつ空いてる?僕は明日空いてるけど?」 僕「。。。」 DeepLはかなり精度良く翻訳してくれますよね。今回はDeepLのAPIを利用して、テキストファイルを一気に翻訳する機能をPythonで実装しました。 まず、公式サイトにて事前に登録しておき、API_KEYを取得しておきます。 FREE版でも月50万文字まで翻訳可能なので、有難く使わせて頂きました。 ※データセキュリティの問題があるため、取り扱うテキストには注意してください。 最終的なコード 翻訳したいテキストファイル(target.txt)を翻訳して、翻訳結果を異なるテキストファイル(convert.txt)に出力します。 import requests API_KEY = #ここにAPIキーを入力してください。 # ファイルを開く f = open('target.txt', 'r', encoding='UTF-8') TEXT = f.read().replace('\n', ' ') # 改行が含まれている場合はスペースに置換 # URLクエリに仕込むパラメータの辞書を作っておく params = { "auth_key": API_KEY, "text": TEXT, "source_lang": 'EN', # 入力テキストの言語を英語に設定 "target_lang": 'JA' # 出力テキストの言語を日本語に設定(JPではなくJAなので注意) } # パラメータと一緒にPOSTする request = requests.post("https://api-free.deepl.com/v2/translate", data=params) # free用のURL、有料版はURLが異なります result = request.json() # 保存 f = open('convert.txt', 'w', encoding='UTF-8') f.write(result["translations"][0]["text"]) 翻訳言語の指定 翻訳言語を任意に設定するためには、上記コード内のparams内のxxx_langを変更してください。 "source_lang": # ここで元のテキスト言語を指定します "target_lang": # ここで翻訳後のテキスト言語を指定します FREE版とPRO版における実装の違い ポストするURLが異なります。以下のようにプランに合わせてURLを指定してください。 # FREE版 request = requests.post("https://api-free.deepl.com/v2/translate", data=params) # PRO版 request = requests.post("https://api.deepl.com/v2/translate", data=params) 参考文献
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DateFrameのフィルタリング

概要 bool型のシーケンスを指定する事でTrueのものだけを取り出す。 変数に対して.loc[df["カラム"] 条件式]とする事で、条件式に当てはまる要素を含む行のDataFrameが作成される。 import numpy as np import pandas as pd np.random.seed(0) colums = ["apple", "orange", "banana", "strawberry", "kiwifruit"] df = pd.DataFrame() for colum in colums: df[colum] = np.random.choice(range(1, 11), 10) df.index = range(1, 11) display(df) print() print(df.index % 2 == 0) display(df[df.index % 2 == 0]) print() df = df.loc[df["apple"] >= 5] df = df.loc[df["kiwifruit"] >= 5] # df = df.loc[df["apple"] >= 5][df["kiwifruit"] >= 5]でも可 display(df) 出力結果
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

東京都内 新型コロナウイルス陽性者数の分布マップを作ってみた

目的 都内在住・在勤の者として、どのあたりで陽性者数が多いのかが一目でわかりやすくしたかったので個人で開発しました。プログラミング初心者のため、見やすさなどで至らぬ点もあるかとは思いますが、どうぞ温かい目で見守って下さいますと幸いです。 【今回作ったもの】 https://nobukuni-hyakutake.github.io/covid19tokyo/ 時系列推移グラフも一緒に入っていますが、その解説はまた別でしようと思います。 内容概要 東京都のマップ上において、 1. 直近7日間の陽性者数(10万人あたり)がマークの直径と数字で表されています 2. 陽性者数の前週比がマークの色で表現されています スマートフォンでも見られるように作ってあります。 作成環境 言語:Python3.9 使用ライブラリ: データ前処理はPandasとNumpy、マップ作成はJSONとFolium 使用サービス: GitHub pages コード詳細 特徴 Pythonプログラムで元データCSVの取り込みからグラフのHTML出力まで一気に出来るので、毎日のデータ更新が簡単です。Pythonプログラムの実行→Gitコミット→Gitプッシュするだけで、最新データの公開が完了します。(自動的に定期実行させる方法もあるかも知れませんが、全然知識がなくやっていません) 開発した感想 プログラミングを全く知らない状態から、半年かけてPythonを勉強して作り、なんとかものに出来てよかったです。モチベーションとしては、これが作りたかったのもありますが、仕事(コロナとは関係なし)でも使えるスキルを身につけたかったこともあります。 受け身的に本をただ読むだけより、具体的に開発したいものがあって必要に迫られたほうが、プログラミングの勉強がはかどるなと感じました。 区市町村別陽性者数のCSVオープンデータを公開して下さったCode for Japan、開発を応援してくださったCode for Mitaka/Musashinoの皆様へ感謝申し上げます。 参考にした主なサイト  参考にした主な本 データ分析者のためのPythonデータビジュアライゼーション入門 コードと連動してわかる可視化手法 終わりに 感染された方へ、お見舞い申し上げます。 また、都内在住の方や他県から来られる方は、どうぞご参考にして頂ければと存じます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Djangoでアプリ開発をするときの環境構築のやり方

久しぶりにWebアプリを新規で作成しようとしたときに、「あれ?どうやるんだっけ??」ってなってしまうことが多く、備忘録も兼ねてまとめました。 Django公式サイトのチュートリアルのはじめてのDjangoアプリ作成からWebアプリケーションを作成する際に必要な箇所だけ抜粋しました。 主に環境構築を中心に記載していきます。 開発環境 OS:Windows10 エディタ:VS Code ターミナル:Winodows PowerShell Python:3.9.6 1. venvのインストール 今回は「DjangoTutorial」というディレクトリで作業していきます。 まずはディレクトリを作成します。 PS C:> mkdir DjangoTutorial 作成したディレクトリにvenvをインストールします。 PS C:> py -m venv DjangoTutorial 「DjangoTutorial」ディレクトリに移動します。 PS C:> cd DjangoTutorial 2. venvのアクティベート エラーが出てしまうので、下記のコマンドを実行します。 PS C:\DjangoTutorial> Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force venvをアクティベートします。 PS C:\DjangoTutorial> Scripts\activate.ps1 3. Djangoのインストール venvの仮想環境にDjangoをインストールします。 (DjangoTutorial) PS C:\DjangoTutorial> py m pip install Django 4. プロジェクトの作成 プロジェクト名は公式チュートリアルと同様に「mysite」とします。 (DjangoTutorial) PS C:\DjangoTutorial> django-admin startproject mysite 5. Django開発サーバーの起動 Djangoサーバーを起動します。 ※公式チュートリアルにも記載されていますが、このDjangoサーバーは本番環境では利用しないでください。 (DjangoTutorial) PS C:\DjangoTutorial> mysite/manage.py runserver Djangoサーバーが起動したら、Webブラウザでアクセスします。 http://127.0.0.1:8000/ 6. アプリケーションの作成 アプリケーション名も公式チュートリアルと同様に「polls」とします。 (DjangoTutorial) PS C:\DjangoTutorial> cd mysite (DjangoTutorial) PS C:\DjangoTutorial\mysite> py manage.py startapp polls これでアプリケーションの開発を始められます。 参考文献 この記事は下記の情報を参考にして執筆しました。 はじめてのDjangoアプリ作成
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

venvの仮想環境を使い、Djangoでアプリ開発をするときの環境構築手順

久しぶりにWebアプリを新規で作成しようとしたときに、「あれ?どうやるんだっけ??」ってなってしまうことが多く、備忘録も兼ねてまとめました。 Django公式サイトのチュートリアルのはじめてのDjangoアプリ作成からWebアプリケーションを作成する際に必要な箇所だけ抜粋しました。 主に環境構築を中心に記載していきます。 開発環境 OS:Windows10 エディタ:VS Code ターミナル:Winodows PowerShell Python:3.9.6 1. venvのインストール 今回は「DjangoTutorial」というディレクトリで作業していきます。 まずはディレクトリを作成します。 PS C:> mkdir DjangoTutorial 作成したディレクトリにvenvをインストールします。 PS C:> py -m venv DjangoTutorial 「DjangoTutorial」ディレクトリに移動します。 PS C:> cd DjangoTutorial 2. venvのアクティベート エラーが出てしまうので、下記のコマンドを実行します。 PS C:\DjangoTutorial> Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force venvをアクティベートします。 PS C:\DjangoTutorial> Scripts\activate.ps1 3. Djangoのインストール venvの仮想環境にDjangoをインストールします。 (DjangoTutorial) PS C:\DjangoTutorial> py -m pip install Django 4. プロジェクトの作成 プロジェクト名は公式チュートリアルと同様に「mysite」とします。 (DjangoTutorial) PS C:\DjangoTutorial> django-admin startproject mysite 5. Django開発サーバーの起動 Djangoサーバーを起動します。 ※公式チュートリアルにも記載されていますが、このDjangoサーバーは本番環境では利用しないでください。 (DjangoTutorial) PS C:\DjangoTutorial> cd mysite (DjangoTutorial) PS C:\DjangoTutorial\mysite> py manage.py runserver Djangoサーバーが起動したら、Webブラウザでアクセスします。 http://127.0.0.1:8000/ 6. Django開発サーバーの設定 開発サーバーの言語とタイムゾーンを設定します。 下記のファイルを開き設定を変更します。 mysite\settings.py - LANGUAGE_CODE = 'en-us' + LANGUAGE_CODE = 'ja' - TIME_ZONE = 'UTC' + TIME_ZONE = 'Asia/Tokyo' 7. アプリケーションの作成 アプリケーション名も公式チュートリアルと同様に「polls」とします。 (DjangoTutorial) PS C:\DjangoTutorial\mysite> py manage.py startapp polls これでアプリケーションの開発を始められます。 参考文献 この記事は下記の情報を参考にして執筆しました。 はじめてのDjangoアプリ作成
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Codingame 1D Spreadsheet をメモ化を使って解く [Python]

はじめに 最近Codingameの練習問題のEasyを解いているのですが、ちょいちょい詰まることがあります。今回は題名の通り1D Spreadsheetという問題なのですが、これが初心者の自分にはなかなか難しくて放置してまた考えてというふうに繰り返していると数日かかってしまいました。 最初は自力でなんとか解こうと思っていたのですが、最後のテスト問題が解けず、コードを弄っているとどの問題もパスできなくなるなどの負のループに陥ってしまいました。ついに諦めてForumを見てみると、新しい発見とMemoization(メモ化)というアルゴリズムを使うといいということがわかりました。初心者なりにメモ化を使ってみたので、コードに対するフィードバックをもらえると嬉しいです。 問題 問題はセル数と計算セルが与えられて、それの答えをアウトプットするというものです。 計算セルはそれぞれVALUE, ADD, SUB, MULTのいずれかの指令と、それに続く2つのバリューarg1,arg2で構成されています。 指令どおりにarg1とarg2足したり引いたりかけたりするだけだったら簡単なのですが、ここに$を使った参照というものも入ってきます。$0で0番目の答えを参照という具合です。 インプットの例は以下のとおりです。 2 VALUE 3 _ ADD $0 4 そしてこのインプットに対して以下を出力しなければいけません。 3 7 解説すると、最初のセルはVALUEで最初のarg1の3が答えになり、次のセルでは$0と4を足すのですが、$0は最初のセルの答えが入るので答えは7になります。 問題はこの参照が厄介で、前のセルの答えだけでなく、あとのセルも参照しなければいけなかったり、全部が参照で構成されていたりします。 なんとなく説明しただけですので、詳しくは以下をご覧ください。投げやりですみません? https://www.codingame.com/ide/puzzle/1d-spreadsheet コード セル数の上限が100までで、すべてのセルに対して再計算をさせてやるとタイム・アウトしてしまう可能性があります。そこで本コードではメモ化を用いて再計算を防いでやるというアルゴリズムを使います。 インプット まずは競技プロ定番のインプットで、問題に対応させてやります。 追加でメモ化のために答えを記録しておく辞書を用意します。 #セル数のインプット n = int(input()) #インプットされるセルとメモ化のための答えの辞書の用意 cells = [] ans = {} #セルのインプット for i in range(n): cells.append(input().split()) 関数 問題処理のための関数です。i番目のセルの答えが出るようにコードを組んでやります。 いちいち再計算が行われるのを防ぐために、答えが出た時点でans辞書に記録して、他のセルで答えの参照が出たときに再参照と再計算をしないようにしてやります。 def val(i): # iセルの中身をわかりやすいように定義 function = str(cells[i][0]) arg_1 = str(cells[i][1]) arg_2 = str(cells[i][2]) # 辞書内にセルiの答えがあったときに答えを返す if i in ans: return ans[i] # 'SUB $a - $a'のとき0を返す if (function == 'SUB') and (arg_1 == arg_2): ans[i] = int(0) return ans[i] # arg_1が参照だったとき、関数に投げる if '$' in arg_1: arg_1 = val(int(arg_1.strip('$'))) # arg_1が参照だったとき、関数に投げる if '$' in arg_2: arg_2 = val(int(arg_2.strip('$'))) # 最後に参照ではなかったときに計算、'ans'辞書に記録、そして答えを返す。 if ('$' not in str(arg_1)) and ('$' not in str(arg_2)): if function == 'VALUE': ans[i] = int(arg_1) return ans[i] if function == 'ADD': ans[i] = int(arg_1) + int(arg_2) return ans[i] if function == 'SUB': ans[i] = int(arg_1) - int(arg_2) return ans[i] if function == 'MULT': ans[i] = int(arg_1) * int(arg_2) return ans[i] アウトプット 最後に答えを出力するために関数に順番にセル数を投げてやります。 for i in range(n): print(val(i)) まとめ 解けてしまうとなぜこんなのに時間かかってたんだろうと少し恥ずかしい気持ちになりますが、少しでもこの問題を解いていて行き詰まった人の役に立てばなと思います。 あと、コードを人に見てもらうという機会がないので、自分のコードがきれいか汚いかすらわからないのですが、こう書いたほうがいいよとかあったらコメントお願いします! Appendix import sys import math n = int(input()) cells = [] ans = {} for i in range(n): cells.append(input().split()) #ans[i] = None def val(i): function = str(cells[i][0]) arg_1 = str(cells[i][1]) arg_2 = str(cells[i][2]) if i in ans: return ans[i] if (function == 'SUB') and (arg_1 == arg_2): ans[i] = int(0) return ans[i] if '$' in arg_1: arg_1 = val(int(arg_1.strip('$'))) if '$' in arg_2: arg_2 = val(int(arg_2.strip('$'))) if ('$' not in str(arg_1)) and ('$' not in str(arg_2)): if function == 'VALUE': ans[i] = int(arg_1) return ans[i] if function == 'ADD': ans[i] = int(arg_1) + int(arg_2) return ans[i] if function == 'SUB': ans[i] = int(arg_1) - int(arg_2) return ans[i] if function == 'MULT': ans[i] = int(arg_1) * int(arg_2) return ans[i] for i in range(n): print(val(i))
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む