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

Codeforces Round #715 Div. 2C The Sports Festival: 区間DP典型

区間DPの超典型。以下、0-indexed。 この問題の難しさ 入力をソートして、どこからかスタートし、最適に左右にとり続ければよいことがわかるが、これでは、ある地点をスタートして、左右どちらに行くかを計算するため、$O(2000*2^{2000})$のオーダーとなる。 アプローチ まず、入力をソートしてn個の要素を$a_0, a_1, ..., a_{n-1}$とする。 ある区間$[l, r)$の最善なコストを$dp[l, r)$と表す。この時、求めたい結果は$dp[0, n)$である。ただし、題意より、$dp[x, x+1) = 0$である(つまり、ある1つの数を選んでいるとき)。 この時、$dp[l, r) = min ((a_r - a_l) + dp[l+1, r), dp[l, r+1) + (a_r - a_l))$である。つまり、 ある区間のコストとは、それより1つ短いコストに1つ追加したものである。これを図に示すと以下のようになる。 これをテーブルにすると以下のようになる。(線の合流のminをとり、合流地点の$(a_r - a_l)$を足す。 実装上の注意 Pythonは配列へのアクセスが遅い。このため、単純にに書くと(以下実装例のoldFunction)TLEするかぎりぎりになる。これは、Pythonの配列へのアクセスの遅さが関係しているようである。(ある程度大きい配列に対するアクセスのキャッシュの幅が狭いように感じる) このため、実装例では2本のdpを持ち、下から計算している。 このように 実装(Python3) import sys input = sys.stdin.readline def do(): n = int(input()) dat = sorted(list(map(int, input().split()))) dp = [0] * (n+1) for step in range(0, n): newdp = [0] * (n + 1) for i in range(n-step+1, n+1): l, r = n-step-1 , i newdp[i] = min(dp[i], newdp[i-1]) + (dat[r-1] - dat[l]) dp = newdp print(dp[n]) do() def oldFunction(): n = int(input()) dat = sorted(list(map(int, input().split()))) dp = [0] * ( (n+1) * (n+1) ) for width in range(2, n+1): for l in range(0, n - width+1): r = l + width dp[l*n + r] = (dat[r-1] - dat[l]) + min(dp[l*n + r-1], dp[l*n+n + r]) print(dp[n]) 実装(C++) using namespace std; int main() { //FASTIOpre(); ll n; cin >> n; ll x; vector<ll> dat(n); REP(i, n){cin >> x; dat.at(i) = x;} vector<vector<ll>> table(n+1, vector<ll>(n+1, 1e18)); sort(ALL(dat)); REP(i, n) table.at(i).at(i+1) = 0L; ll r; FOR(width, 2, n+1){ REP(l, n- width+1){ r = l + width; table.at(l).at(r) = min(table.at(l).at(r), table.at(l).at(r-1) + (dat.at(r-1) - dat.at(l))); table.at(l).at(r) = min(table.at(l).at(r), (dat.at(r-1) - dat.at(l)) + table.at(l+1).at(r)); } } cout << table.at(0).at(n) << "\n"; }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

matplotlibで描画したテキストのバウンディングボックスを取得する

TL;DR matplotlibでax.text()を用いて描画したテキストのバウンディングボックス(bounding box, bbox)を取得するには次のようにします。 調査した環境はmacOS Big Sur 11.2.3, Python 3.9.2, matplotlib 3.3.4です。 import matplotlib.pyplot as plt fig, ax = plt.subplots(1,1) # 図の範囲を指定する(これがないと正しくbboxを取得できない) ax.set_xlim(0, 1) ax.set_ylim(0, 1) # 先に描画を行う(これがないとエラーになる) fig.canvas.draw() # テキストの描画 text = ax.text(0.2, 0.2, "abcdefg0123あいう日本語", color='black', fontname="GenEi Gothic P", fontsize=20) # グラフの座標でのbboxの取得 bbox = text.get_window_extent().transformed(ax.transData.inverted()) # bboxの描画 ax.plot([bbox.x0, bbox.x0, bbox.x1, bbox.x1, bbox.x0], [bbox.y0, bbox.y1, bbox.y1, bbox.y0, bbox.y0]) plt.show() 注意点 先にfig.canvas.draw()を呼ぶ fig.canvas.draw()を呼ばないと次のようなエラーが出ます。 RuntimeError Traceback (most recent call last) <ipython-input-42-009947e9c4d3> in <module> 12 13 # グラフ内の座標でのbboxの取得 ---> 14 bbox = text.get_window_extent().transformed(ax.transData.inverted()) 15 16 # bboxの描画 ~/miniconda3/envs/cartopy-env/lib/python3.9/site-packages/matplotlib/text.py in get_window_extent(self, renderer, dpi) 897 self._renderer = self.figure._cachedRenderer 898 if self._renderer is None: --> 899 raise RuntimeError('Cannot get window extent w/o renderer') 900 901 with cbook._setattr_cm(self.figure, dpi=dpi): RuntimeError: Cannot get window extent w/o renderer 図の範囲を設定する ax.set_xlim(0, 1) ax.set_ylim(0, 1) を書かず、図の範囲を設定しない場合、bboxが正しく取得できません。 .inverse_transformed()は非推奨 古い情報だと bbox = text.get_window_extent().transformed(ax.transData.inverted()) の部分を bbox = text.get_window_extent().inverse_transformed(ax.transData) と書いていることがありますが、これは非推奨(deprecated)です。 <ipython-input-45-c5bc8ab82cc2>:14: MatplotlibDeprecationWarning: The inverse_transformed function was deprecated in Matplotlib 3.3 and will be removed two minor releases later. Use transformed(transform.inverted()) instead. bbox = text.get_window_extent().inverse_transformed(ax.transData) 参考にしたページ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

『クリギング入門』のPython実装

クリギング入門 コロナ社様のサイトにて読者モニターレビューをさせていただきました。コロナ社:『クリギング入門』の「レビュー」 地理空間データを用いる場合の空間内挿推定手法の1つであるクリギングの分かりやすい入門書です。 分かりやすい本でしたが実装例がExcelのためPythonに翻訳してみました。 pyKrigingというクリギングのライブラリがあるようですが、今回は1ステップずつを確認しつつ検証することが目的であるためテキストのExcel計算をPythonで泥臭く1つずつ実施しています。 テキスト「6.2 Excelを用いた計算」のPythonでの実行 (1) バリオグラム推定 import pandas as pd import numpy as np from scipy.spatial import distance_matrix from scipy.optimize import minimize import matplotlib.pyplot as plt データ入力 df = pd.DataFrame({'x': [2.0, 1.0, 4.0, 6.0, 5.5], 'y': [4.0, 3.0, 2.5, 2.0, 4.0], 'z':[5.3, 4.5, 4.6, 2.9, 3.2]}) df x y z 0 2.0 4.0 5.3 1 1.0 3.0 4.5 2 4.0 2.5 4.6 3 6.0 2.0 2.9 4 5.5 4.0 3.2 図6.6 標本平均、標本分散の計算 m = df.z.mean() m 4.1 s2 = df.z.var() s2 1.0249999999999997 図6.7 データ間の距離の計算 df_dist = pd.DataFrame(distance_matrix(df.iloc[:, 0:2], df.iloc[:, 0:2])) df_dist 0 1 2 3 4 0 0.000000 1.414214 2.500000 4.472136 3.500000 1 1.414214 0.000000 3.041381 5.099020 4.609772 2 2.500000 3.041381 0.000000 2.061553 2.121320 3 4.472136 5.099020 2.061553 0.000000 2.061553 4 3.500000 4.609772 2.121320 2.061553 0.000000 図6.8 非類似度の計算 df_dissimilarity = pd.DataFrame(np.square(distance_matrix(df.iloc[:, 2:3], df.iloc[:, 2:3])) / 2) df_dissimilarity 0 1 2 3 4 0 0.000 0.320 0.245 2.880 2.205 1 0.320 0.000 0.005 1.280 0.845 2 0.245 0.005 0.000 1.445 0.980 3 2.880 1.280 1.445 0.000 0.045 4 2.205 0.845 0.980 0.045 0.000 図6.9 データペアの距離と非類似度の整理1 =INDEX('データ(3)'!$D$4:$H$8,B11,C11) df_2 = pd.DataFrame(columns = ['j', 'k', 'h', 'g']) for j in range(len(df_dist)): for k in range(j + 1, len(df_dist)): df_2 = df_2.append({'j': j + 1, 'k': k + 1, 'h': df_dist.iloc[j, k], 'g': df_dissimilarity.iloc[j, k]}, ignore_index=True) df_2 j k h g 0 1.0 2.0 1.414214 0.320 1 1.0 3.0 2.500000 0.245 2 1.0 4.0 4.472136 2.880 3 1.0 5.0 3.500000 2.205 4 2.0 3.0 3.041381 0.005 5 2.0 4.0 5.099020 1.280 6 2.0 5.0 4.609772 0.845 7 3.0 4.0 2.061553 1.445 8 3.0 5.0 2.121320 0.980 9 4.0 5.0 2.061553 0.045 図6.10 バリオグラム雲の計算 plt.scatter(df_2.iloc[:, 2:3], df_2.iloc[:, 3:4]) plt.title("バリオグラム雲") plt.xlabel("h") plt.ylabel("γ") plt.grid(True) 図6.11 ラグによるデータ間距離の分類 lag = 1 df_2['h2'] = list(map(int, df_2.h / lag)) df_2 j k h g h2 0 1.0 2.0 1.414214 0.320 1 1 1.0 3.0 2.500000 0.245 2 2 1.0 4.0 4.472136 2.880 4 3 1.0 5.0 3.500000 2.205 3 4 2.0 3.0 3.041381 0.005 3 5 2.0 4.0 5.099020 1.280 5 6 2.0 5.0 4.609772 0.845 4 7 3.0 4.0 2.061553 1.445 2 8 3.0 5.0 2.121320 0.980 2 9 4.0 5.0 2.061553 0.045 2 図6.12 非類似度の平均化(経験バリオグラム) df_3 = df_2.groupby('h2').mean() df_3 j k h g h2 1 1.00 2.00 1.414214 0.32000 2 2.75 4.25 2.186106 0.67875 3 1.50 4.00 3.270691 1.10500 4 1.50 4.50 4.540954 1.86250 5 2.00 4.00 5.099020 1.28000 図6.13 経験バリオグラムの作成 plt.scatter(df_3.iloc[:, 2:3], df_3.iloc[:, 3:4]) plt.title("経験バリオグラム") plt.xlabel("h") plt.ylabel("γ") plt.grid(True) 図6.14 理論バリオグラム(初期パラメータ)表示 a = 3 b = 0 c = 1.03 df_3['g2'] = b + (c - b) * (1 - np.exp((df_3.h / a) ** 2 * (-1))) df_3 j k h g g2 h2 1 1.00 2.00 1.414214 0.32000 0.205240 2 2.75 4.25 2.186106 0.67875 0.424347 3 1.50 4.00 3.270691 1.10500 0.716214 4 1.50 4.50 4.540954 1.86250 0.925814 5 2.00 4.00 5.099020 1.28000 0.972693 plt.scatter(df_3.iloc[:, 2:3], df_3.iloc[:, 3:4]) plt.plot(df_3.iloc[:, 2:3], df_3.iloc[:, 4:5], color = 'red') plt.title("経験バリオグラム・理論バリオグラム") plt.xlabel("h") plt.ylabel("γ") plt.grid(True) 図6.15 誤差2乗和の計算2 rss = sum((df_3.g - df_3.g2) ** 2) rss 1.2008629589537976 図6.16 ソルバーを用いた理論バリオグラムのパラメータ決定 図6.17 決定した理論バリオグラム # 目的変数 def func(x): return sum([(df_3.g.iloc[i] - x[1] * (1 - np.exp((df_3.h.iloc[i] / x[0]) ** 2 * (-1)))) **2 for i in range(len(df_3))]) # 制約条件式 def cons(x): return np.array([x[0] - 2, x[1] - 1]) cons = ({'type': 'ineq', 'fun' : cons}) result = minimize(func, [3, 1.03], constraints=cons) print(result) a = result.x[0] b = 0 c = result.x[1] print(a, b, c) fun: 0.22117939583238733 jac: array([ 0.00044499, -0.00123285]) message: 'Optimization terminated successfully.' nfev: 29 nit: 7 njev: 7 status: 0 success: True x: array([2.91004696, 1.63761046]) 2.910046955029522 0 1.637610456087436 # 決定した理論バリオグラム plt.scatter(df_3.iloc[:, 2:3], df_3.iloc[:, 3:4]) plt.plot(df_3.iloc[:, 2:3], df_3.iloc[:, 5:6], color = 'red') plt.title("経験バリオグラム・理論バリオグラム") plt.xlabel("h") plt.ylabel("γ") plt.grid(True) (2) 単純クリギング 図6.18 観測データ間の共分散行列 a = 2.91 b = 0 c = 1.64 df_cov = df_dist.copy() for i in range(len(df_dist)): for j in range(len(df_dist)): df_cov.iloc[i, j] = c * np.exp((df_dist.iloc[i, j] / a) ** 2 * (-1)) df_cov 0 1 2 3 4 0 1.640000 1.295007 0.783983 0.154570 0.386003 1 1.295007 1.640000 0.550109 0.076104 0.133358 2 0.783983 0.550109 1.640000 0.992838 0.963956 3 0.154570 0.076104 0.992838 1.640000 0.992838 4 0.386003 0.133358 0.963956 0.992838 1.640000 図6.20 共分散行列の逆行列計算 df_cov_inv = pd.DataFrame(np.linalg.inv(df_cov)) df_cov_inv 0 1 2 3 4 0 1.998142 -1.383630 -0.570299 0.370644 -0.246963 1 -1.383630 1.681016 0.019621 -0.105598 0.241365 2 -0.570299 0.019621 1.453866 -0.616057 -0.348961 3 0.370644 -0.105598 -0.616057 1.232949 -0.462960 4 -0.246963 0.241365 -0.348961 -0.462960 1.133639 図6.21 推定点と観測点の座標 図6.22 推定点と観測点間の距離計算 x = 1.5 y = 3 df_simple = df.copy() df_simple['d'] = np.sqrt((df['x'] - x) ** 2 + (df['y'] - y) ** 2) df_simple x y z d 0 2.0 4.0 5.3 1.118034 1 1.0 3.0 4.5 0.500000 2 4.0 2.5 4.6 2.549510 3 6.0 2.0 2.9 4.609772 4 5.5 4.0 3.2 4.123106 6.23図 推定点と観測点間の共分散行列 df_simple['B'] = c * np.exp((df_simple['d'] / a)**2 * (-1)) df_simple x y z d B 0 2.0 4.0 5.3 1.118034 1.414935 1 1.0 3.0 4.5 0.500000 1.592291 2 4.0 2.5 4.6 2.549510 0.761176 3 6.0 2.0 2.9 4.609772 0.133358 4 5.5 4.0 3.2 4.123106 0.220284 6.24図 単純クリギングのクリギング係数計算 6.25図 出力されたクリギング係数 df_simple['w'] = np.dot(df_cov_inv, df_simple.B) df_simple x y z d B w 0 2.0 4.0 5.3 1.118034 1.414935 0.185027 1 1.0 3.0 4.5 0.500000 1.592291 0.772940 2 4.0 2.5 4.6 2.549510 0.761176 0.171927 3 6.0 2.0 2.9 4.609772 0.133358 -0.050194 4 5.5 4.0 3.2 4.123106 0.220284 -0.042750 6.26図 データの平均との残差 df_simple['za'] = df_simple['z'] - m df_simple x y z d B w za 0 2.0 4.0 5.3 1.118034 1.414935 0.185027 1.2 1 1.0 3.0 4.5 0.500000 1.592291 0.772940 0.4 2 4.0 2.5 4.6 2.549510 0.761176 0.171927 0.5 3 6.0 2.0 2.9 4.609772 0.133358 -0.050194 -1.2 4 5.5 4.0 3.2 4.123106 0.220284 -0.042750 -0.9 6.27図 推定点1における推定値 z_sk = m + sum(df_simple.w * df_simple.za) s2_sk = c - sum(df_simple.w * df_simple.B) cv = np.sqrt(s2_sk) / z_sk print(z_sk, s2_sk, cv) 4.815879929438207 0.03269746133842899 0.037547529485399515 (3) 通常クリギング df_simple = df_dist.copy() df_simple 0 1 2 3 4 0 0.000000 1.414214 2.500000 4.472136 3.500000 1 1.414214 0.000000 3.041381 5.099020 4.609772 2 2.500000 3.041381 0.000000 2.061553 2.121320 3 4.472136 5.099020 2.061553 0.000000 2.061553 4 3.500000 4.609772 2.121320 2.061553 0.000000 図6.28 観測点間のバリオグラム行列 a = 2.91 b = 0 c = 1.64 for i in range(len(df_simple)): for j in range(len(df_simple.columns)): df_simple.iloc[i, j] = c * (1 - np.exp((df_simple.iloc[i, j] / a) ** 2 * (-1))) df_simple[5] = 1 df_simple = df_simple.append(pd.Series([1, 1, 1, 1, 1, 0]), ignore_index=True) df_simple 0 1 2 3 4 5 0 0.000000 0.344993 0.856017 1.485430 1.253997 1 1 0.344993 0.000000 1.089891 1.563896 1.506642 1 2 0.856017 1.089891 0.000000 0.647162 0.676044 1 3 1.485430 1.563896 0.647162 0.000000 0.647162 1 4 1.253997 1.506642 0.676044 0.647162 0.000000 1 5 1.000000 1.000000 1.000000 1.000000 1.000000 0 図6.29 バリオグラム行列の逆行列 df_simple_inv = pd.DataFrame(np.linalg.inv(df_simple)) df_simple_inv 0 1 2 3 4 5 0 -1.976357 1.442380 0.562276 -0.316280 0.287981 0.129755 1 1.442380 -1.522582 -0.041257 0.252207 -0.130748 0.349919 2 0.562276 -0.041257 -1.450911 0.596036 0.333855 -0.047786 3 -0.316280 0.252207 0.596036 -1.097284 0.565320 0.323801 4 0.287981 -0.130748 0.333855 0.565320 -1.056408 0.244310 5 0.129755 0.349919 -0.047786 0.323801 0.244310 -0.867162 図6.30 観測点と推定点間のバリオグラム行列 df_simple_2 = df.copy() _0 = 1.5 y_0 = 3 df_simple_2['h'] = np.sqrt((df_simple_2['x'] - x_0) ** 2 + (df_simple_2['y'] - y_0) ** 2) df_simple_2['B'] = c * (1 - np.exp((df_simple_2['h'] / a) ** 2 * (-1))) s_no_name = pd.Series([1], index=['B']) s_no_name df_simple_2 = df_simple_2.append(s_no_name, ignore_index=True) df_simple_2 x y z h B 0 2.0 4.0 5.3 1.118034 0.225065 1 1.0 3.0 4.5 0.500000 0.047709 2 4.0 2.5 4.6 2.549510 0.878824 3 6.0 2.0 2.9 4.609772 1.506642 4 5.5 4.0 3.2 4.123106 1.419716 図6.31 通常クリギングのクリギング係数計算 df_simple_2['w'] = np.dot(df_simple_inv, df_simple_2['B']) df_simple_2 x y z h B w 0 2.0 4.0 5.3 1.118034 0.225065 0.180233 1 1.0 3.0 4.5 0.500000 0.047709 0.760010 2 4.0 2.5 4.6 2.549510 0.878824 0.173693 3 6.0 2.0 2.9 4.609772 1.506642 -0.062158 4 5.5 4.0 3.2 4.123106 1.419716 -0.051777 5 NaN NaN NaN NaN 1.000000 -0.028557 図6.32 通常クリギングによる推定結果 z_ok = sum(df_simple_2['w'][0:5] * df_simple_2['z'][0:5]) s2_ok = df_simple_2['w'][len(df_simple_2) - 1] + sum(df_simple_2['w'][0:5] * df_simple_2['B'][0:5]) cv = np.sqrt(s2_ok) / z_ok print(z_ok, s2_ok, cv) 4.828319580970582 0.03375263348265488 0.038050277103642186 関数例が誤っていると思われる。正しくは =INDEX('データ(3)'!\$D\$4:\$H\$8,B11,C11) ↩ =(J6-K6)^2 がK6セルと指定されているが、O6セルから上にコピー ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Google Translation APIを利用してみよう!

Google Translation APIを使う方法 プロジェクト作成 課金を有効化 APIを有効化 認証情報の設定 GOOGLE_APPLICATION_CREDENTIALSのパスを通す クライアントライブラリのインストール コードを実行 の7つの手順を行えば大丈夫 1. プロジェクトの作成 リンクを参考にしてプロジェクトを作成してください 上図の青色「プロジェクトの選択ページに移動」をクリックしてプロジェクトの選択・または作成してください 2. 課金の有効化 リンクを参考にしてプロジェクトに対する課金を有効にしてください。 - 大まかな手順 1. 課金が有効になっているかどうかの確認 - Cloud Consoleの左上にあるナビゲーション メニュー(menu)を開き、[お支払い] を選択 - お支払いの概要や月の見積もりが出てきたら課金を有効になっている - もし請求先アカウントとリンクする、または、請求先アカウントを管理が出てきたら有効になっていない証拠 2. プロジェクトと請求先アカウントをリンクする 3. 請求先アカウントが存在しなかったら、請求先アカウントを作成する 3. APIの有効化 1.のリンクに記載されている下図のAPIの有効化をクリックしてAPIを有効にしてください 4. 認証情報の設定 認証情報の設定は以下の手順で行う 1. サービスアカウントの作成 2. サービスアカウントキーの作成 4.1 サービスアカウントの作成 1.のリンクに記載されている下図のサービスアカウントの作成をクリックしてサービスアカウントを以下の手順で作成してください 4.2 サービスアカウントキーの作成 下の手順で実行を行い、キーをダウンロードする ちなみに、サービスアカウントのメールアドレスは以下の図の場所にある ・左上の三本線をクリック ・認証情報をクリック ・サービスアカウントは下図のように画面下部に存在する。それをクリックしてキーを作成 5. GOOGLE_APPLICATION_CREDENTIALSのパスを通す 先程ダウンロードしたJsonファイルのキーのパスをメモしておく Macユーザは以下のコマンドを実行 Windowsユーザは以下のコマンドを実行 6. クライアントライブラリのインストール 今回私はPythonを用いてAPIを実行するので以下のコマンドを実行 以下のコマンドは使用するプログラミング言語によって異なるので、Python以外のユーザーは1.のリンクから下記の扱いたい言語を選択してインストール 7.コードの実行 これでGoogle Translation APIを利用する準備が整いました。 サンプルコードはこちらにあります。 以下のサンプルコードを実行しましょう。すると下図の出力が出るはずです translate_text.py def translate_text(target, text): """Translates text into the target language. Target must be an ISO 639-1 language code. See https://g.co/cloud/translate/v2/translate-reference#supported_languages """ import six from google.cloud import translate_v2 as translate translate_client = translate.Client() if isinstance(text, six.binary_type): text = text.decode("utf-8") # Text can also be a sequence of strings, in which case this method # will return a sequence of results for each text. result = translate_client.translate(text, target_language=target) print(u"Text: {}".format(result["input"])) print(u"Translation: {}".format(result["translatedText"])) print(u"Detected source language: {}".format(result["detectedSourceLanguage"])) if __name__ == "__main__": # execute only if run as a script translate_text("en","私はペンです")
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【競プロ】ソートされた2次元行列から要素を探し出す3通りの方法

本記事では,ソートされた行列から特定の要素を探し出すアルゴリズムを3通り紹介します.ソートされた行列とは ある要素より右側にある数字はその要素以上 ある要素より下側にある数字はその要素以上 を満たすもので,例えば下の図のような行列です. 行列のサイズは$h*w$とします.この中に例えば「8」があるかどうかを判定し,あればそのインデックス(座標)を返すことが目標です(複数ある場合はどのインデックスを返してもいいものとします). まず1次元配列のおさらい まず,ソートされた1次元の配列から特定の要素を探索する場合,全要素を一つずつ調べていると$O(n)$時間($n$は配列の長さ)かかりますが,2分探索によって$O(\log n)$にできることが一般的に知られています.Pythonでは$bisect.bisect\_left()$などを使えば何も知らなくても勝手にできてしまいます. import bisect A = [1,4,5,5,7,8,10] idx1 = bisect.bisect_left(A, 5) # 5以上の最小のインデックスを返す print(idx1) # 2 idx2 = bisect.bisect(A, 5) # 5より大きい最小のインデックスを返す, bisect_rightも同様. print(idx2) # 4 ただこれでは元も子もないので,最も基本的な二分探索のやり方をおさらいします.配列を$A$,探したい値を$key$とします.まず配列の両端に二つのポインター$left = 0$と$right = n-1$を置きます.そして,$mid = \frac{left + right}{2}$ として $A[mid] < key$ ならば $left = mid+1$とする $A[mid] = key$ ならば発見できたのでそこで終了. $A[mid] > key$ ならば $right = mid-1$とする を,$left <= right$である限り繰り返します.$left > right$になっても発見できなければ,$key$は$A$の中になかったということになります(判定条件や境界条件には色々と流儀がありますが,単純な二分探索はこれで十分動くはずです). def binary_search(A, key): n = len(A) left, right = 0, n - 1 while left <= right: mid = (left+right) // 2 if A[mid] < key: left = mid + 1 elif A[mid] == key: return mid else: right = mid - 1 return -1 二分探索の肝は,$mid$という一つの値を調べることで,その左右どちらかの領域にある要素は全て調べる必要がなくなるということです.これによって調べる要素の個数が減り,$O(\log n)$で探索することができるようになりました.これを生かして,行列に対してもなるべく探索する要素を減らせる方法を考えていきます. 2次元配列の探索1: O(h log w) の方法 まずはじめに思いつくのは,各行(または各列)に対して配列の2分探索を行う方法です.これは全ての行または列に対してfor文を回すだけで実行できます. def search_matrix(self, A: List[List[int]], key: int) -> tuple: h = len(A) w = len(A[0]) for i in range(h): # 各行について配列の二分探索を行う left, right = 0, w - 1 while left <= right: mid = (left+right) // 2 if A[i][mid] < key: left = mid + 1 elif A[i][mid] == key: return (i, mid) else: right = mid - 1 return (-1, -1) # 発見できなかった場合 この計算量は$O(min(h \log w, w \log h))$となります.$h, w$の大小に合わせて,列を探索するか行を探索するかを決めれば良いです.これでも十分な速さで動くアルゴリズムと言えます. 2次元配列の探索2: O(h + w) の方法 ただ,これよりも速く,スマートな方法があります.例えば,現在行列の中の右上$(0, w-1)$にいるとします.もし,$key < A[0][w-1]$だった場合何が言えるでしょうか?行列中のある要素の下にある数は常にその要素以上であることが保証されています.既に$A[0][w-1]$が$key$より大きいのですから,これより下の数は必ず$key$より大きいことがわかり,もう探索する必要がありません. よって次は$A[:][:w-2]$を探索します.この行列の右上$A[0][w-2]$に関して,今度は$key > A[0][w-2]$が成り立っていたとします.$A[0][w-2]$と同じ行でより左側にある要素は常に$A[0][w-2]$以下なので,これらに$key$は含まれません.よって次は$A[1:][:w-2]$の中を探せば十分でしょう. これを繰り返していくと,いずれ$key$が見つかるか,もしくは探す行列がなくなって終わります.ここでは毎回探したい行列の右上の成分を$key$と比較することを繰り返していますが,これは次のような操作をしていると言い換えることができます. 行列の右上からスタートする 現在の要素$(i, j)$と$key$を比較 $A[i][j] < key$ならば次は$(i+1, j)$を探索 $A[i][j] == key$ならば発見,終了 $A[i][j] > key$ならば次は$(i, j-1)$を探索 もし$i >= h$または$j < 0$になれば発見できず終了 これはつまり,行列の右上から左下の方へと斜めに要素を探索していくだけで全要素を探索できるということです. def search_matrix(self, A: List[List[int]], key: int) -> tuple: h = len(A) w = len(A[0]) i, j = 0, w - 1 # 右上からスタート while i < w and j >= 0: # 行列の範囲から出てしまうまで続ける if matrix[row][column] < key: i += 1 elif A[i][j] == key: return (i, j) else: j -= 1 return (-1, -1) # 発見できなかった場合 この場合,最も時間がかかるケースは右上から左下まで移動するケースなので,時間計算量は$O(h + w)$になります. 2次元配列の探索3: O(h log (w/h)) の方法 方法2でも十分速いのですが,なんというか二分探索っぽくありません.2次元でも二分探索する方法はないのでしょうか? 実はあります.   ある要素$A[i][j]$が$key$より小さかった場合に何が言えるかもう一度考えてみましょう.$A[i][j]$の左側の要素と上側の要素は$key$以下です.それだけでなく,それらに囲まれた左上の要素全てが$key$より小さくなるはずです.逆に$A[i][j] > key$だった場合,$A[i][j]$を右上とした矩形領域の要素は全て$key$より大きいことになります. 例えば行列の$i$行目で二分探索を行い,求める$key$が見つからなかったとします.二分探索が終わった後,2つのポインタは$right+1 = left$という位置関係にあり,$right$は$key$より小さいもののうち最大の要素,$left$は$key$より大きいもののうち最小の要素にあります.すると,$A[i][right]$を右下とした行列の要素は全て$key$より小さく,$A[i][left]$を左上とした行列の要素は全て$key$より大きいことがわかります. このエリアに$key$はありません.よって残された右上と左下の領域のみを探索すれば良いことになります.この2つの領域は 右上領域: 縦 $0$ ~ $i-1$, 横 $left$ ~ $w-1$ 左下領域: 縦 $i+1$ ~ $h-1$, 横 $0$ ~ $right$ となります. それが求まれば,次は2つの領域それぞれについて同じように探索していきます.小行列の範囲がなくなれば探索終了です.探索される小行列の数は再帰的に増えていきますが,その都度探索しなくていい領域も分かってくるので,全体として探索する領域は少なくなり,効率的に計算ができます.なお二分探索は領域のちょうど真ん中の行で行えば十分だと思われます. def search_matrix(self, A: List[List[int]], key: int) -> tuple: h = len(A) w = len(A[0]) def search_subarea(top_row, left_col, bottom_row, right_col): # 部分行列を探索する関数 if top_row > bottom_row or left_col > right_col: # 領域が存在しなければ探索しない return (-1, -1) mid_row = (top_row + bottom_row) // 2 # 真ん中の行 left, right = left_col, right_col while left <= right: # 二分探索 mid_col = (left+right) // 2 if A[mid_row][mid_col] < key: left = mid_col + 1 elif A[mid_row][mid_col] == key: return (mid_row, mid_col) else: right = mid_col - 1 # 発見できなければ2つの部分行列を探索 upper_right = search_subarea(top_row, left, mid_row - 1, right_col) lower_left = search_subarea(mid_row + 1, left_col, bottom_row, right) if upper_right != (-1, -1): return upper_right elif lower_left != (-1, -1): return lower_left else: return (-1, -1) return search_subarea(0, 0, h-1, w-1) 上のコードでは、部分行列の中を再帰的に探索する$search\_subarea$関数を動かしています.入力の$(top\_row, left\_col, bottom\_row, right\_col)$はそれぞれ,探索する行列の上端,左端,下端,右端の座標を表しています.中央の行である$mid\_row$に対して二分探索を行い,$key$を発見できなければ$left, right$を元に2つの小行列を再帰的に探索していきます. この方法の計算量を考えるのは少々複雑です.縦$h$,横$w$($h < w$とする)の行列に対しては,まず$log(w)$で$mid\_col$を探索し,その後最悪ケースでちょうど半分の地点で分割された2つの小行列を探索するため,計算時間は漸化的に $T(h, w) = \log w + 2 * T(h/2, w/2)$ と書けます.stack-overflowの記事によれば,この漸化式を解くとどうやら $O(h * \log \frac{w}{h})$ のオーダーになるらしいです.(私もよくわかっていないので教えてください...) 結局どれが速いのか 方法1: $O(h \log w)$ ($h < w$のとき) 方法2: $O(h + w)$ 方法3: $O(h * \log \frac{w}{h})$ ($h < w$のとき) 結局どれが速いのでしょうか?まず方法1と方法3に関しては,時間計算量の式を見るに方法3の方が常に速いと言えます.方法3はうまく探索範囲を絞ることで二分探索する行の範囲を減らしているので,直感的にも方法3の方が速いのは納得がいきます. 問題は方法2と方法3の比較ですが,これは場合によります.具体的には,$h$と$w$の比によってどちらが適しているかが変化します.方法1の時間計算量は長辺の長さがボトルネックになる一方,方法3の時間計算量はlogの外に出ている短辺の長さがボトルネックになります.厳密な議論ができず申し訳ないのですが,stack-overflowの記事によれば,$100 \times 100$の行列では方法1の方が速く,$10 \times 1000$の行列では方法3の方が速くなるようです. 例えば極端な例として$1 \times 10000$の行列を考えた場合,方法2は端から全要素を探索するような挙動になってしまうため,二分探索を行う方法3のほうが明らかに速いです. 実装面を考えると方法2がシンプルで良いですが,縦横比が極端に違うような配列では方法3を使う方が良いのだと思われます. まとめ 2次元のソートされた行列から要素を探す3通りの方法を紹介しました.それぞれ長所と短所があることがわかったので,今度街でソートされた行列を見かけた際には,状況に応じてアルゴリズムを使い分けられると周りの友達に一目置かれること間違いなしです.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

第二回日本最強プログラマー学生選手権 参戦記

第二回日本最強プログラマー学生選手権 参戦記 難易度絶壁の直前に数学問題置くのやめて……. 久々の三桁パフォ……. JSC2021A - Competition 3分で突破. 書くだけ. X, Y, Z = map(int, input().split()) print((Y * Z - 1) // X) JSC2021B - Xor of Sequences 3分で突破. 書くだけ. N, M = map(int, input().split()) A = set(map(int, input().split())) B = set(map(int, input().split())) print(*sorted(A ^ B)) JSC2021C - Max GCD 2 9分で突破. B≤2×105 だからシングルループは OK. GCD を a と置くと x は A 以上の最小の a の倍数とし、y は x + a として、y ≤ B であれば、その a は答えとして有効であるということになる. あとは B までループを回してやれよい. A, B = map(int, input().split()) for a in range(1, B + 1): x = (A + (a - 1)) // a * a y = x + a if y > B: continue result = a print(result) JSC2021D - Nowhere P 64分で突破. Wolfram|Alpha に g(1)=P-1, g(n+1) = g(n) * (P-2) を入力したら g(n) = (P - 1)(P - 2)n-1 と教えてくれたので解けた. m = 1000000007 N, P = map(int, input().split()) print((P - 1) * pow(P - 2, N - 1, m) % m)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pandas結合

結合方法 concat結合 シンプルな結合 merge結合 内部結合 外部結合 左結合 右結合 使うdf import pandas as pd df1 = pd.DataFrame({ "ID" : [1,2,3], "Country" : ["鈴木","中村","小島"], }) df2 = pd.DataFrame({ "ID" : [2,3,4], "Address" : ["Hikone","Tokyo","Nagoya"], }) concat結合 シンプルに右にくっつくか、下にくっつくか。 右にくっつく concat_df = pd.concat([df1, df2], axis=1) 下にくっつく concat_df = pd.concat([df1, df2], axis=0) merge結合 キー(ID)に紐づいて結合する。 内部結合 2つのdfのキーが一致するデータのみ抽出 inner_df = pd.merge(df1, df2, on="ID", how="inner") 外部結合 2つのdfのキーに紐づくデータを全て抽出 outer_df = pd.merge(df1, df2, on="ID", how="outer") 左結合 (引数で)左指定のdfのキーに一致するデータを抽出 left_df = pd.merge(df1, df2, on="ID", how="left") 右結合 (引数で)右指定のdfのキーに一致するデータを抽出 right_df = pd.merge(df1, df2, on="ID", how="right") 漏れなくデータを結合したいのか、どこに欠損を出したくないのか、で使い分ける感じですかね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

yukicoder contest 291 参戦記

yukicoder contest 291 参戦記 A 1476 esreveR dna esreveR ai,i と aN-i,N-i の組み合わせ、それぞれが4C2で6パターンとなり、それぞれの組み合わせは互いに影響しあわないので、6⌊N/2⌋が答えとなる. m = 998244353 N = int(input()) print(pow(6, N // 2, m))
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flaskを使用したTwitter ログイン連携メモ

Flaskを使用したTwitterログイン連携についてメモする。 Google連携やFacebook連携を試したもののTwitter版 大まかな処理の流れ リクエストトークンを取得する。※GoogleやFacebookなどと異なる点。 リクエストトークンを指定してTwitterへ認可リクエスト(Authorization Request)を行う。 ユーザー認証/同意を行い、認可レスポンスを受け取る。 認可レスポンスを使ってトークンリクエストを行う。 事前準備 Twitterデベロッパーコンソールからアプリケーションを登録する。 API Key and Secretの取得する。 コールバック(リダイレクト)URIの設定する。 今回はhttp://localhost:5000/callback を設定する。 実装 アクセストークンを取得するまでのコード from requests_oauthlib import OAuth1Session from flask import Flask, jsonify, request, redirect import urllib.parse as parse # App Info api_key = "<YOUR API KEY>" api_secret = "<YOUR API SECRET>" # Twitter Endpoint twitter_base_url = 'https://api.twitter.com' authorization_endpoint = twitter_base_url + '/oauth/authenticate' request_token_endpoint = twitter_base_url + '/oauth/request_token' token_endpoint = twitter_base_url + '/oauth/access_token' app = Flask(__name__) @app.route("/login") def login(): # 1.リクエストトークンを取得する。 # (Step 1: Obtaining a request token:https://developer.twitter.com/en/docs/authentication/guides/log-in-with-twitter) twitter = OAuth1Session(api_key, api_secret) oauth_callback = request.args.get('oauth_callback') res = twitter.post(request_token_endpoint, params={ 'oauth_callback': oauth_callback}) request_token = dict(parse.parse_qsl(res.content.decode("utf-8"))) oauth_token = request_token['oauth_token'] # 2.リクエストトークンを指定してTwitterへ認可リクエスト(Authorization Request)を行う。 # (Step 2: Redirecting the user:https://developer.twitter.com/en/docs/authentication/guides/log-in-with-twitter#tab2) return redirect(authorization_endpoint+'?{}'.format(parse.urlencode({ 'oauth_token': oauth_token }))) @app.route("/callback") def callback(): # 3.ユーザー認証/同意を行い、認可レスポンスを受け取る。 oauth_verifier = request.args.get('oauth_verifier') oauth_token = request.args.get('oauth_token') # 4.認可レスポンスを使ってトークンリクエストを行う。 # (Step 3: Converting the request token to an access token:https://developer.twitter.com/en/docs/authentication/guides/log-in-with-twitter#tab3) twitter = OAuth1Session( api_key, api_secret, oauth_token ) res = twitter.post( token_endpoint, params={'oauth_verifier': oauth_verifier} ) access_token = dict(parse.parse_qsl(res.content.decode("utf-8"))) return jsonify(access_token) if __name__ == "__main__": app.run(debug=True) 参考情報 3-legged authorization Log in with Twitter [Python] OAuth認証でTwitter連携/ログインを実装する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

altairでとあるアイドルグループのメンバー在籍期間のチャート化してみた

記事の内容 数年前から転校少女*というアイドルグループを応援しているのですが、メンバーの入れ替わりが激しく誰がいつ在籍していたのか分からなくなったので「altair」というライブラリを使って可視化してみるという内容です。 altairのインストール pipでインストールします pip install altair 準備データ tenkou.csv No,Member,Start,End,Color,Color Name 1,松井さやか,2014/11/01,2021/05/01,#ff6666,red 2,塩川莉世,2014/11/01,2021/05/01,#ff99cc,pink 3,岡田夢以,2014/11/01,2019/11/16,#66ccff,blue 4,栗田恵美,2014/11/01,2018/04/28,#66ff66,green 5,松本香穂,2014/11/01,2018/04/28,#ffff66,yellow 6,古森結衣,2017/01/28,2018/04/28,#FFA500,orange 7,渚まお,2017/01/28,2019/04/30,#DFA0D2,purple 8,千葉妃理,2017/06/01,2018/07/26,#0000ff,black 9,寺田葵,2018/09/16,2020/12/26,#00A474,black 10,小西杏優,2019/08/25,2020/01/12,#FFA533,black 11,上原わかな,2019/08/25,2020/04/23,#800080,black 12,小倉月奏,2019/08/25,2021/04/16,#FFFF00,black 13,佐藤かれん,2020/11/21,2021/05/01,#800088,black 14,成島有咲,2020/11/21,2021/05/01,#66ddff,black 15,佐々木美紅,2020/11/21,2021/05/01,#FFA566,black データの内容はNo(ソート用)、メンバーの名前、在籍期間を表すstartとend、グラフの色を設定するためのColor、Color Nameを用意しました。 まだ在籍しているメンバーのendはとりあえずで「2021/05/01」を設定しています。 コード1 早速コードを書いてみました chart.py import pandas as pd import altair as alt df = pd.read_csv("tenkou.csv") df["Start"] = pd.to_datetime(df["Start"]) df["End"] = pd.to_datetime(df["End"]) chart = alt.Chart(df).mark_bar().encode( x='Start', x2='End', y=alt.Y('Member',sort=list(df.sort_values(['No']))), color='Color' ).properties( width=1500, height=500 ).interactive() chart.show() 実行結果 画像なので分からないのですが、チャートの部分は横スクロールが出来るようになっています。 横スクロールが出来るようになってはいますが、何も設定していないと「properties」の「width」で指定した幅でデータが表示されるように横軸の間隔が調整されるみたいです。 問題と原因 用意したデータの「Color」にはメンバーの担当カラーを設定していたのですが、グラフの色が用意した色になっていない。 調べたところ、以下の部分は色を指定するのではなく、色分けするデータ群に対するラベルの意味合いみたいです。 color='Color' 例えば、一期生、二期生・・というデータを加えて、それぞれ色分けするような場合に使えます。 色はaltairが勝手に設定するみたいで、このやり方だと自分で色の設定は出来ないっぽい。 色の設定は別でやる必要あり。 コード2 chart2.py import pandas as pd import altair as alt df = pd.read_csv("tenkou.csv") df["Start"] = pd.to_datetime(df["Start"]) df["End"] = pd.to_datetime(df["End"]) chart = alt.Chart(df).mark_bar(color='Red').encode( x='Start', x2='End', y=alt.Y('Member',sort=list(df.sort_values(['No']))), ).properties( width=1500, height=500 ).interactive() chart.show() 変更点としては、mark_barに色を設定したのと、colorの設定を消しただけ 実行結果 設定した色に変更されました。 今回は赤だけを設定したのですが、メンバー毎に色を設定したくてリストで担当カラーを渡してみましたがエラーとなり失敗しました。 現状、自分で好きな色に設定することは難しそう 感想 割と簡単かつ綺麗に描画された点は良いなと思いました。 今後使っていく場合、ドキュメントが若干分かり辛いのと、情報が少ないので細かい使い方を調べるのには少し苦労しそう。 あとは、応援してるグループのメンバー在籍期間を可視化すると改めて入れ替わりが激しいなと思い知った。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Lambda内Lambdaの呼び出しで Invalid type for parameter Payload

Invalid type for parameter Payload Lambda内でLambdaを非同期で呼び出そうとして 以下のような呼び出しでエラー発生。 response = boto3.client('lambda').invoke( FunctionName='hogehoge-actions-reportstate', InvocationType='Event', Payload=directive ) 原因はPayloadをdictで渡していたこと。json.dumps()で変換後に渡すことで解決。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Lambda内Lambdaの呼び出しで Invalid type for parameter Payload

Invalid type for parameter Payload AWS Lambda内でLambdaを非同期で呼び出そうとして 以下のような呼び出しでエラー発生。 directive = { 'target': 'hogehoge', 'set': 'mogemoge' } response = boto3.client('lambda').invoke( FunctionName='hogehoge-actions-reportstate', InvocationType='Event', Payload=directive ) 原因はPayloadで渡す変数「directive」をdictで渡していたこと。json.dumps()で変換後に渡すことで解決。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonでpickleをJSONに保存する

python でpickleをJSONに保存する pickle.dump で得たバイナリを16進数文字列に変換してその文字列を保存します データベースのテキストフィールドにクラスオブジェクトを保存したかったというのが本記事の動機です 出力処理 対象オブジェクトを pickle.dump し、そのバイナリを16進数文字列に変換します def ObjectToJSON(obj): out = io.BytesIO() pickle.dump(obj, out) buf = out.getbuffer() hexBin = codecs.encode(buf, 'hex') hexStr = hexBin.decode("utf-8") return json.dumps(hexStr) 入力処理 出力処理で作成した16進数文字列をバイナリに変換しそれを pickle.load に与えます def JSONToObject(jsonStr): hexStr = json.loads(jsonStr) bin = bytes.fromhex(hexStr) input = io.BytesIO(bin) obj = pickle.load(input) return obj 実験ソースコード import codecs import copy import io import json import numpy as np import pickle class Hoge: def __init__(self): self.a = 0 self.b = 0.0 self.c = "" self.d = np.zeros(10) def DumpVars(self): for k in vars(self): print(k, self.__dict__[k]) def ObjectToJSON(obj): out = io.BytesIO() pickle.dump(obj, out) buf = out.getbuffer() hexBin = codecs.encode(buf, 'hex') hexStr = hexBin.decode("utf-8") return json.dumps(hexStr) def JSONToObject(jsonStr): hexStr = json.loads(jsonStr) bin = bytes.fromhex(hexStr) input = io.BytesIO(bin) obj = pickle.load(input) return obj def Test1(): print("# Test1") x = Hoge() x.a = 1 x.b = 1.2345 x.c = "ほげ" x.d = np.repeat(np.arange(3), 4).reshape(3, 4) x.DumpVars() jsonStr = ObjectToJSON(x) print(jsonStr) def Test2(): print("# Test2") jsonStr = '"8003635f5f6d61696e5f5f0a486f67650a7100298171017d71022858010000006171034b015801000000627104473ff3c083126e978d58010000006371055806000000e381bbe3819271065801000000647107636e756d70792e636f72652e6d756c746961727261790a5f7265636f6e7374727563740a7108636e756d70790a6e6461727261790a71094b0085710a430162710b87710c52710d284b014b034b0486710e636e756d70790a64747970650a710f5802000000693871108988877111527112284b0358010000003c71134e4e4e4affffffff4affffffff4b007471146289436000000000000000000000000000000000000000000000000000000000000000000100000000000000010000000000000001000000000000000100000000000000020000000000000002000000000000000200000000000000020000000000000071157471166275622e"' x = JSONToObject(jsonStr) x.DumpVars() def main(): Test1() Test2() if __name__ == "__main__": main() 以上です
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pandas データフレームの集約・抽出・集計・並び替えのコマンド集

pandasでデータフレームを扱う際に、よく使うコマンドを整理しました。 基礎統計量の確認 <データフレーム名>.describe() カラム内のデータ数を数える <データフレーム名>["<カラム名>"].value_counts() 列データのユニークな要素数を数える <データフレーム名>["<カラム名>"].nunique() groupby関数による集約 <データフレーム名>.groupby("<カラム名1">)["<カラム名2>"].value_counts() 特定の要素ごとの平均値、合計値、中央値を出す <データフレーム名>.groupby("<カラム名">).mean() <データフレーム名>.groupby("<カラム名">).sum() <データフレーム名>.groupby("<カラム名">).median() groupby関数で集約に用いたカラムをインデックスにしない場合 groupby("<カラム名>", as_index=False) groupby関数で2以上の列を指定する場合 groupby(["<カラム名1>","<カラム名2>"]) 件数を集計する場合(クロス集計) pd.crosstab(<データフレーム名>["<カラム名1>"], <データフレーム名>["<カラム名2>"]) 件数を集計する場合(クロス集計、行方向の合計値が全体で1になるように正規化したい場合) pd.crosstab(<データフレーム名>["<カラム名1>"], <データフレーム名>["<カラム名2>"], normalize = "index") 条件に該当したデータ抽出 <データフレーム名>[<データフレーム名>["<カラム名>"] == "値"] または <データフレーム名>.query("<カラム名> == '値'") データ並び替え # 昇順(デフォルト) <データフレーム名>.sort_values("<カラム名>") # 降順 <データフレーム名>.sort_values("<カラム名>", ascending=False) カラム名変更 <データフレーム名>.rename(columns={"<カラム名>":"<変更後カラム名>"})
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】VSCodeで保存時のフォーマットでimportのソートをさせないようにする

概要 Pythonでライブラリの読み込み対象のパスを追加する際、Python Tips:ライブラリ読み込み対象ディレクトリを追加したいの記事にあるように、sys.path.append("/Users/username/Desktop")のような感じで、importの前に探索パスを追加したいことがあります。この時にVSCodeのPython拡張機能で保存時のフォーマットを設定していると、importが自動でソートされてしまい、意図した順番にならないことがあります。 もちろん保存時のフォーマットをしなければ良いのですが、保存時のフォーマットを有効にしたままimportのソートを無効にするにはどうすれば良いのかというのをメモ書きしておきます。 対応 Disable python import sorting in VSCodeのstackoverflowの記事にある通り、保存時のE402のチェックを無効にすれば自動でのimportのソートが行われません。E402とはModule level import not at top of file (E402)の記事にある通り、importをファイルの上側に書かなければいけないというルールです。 上記のstackoverflowの記事にもありますが、VSCodeのsettings.jsonに以下の設定を追加します。 settings.json "python.formatting.autopep8Args": ["--ignore", "E402"]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

厚生労働省オープンデータからCOVID-19 PCR検査陽性者数をツィートするDockerコンテナを作ってみた

この記事の目的 厚生労働省が公開しているオープンデータをフェッチして、今日のPCR PCR検査陽性者数をTwitterにツィートするDockerコンテナを作成します。 開発・実行環境 Windows ホストPC エディション:Windows 10 Home バージョン:20H2 OS ビルド:19042.867 エクスペリエンス:Windows Feature Experience Pack 120.2212.551.0 Docker Desktop 3.2.2 Docker 20.10.5 build 55c4c88 厚生労働省のオープンデータをフェッチする データをフェッチするスクリプトを開発する 厚生労働省のオープンデータの保存場所を調査します。このページを眺めるとオープンデータは"https://www.mhlw.go.jp/content/"にcsv形式で保存されていることがわかります。 Pythonのrequestsモジュールを使ってオープンデータをフェッチしましょう。今回はこんなコードを書きました。 mhlw/uri_list.py # coding: UTF-8 uri_list = [ 'pcr_positive_daily.csv', 'pcr_tested_daily.csv', 'cases_total.csv' ] if __name__ == '__main__': from sys import stdout for uri in uri_list: stdout.write(f'{uri}\n') オープンデータはもっとあるのですが、今回はuri_listで格納した3ファイルをフェッチします。実際に使うのはpcr_positive_daily.csvだけです。 mhlw/fetch.py # coding: UTF-8 if __name__ == '__main__': from sys import argv, path from urllib.parse import urljoin path.append('http') import http_client endpoint_url = argv[1] uri_list = argv[2:] for uri in uri_list: url = urljoin(endpoint_url, uri) http = http_client.HttpClient(url) res = http.sync_get() with open(uri, 'w') as f: f.write(res) http/http_client.py # coding: UTF-8 class HttpClient: def __init__(self, url): self._url = url def sync_get(self): import requests self._res = requests.get(self._url) body = None if self._res.status_code == 200: body = self._res.text else: raise RuntimeError('The http request is failed.') return body 次のように実行しました。lsコマンドを実行すると3つのcsvファイルがフェッチできていることがわかります。 (base) root@251c769e696d:/mnt/docker/anaconda3/covid-19_mhlw_open_data# python mhlw/uri_list.py | xargs python mhlw/fetch.py "https://www.mhlw.go.jp/content/" (base) root@251c769e696d:/mnt/docker/anaconda3/covid-19_mhlw_open_data# ls http mhlw pcr_positive_daily.csv pcr_tested_daily.csv もともとのcsvの文字コードがutf-8ではないため(たぶんShift-JISかな)、ヘッダの日本語が文字化けしちゃいました。今回は気にせずこのまま進めます。 実行するDockerfileを作成する 次に開発したスクリプトを動作させるDockerコンテナを作ります。 Dockerfile FROM python:3.9.4-slim-buster ENV WORKDIR_NAME "/app" ENV DIR_HTTP "http" ENV DIR_MHLW "mhlw" ENV ENDPOINT_URL "https://www.mhlw.go.jp/content/" WORKDIR $WORKDIR_NAME RUN mkdir $DIR_HTTP RUN mkdir $DIR_MHLW COPY ./${DIR_HTTP}/*.py ${WORKDIR_NAME}/${DIR_HTTP}/ COPY ./${DIR_MHLW}/*.py ${WORKDIR_NAME}/${DIR_MHLW}/ RUN pip install requests CMD python ${DIR_MHLW}/uri_list.py | xargs python ${DIR_MHLW}/fetch.py ${EHLW_ENDPOINT_URL} ビルドして実行します。今回は期待値が得られているか確認したいので、次のコマンドでイメージのビルド、コンテナの作成・実行を行います。 PS > docker build --tag=covid19 . PS > docker run covid19 残念ながらこのままだとPythonスクリプトは期待通りに動作しません。なぜならDockerコンテナからエンドポイントのドメイン mhlw.go.jp の名前解決ができないからです。この辺はDockerコンテナ・ネットワークを解説しないとうまく説明できないので別記事で解説しようと思いますが、一言で説明するとDockerコンテナが属しているネットワーク・サブネットとホストPCが属しているネットワーク・サブネットが異なります。なのでコンテナが属しているネットワークからインターネットに直接アクセスできません。 余談ですが、デバッグしたい場合はコンテナにインタラクティブモードで接続するのがおススメです。 PS > docker run --interactive --tty covid19 bash コンテナからホスト経由でインターネットにアクセスする 真面目に対応するのであれば、ホストPCのNATの設定を変更したりするのでしょうが(すみません、ちゃんと調査していません)、今回は開発環境兼実行環境のWindowsホストPCで実行するので、Dockerコンテナをhostネットワークに紐づけて実行します。 こんな感じで実行しました。 PS > docker run --net host covid19 cases_total.csv fetch.py pcr_positive_daily.csv pcr_tested_daily.csv uri_list.py PS > hostネットワークって何ぞや、ということですが、簡単に説明しますとdockerをインストールするとデフォルトで3つの仮想ネットワークを作成します。そのうちの1つがhostネットワークでDockerエンジンが動作しているホストPCと同じサブネットの仮想ネットワークにコンテナが接続されます。Dockerの仮想ネットワークはdocker network lsコマンドで確認できます。 PS > docker network ls NETWORK ID NAME DRIVER SCOPE e9f868af4a34 bridge bridge local 4b2fd62d429a host host local b475f904ff07 none null local 詳しくは公式ページをご覧ください。僕もそのうち解説します。(3年くらいにすごくまじめに調べた記憶があるのですが、今はうるおぼえ故に。すみません。) 補足 オープンデータのフェッチのためにPythonスクリプト書いたけれども、curlでもよかったなぁ。 今日のPCR検査陽性者数を求める PCR検査陽性者数を分析するスクリプトを開発する まずは分析のスタートポイントを実装します。 mhlw/analysis.py # coding: UTF-8 scripts = { 'pcr_positive_daily.csv': 'analysis_pcr_positive_daily.py', 'pcr_tested_daily.csv' : None, 'cases_total.csv' : None } if __name__ == '__main__': from sys import argv, path from os.path import dirname, join from subprocess import Popen, PIPE import binascii file_name = argv[1] script = scripts.get(file_name) if script != None: data = None with open(argv[1], 'rb') as input: script = join(dirname(argv[0]), script) with Popen(['python', script], stdin=input, stdout=PIPE) as pipe: data = pipe.stdout.read() res = data.decode(encoding='utf-8') print(f'\"{res}\"') 次にpcr_positive_daily.csvを解析して、本日のPCR検査陽性者数を分析するスクリプトを作成します。Pandaを使えば簡単に詳細な解析できると思いますが、今回は割愛します。(Pandaはちょっと触ったぐらいなので今後調査したいです。) analysis_pcr_positive_daily.py # coding: UTF-8 if __name__ == '__main__': from sys import argv, path from sys import stdin, stdout from csv import reader from datetime import datetime data = list(reader(stdin)) header = data[:1][0] body = data[1:] now = datetime.now() key = f'{now.year}/{now.month}/{now.day}' item = [ item for item in body if item[0] == key ] stdout.write(f'As of {key}, the number of positive PCR tests in Japan is {item[0][1]}.') 開発環境で実行します。こんな感じです。 (base) root@251c769e696d:/mnt/docker/anaconda3/covid-19_mhlw_open_data# ls -1 *.csv | xargs -l python mhlw/analysis.py "As of 2021/4/15, the number of positive PCR tests in Japan is 4570." (base) root@251c769e696d:/mnt/docker/anaconda3/covid-19_mhlw_open_data# Dockerfileを更新する 開発環境で動作したのでDockerfileを更新して動作確認をします。 FROM python:3.9.4-slim-buster ENV WORKDIR_NAME "/app" ENV DIR_HTTP "http" ENV DIR_MHLW "mhlw" ENV ENDPOINT_URL "https://www.mhlw.go.jp/content/" WORKDIR $WORKDIR_NAME RUN mkdir $DIR_HTTP RUN mkdir $DIR_MHLW COPY ./${DIR_HTTP}/*.py ${WORKDIR_NAME}/${DIR_HTTP}/ COPY ./${DIR_MHLW}/*.py ${WORKDIR_NAME}/${DIR_MHLW}/ RUN pip install requests CMD python ${DIR_MHLW}/uri_list.py | xargs python ${DIR_MHLW}/fetch.py ${EHLW_ENDPOINT_URL} && ls *.csv | xargs -l python ${DIR_MHLW}/analysis.py CMDが長くなってきましたね。シェルスクリプトにまとめたほうが良いかもしれません。イメージからコンテナを作成して実行します。 PS > docker build --tag=covid19 . PS > docker run --net host covid19 "As of 2021/4/15, the number of positive PCR tests in Japan is 4570." PS > ツィートする 本日の陽性者数が分析できたので最後にツィートします。Python Twitter Toolsを使います。 pipでインストールできるようです。 # pip install twitter 今回はこんなコードを書きました。 twitter/twitter_client.py # coding: UTF-8 if __name__ == '__main__': from sys import argv, path from datetime import datetime from requests_oauthlib import OAuth1Session from twitter import * path.append('.') import runtime_env tweet = (argv[1]).replace('"', '') endpoint_url = runtime_env.twitter_endpoint_url() token = runtime_env.twitter_access_token() token_secret = runtime_env.twitter_access_token_secret() consumer_key = runtime_env.twitter_api_key() consumer_secret = runtime_env.twitter_api_secret_key() t = Twitter(auth=OAuth(token, token_secret, consumer_key, consumer_secret)) t.statuses.update(status=tweet) Twitterにツィートするにはアカウントのトークンが必要になりますが、スクリプトにコーディングするのは危険なので環境変数から読み込むようにしています。環境変数を読みだすスクリプトを記述しました。 runtime_env.py # coding: UTF-8 def twitter_endpoint_url(): import os return os.environ['TWITTER_POST_ENDPOINT_URL'] def twitter_access_token(): import os return os.environ['TWITTER_ACCESS_TOKEN'] def twitter_access_token_secret(): import os return os.environ['TWITTER_TOKEN_SECRET'] def twitter_api_key(): import os return os.environ['TWITTER_API_KEY'] def twitter_api_secret_key(): import os return os.environ['TWITTER_API_SECRET_KEY'] Dockerfileを更新します。こんな感じで記述しました。 FROM python:3.9.4-slim-buster ENV WORKDIR_NAME "/app" ENV DIR_HTTP "http" ENV DIR_MHLW "mhlw" ENV DIR_TWITTER "twitter" ENV EHLW_ENDPOINT_URL "https://www.mhlw.go.jp/content/" ENV TWITTER_POST_ENDPOINT_URL "https://api.twitter.com/1.1/statuses/update.json" ENV TWITTER_ACCESS_TOKEN "<アクセストークン>" ENV TWITTER_TOKEN_SECRET "<アクセストークン・シークレット>" ENV TWITTER_API_KEY "<APIキー>" ENV TWITTER_API_SECRET_KEY "<APIキー・シークレット>" WORKDIR $WORKDIR_NAME RUN mkdir $DIR_HTTP RUN mkdir $DIR_MHLW RUN mkdir $DIR_TWITTER COPY ./*.py ${WORKDIR_NAME}/ COPY ./${DIR_HTTP}/*.py ${WORKDIR_NAME}/${DIR_HTTP}/ COPY ./${DIR_MHLW}/*.py ${WORKDIR_NAME}/${DIR_MHLW}/ COPY ./${DIR_TWITTER}/*.py ${WORKDIR_NAME}/${DIR_TWITTER}/ RUN pip install requests RUN pip install twitter RUN pip install requests-oauthlib CMD python ${DIR_MHLW}/uri_list.py | xargs python ${DIR_MHLW}/fetch.py ${EHLW_ENDPOINT_URL} && ls *.csv | xargs -l python ${DIR_MHLW}/analysis.py | xargs -0 python ${DIR_TWITTER}/twitter_client.py イマージからコンテナを作成して実行します。こんな感じです。 PS > docker build --tag=covid19 . PS > docker run --net host covid19 PS > Twitterのタイムラインを見るとツィートできていました。 まとめ 結構おおらかなスクリプトになってしまったので真面目に作るならもうちょいちゃんとしなきゃな、と思います。Dockerfile の CMD 行が長くなってしまったのでシェルスクリプトかPythonスクリプト化してもよいかもですね。 やっぱり厚生労働省の csv ファイルは curl 使えばよかったと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

株価分析(SMA) - リターンを求める

はじめに 前回はこんな記事を書きました。 今回はリターンがどれくらいになるのか求めてみたいと思います。 リターンの計算ですが、今まではしこしことトレーディングクラスなどを書いていたのですが、BackTestingという便利なライブラリを見つけたのでこれを使用することにします。 インストールは上のページにも書いてありますが下記のように行います。 $ pip install Backtesting まずはリターンを求めてみる BackTestingを使用するときには、Strategyクラスを実装する必要があります。 ここではゴールデンクロスで買う、デッドクロスで売る、という戦略でいくことにします。 long_term, short_termをクラス変数で定義しています。クラス変数として定義することで、後で書きますがoptimizeメソッドを使用して最適な組み合わせを求めることができるようになります。 initメソッドでSMAを求めています。Backtestingの中にもSMAを計算する処理はあるのですが、今後のことも考えて、TA-Libを使用することにしています。 nextメソッドは時系列データごとに呼び出されます。ここではゴールデンクロスで買い、デッドクロスで売るという単純な戦略を取ってます。 class SmaCross(Strategy): ''' SMS Cross Strategy ''' long_term = 75 short_term = 25 def init(self): close = self.data['Close'] self.long_sma = self.I(talib.SMA, close, self.long_term) self.short_sma = self.I(talib.SMA, close, self.short_term) def next(self): if crossover(self.short_sma, self.long_sma): self.buy() elif crossover(self.long_sma, self.short_sma): self.sell() では、リターンを求めてみます。 株価の取得は前と同じget_stockメソッドを使っています。 Backtestクラスのインスタンスを作成します。ここで先ほど作成したStrategyクラスを指定します。 trade_on_closeで取引のタイミングを指定します。Trueにするとその日の終値で取引を行い、Falseにすると翌日の始値で取引を行います。その日の終値を確認して翌日に仕掛けるということでFalseにしています。 exclusive_ordersは取引時にそれまでのポジションをクローズするかどうかを指定します。毎回ポジションをクローズしたいのでTrueとしています。 runで計算を開始します。 df = get_stock(TICKER, START_DATE, END_DATE) bt = Backtest( df, SmaCross, cash=INIT_CASH, trade_on_close=False, exclusive_orders=True ) output = bt.run() print(output) bt.plot() print(output)の結果です。 結果 Start 2011-01-04 00:00:00 End 2020-12-30 00:00:00 Duration 3648 days 00:00:00 Exposure Time [%] 94.9775 Equity Final [$] 650714 Equity Peak [$] 1.42691e+06 Return [%] -34.9286 Buy & Hold Return [%] 163.934 Return (Ann.) [%] -4.3251 Volatility (Ann.) [%] 18.8042 Sharpe Ratio 0 Sortino Ratio 0 Calmar Ratio 0 Max. Drawdown [%] -65.8071 Avg. Drawdown [%] -6.89812 Max. Drawdown Duration 2779 days 00:00:00 Avg. Drawdown Duration 192 days 00:00:00 # Trades 41 Win Rate [%] 36.5854 Best Trade [%] 40.7543 Worst Trade [%] -13.8896 Avg. Trade [%] -1.04275 Max. Trade Duration 288 days 00:00:00 Avg. Trade Duration 85 days 00:00:00 Profit Factor 0.806629 Expectancy [%] -0.650414 SQN -0.722688 _strategy SmaCross _equity_curve ... _trades Size EntryB... dtype: object "Return [%]"の行にリターンが出ています。結果は残念ながらマイナス39.4%でした。 bt.plot()でトレードの結果のグラフを出力できます。2013年半ばにピークを迎えた後、下降の一途を辿っているように見えます。 最適な組み合わせを探す(1) Backtestingのoptimizeメソッドを使用すると、短期と長期の期間の最適な組み合わせを求めることができます。 SmaCrossクラスのクラス変数で定義したlong_term, short_termを範囲で指定します。するとこの範囲から最適な組み合わせを見つけてくれます。 return_heatmapをTrueにすると、組み合わせのヒートマップを取得することができます。plot_heatmapsメソッドを使用すればグラフを表示できます。 stats, heatmap = bt.optimize( long_term=range(3, MAX_LONG_TERM + 1, 101), short_term=range(2, MAX_LONG_TERM, 100), return_heatmap=True, constraint=lambda p: p.short_term < p.long_term) statsを表示してみます。 print(stats) 結果 Start 2011-01-04 00:00:00 End 2020-12-30 00:00:00 Duration 3648 days 00:00:00 Exposure Time [%] 98.775 Equity Final [$] 1.97874e+06 Equity Peak [$] 1.97939e+06 Return [%] 97.8745 Buy & Hold Return [%] 163.934 Return (Ann.) [%] 7.27493 Volatility (Ann.) [%] 21.6981 Sharpe Ratio 0.33528 Sortino Ratio 0.52895 Calmar Ratio 0.152433 Max. Drawdown [%] -47.7255 Avg. Drawdown [%] -4.58205 Max. Drawdown Duration 2490 days 00:00:00 Avg. Drawdown Duration 108 days 00:00:00 # Trades 141 Win Rate [%] 48.227 Best Trade [%] 35.591 Worst Trade [%] -13.1921 Avg. Trade [%] 0.490535 Max. Trade Duration 192 days 00:00:00 Avg. Trade Duration 26 days 00:00:00 Profit Factor 1.44673 Expectancy [%] 0.629784 SQN 1.34157 _strategy SmaCross(long_te... _equity_curve ... _trades Size Entry... dtype: object リターンはlong_term=23, short_term=17のときにプラス97.9%となってます。約2倍になったことになりますね。 トレードの結果をグラフにしてみます。 bt.plot() 下記のようにするとヒートマップを表示できます。 plot_heatmaps(heatmap, agg='mean', plot_width=2048, filename='heatmap') long_term=23, short_term=17のところが、明るい色になってることがわかりますが、ここが最適な組み合わせの場所となります。 最適な組み合わせを探す(2) 上では2〜100日の5日刻みで最適な組み合わせを求めています。今度は250日までの期間を1日刻みで求めてみたいと思います。プログラム的にはほぼ同じで、期間と刻みが違うだけです。私のPCで1時間ちょっとかかりました。 statsの内容は下記の通りです。リターンはプラス144.6%で、資産が約2.4倍になったことになります。 結果 Start 2011-01-04 00:00:00 End 2020-12-30 00:00:00 Duration 3648 days 00:00:00 Exposure Time [%] 86.2393 Equity Final [$] 2.44551e+06 Equity Peak [$] 2.44631e+06 Return [%] 144.551 Buy & Hold Return [%] 163.934 Return (Ann.) [%] 9.63845 Volatility (Ann.) [%] 21.03 Sharpe Ratio 0.458318 Sortino Ratio 0.731527 Calmar Ratio 0.309028 Max. Drawdown [%] -31.1895 Avg. Drawdown [%] -4.22459 Max. Drawdown Duration 452 days 00:00:00 Avg. Drawdown Duration 50 days 00:00:00 # Trades 13 Win Rate [%] 69.2308 Best Trade [%] 44.9915 Worst Trade [%] -17.4933 Avg. Trade [%] 7.17498 Max. Trade Duration 629 days 00:00:00 Avg. Trade Duration 243 days 00:00:00 Profit Factor 4.90971 Expectancy [%] 8.24431 SQN 2.07852 _strategy SmaCross(long_te... _equity_curve ... _trades Size EntryB... 続いてヒートマップをグラフにします。 右上の方が明るい色になってます。この辺の組み合わせが良いようです。 このヒートマップですが、軸のラベルがつぶれてしまって見えなくなってます。色なども調整したいので、Seabornのheatmapでグラフを作成してみます。 sns.set(font='IPAexGothic') d = heatmap.reset_index().pivot('short_term', 'long_term', 'SQN') fig, ax = plt.subplots(figsize=(12, 9)) sns.heatmap(d, square=True, cmap='seismic', center=0, ax=ax) ax.set_title('移動平均期間の組み合わせによるSQN') ax.invert_yaxis() ax.grid() plt.show() 赤の濃いところが良い組み合わせ、青の濃いところが悪い組み合わせになってます。 最後に 今回はSMAを使用して、長期と短期がどの組み合わせが最適化を求めました。結果、リターンは144%となりました。これは資産が2.4倍になったことになり年利にすると9%です(複利)。 しかしよく考えてみると、2011年当時10000円くらいだった日経平均株価は2020年末頃には26000円くらいになってます。2011年に買って何もしなければ資産が2.6倍(160%の増加)になった計算になります。 この結果だけを見るとSMAを使った戦略はもう一つなのかな、と思ってしまいます。ただし、今回はゴールデンクロスで買ってデッドクロスで売るという単純な戦略です。もっと複雑な戦略をとれば結果は変わるのだと思います。 今回のソースはGitHubに置いてます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS EC2 インスタンスのメタデータを取得する

初めに インスタンス内部からインスタンスIDを知りたいことがあったのでPythonで取得するコードを書いた。 インスタンスのメタデータとは インスタンスIDやAMIのIDなどのインスタンス自身の情報のこと。インスタンスからcurl http://169.254.169.254/latest/meta-data/ を実行することで取得できる。 最初にシェルで取得しようとして以下のコードを書いたが、うまくいかないことがわかった。理由は実行結果を見るとわかるように、パスが深いところがあるため。例えば、identity-credentials/: ec2/ は、さらにcurl http://169.254.169.254/latest/meta-data/identity-credentials/ec2を実行しなければidentity-credentials(トークンなどの認証情報)の値は返ってこない。 data=`curl -s http://169.254.169.254/latest/meta-data/` echo $data | sed 's/ /\n/g' | while read line do echo "$line:" `curl -s http://169.254.169.254/latest/meta-data/$line` done 実行結果(値は少し変えている) ami-id: ami-06098fd00463352b6 ami-launch-index: 0 ami-manifest-path: (unknown) block-device-mapping/: ami root events/: maintenance/ hibernation/: configured hostname: ip-172-31-38-1.ap-northeast-1.compute.internal identity-credentials/: ec2/ instance-action: none instance-id: i-0d25432b11k20tc94 instance-life-cycle: on-demand instance-type: t2.micro local-hostname: ip-172-31-38-1.ap-northeast-1.compute.internal local-ipv4: 172.31.38.1 mac: 04:3r:66:3d:76:18 metrics/: vhostmd network/: interfaces/ placement/: availability-zone availability-zone-id region profile: default-hvm public-hostname: ec2-13-125-99-211.ap-northeast-1.compute.amazonaws.com public-ipv4: 13.125.99.211 public-keys/: 0=key reservation-id: r-0e1bbhj32u77445y7 security-groups: default services/: domain partition この問題に対処するため、Pythonで再帰的にcurlを実行することにした。 つまり、以下の処理を再帰的に行う。 認証情報がほしい ↓ curl http://169.254.169.254/latest/meta-data/ ↓ curl http://169.254.169.254/latest/meta-data/identity-credentials/ ↓ curl http://169.254.169.254/latest/meta-data/identity-credentials/ec2 ↓ 認証情報取得 コード 実際に書いたコードがこちら。 import subprocess def run_curl(url): args = ['curl', '-s', url] res = subprocess.run(args, encoding='utf-8', stdout=subprocess.PIPE) return res def res_analysis(res): for path in res.stdout.split('\n'): if '/' in path: res_analysis(run_curl(res.args[2] + path)) else: print('{}: {}'.format(path, run_curl(res.args[2] + path).stdout)) if __name__ == "__main__": url = 'http://169.254.169.254/latest/meta-data/' res_analysis(run_curl(url)) 解説 Pythonでcurlコマンドを実行するため、subprocessをインポート。encodingを指定して、バイナリ文字をutf-8に変換する。標準出力を変数に格納するためにstdout=subprocess.PIPEを指定する。 対話モードで実行した場合 >>> subprocess.run(args, encoding='utf-8', stdout=subprocess.PIPE) CompletedProcess(args=['curl', '-s', 'http://169.254.169.254/latest/meta-data/'], returncode=0, stdout='ami-id\nami-launch-index\nami-manifest-path\nblock-device-mapping/\nevents/\nhibernation/\nhostname\nidentity-credentials/\ninstance-action\ninstance-id\ninstance-life-cycle\ninstance-type\nlocal-hostname\nlocal-ipv4\nmac\nmetrics/\nnetwork/\nplacement/\nprofile\npublic-hostname\npublic-ipv4\npublic-keys/\nreservation-id\nsecurity-groups\nservices/') 以下の条件分岐だが、curlでパスが返ってきた場合とIDなどの値が返ってきた場合で処理を分けるために使用している。パスが返ってきた場合、URLにそのパスを追加して再度curlを実行する。値が返ってきた場合出力する。 if '/' in path: res_analysis(run_curl(res.args[2] + path)) else: print('{}: {}'.format(path, run_curl(res.args[2] + path).stdout)) 書いていて一番気持ち悪いと思ったが、現状これが一番簡潔なのかなと思っているのが以下のコード。再利用のためsubprocess.runは1つのメソッドとして分けたかった。その結果、合成関数みたいになってしまった。 res_analysis(run_curl(url)) 実行結果(値は少し変えている。...の部分は省略していることを表す。) ami-id: ami-06098fd00463352b6 ami-launch-index: 0 ami-manifest-path: (unknown) ami: /dev/xvda root: /dev/xvda history: [] scheduled: [] configured: false hostname: ip-172-31-38-1.ap-northeast-1.compute.internal info: { "Code" : "Success", "LastUpdated" : "2021-04-17T07:01:28Z", "AccountId" : "012..." } ec2-instance: { "Code" : "Success", "LastUpdated" : "2021-04-17T07:02:09Z", "Type" : "AWS-HMAC", "AccessKeyId" : "ASIA...", "SecretAccessKey" : "OL...", "Token" : "IQ...", "Expiration" : "2021-04-17T13:07:15Z" } instance-action: none instance-id: i-0d25432b11k20tc94 instance-life-cycle: on-demand instance-type: t2.micro local-hostname: ip-172-31-38-1.ap-northeast-1.compute.internal local-ipv4: 172.31.38.1 mac: 06:2f:79:4c:69:19 vhostmd: <?xml version="1.0" encoding="UTF-8"?> device-number: 0 interface-id: eni-6y01019922ooo176528 13.125.99.211: 172.31.38.1 local-hostname: ip-172-31-38-1.ap-northeast-1.compute.internal local-ipv4s: 172.31.38.1 mac: 04:3r:66:3d:76:18 owner-id: 012... public-hostname: ec2-13-125-99-211.ap-northeast-1.compute.amazonaws.com public-ipv4s: 13.125.99.211 security-group-ids: sg-b78p90r2 security-groups: default subnet-id: subnet-e6g098l4 subnet-ipv4-cidr-block: 172.31.32.0/20 vpc-id: vpc-c4b1weee vpc-ipv4-cidr-block: 172.31.0.0/16 vpc-ipv4-cidr-blocks: 172.31.0.0/16 availability-zone: ap-northeast-1a availability-zone-id: apne1-az4 region: ap-northeast-1 profile: default-hvm public-hostname: ec2-13-125-99-211.ap-northeast-1.compute.amazonaws.com public-ipv4: 13.125.99.211 0=key: <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "..." "http://www..."> <html xmlns="http://www..." xml:lang="en" lang="en"> <head> <title>404 - Not Found</title> </head> <body> <h1>404 - Not Found</h1> </body> </html> reservation-id: r-0e1bbhj32u77445y7 security-groups: default domain: amazonaws.com partition: aws 参考記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSのlambdaを使って、翻訳機能を実装してみた

はじめに 最近本格的にAWSの勉強を始めたので、そのoutputのために書きました。 同じようにAWSの初学者の方の助けになると幸いです。 前提として python を基本コードとして使います AWS マネジメントコンソールを登録している前提で始めます 関数の作成 まずAWS マネジメントコンソールを開き、上の検索バーのところで「lambda」検索します。 下の画面に行くと、オレンジ色の「関数の作成」を選びます。 その後の設定は、以下のようにします。 関数が、作成されると次にコードの部分に以下の内容をペーストしてください。 これでソースコードは完成です! import json import boto3 translate = boto3.client('translate') def lambda_handler(event, context): input_text = "おはよう" response = translate.translate_text( Text= input_text , SourceLanguageCode='ja', TargetLanguageCode='en' ) output_text = response.get('TranslatedText') return { 'statusCode': 200, 'body3': json.dumps({ 'output_text' : output_text }) } 次に「TEST」クリックして、出力してみます。 すると、TranslateText operation の権限がない状態でその関数を使ったためエラーが出ていることが分かります。なので、次に権限の付与を行います。 権限を与える 設定からアクセス権限を選択し、実行ロール中の付与を与えたい関数を選択します。 そして、アクセス権限の、「ポリシーをアタッチします」を押してください。そこの検索バーから「Translate Full Acess」を選択します。すると付与成功です! ちなみに先ほどのこの部分で確認できます。 以下が完成結果です。 今回の概要
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Excel上のシステムリストをDiagramsで構成図に変換してみた

経緯 Excelのシステム一覧を自動的に構成図に変換するツールはないか調べており、PythonのDiagramsが使えそうなので試してみました。 成果物 下記のようなExcelを使い、構成図を自動出力するプログラムを作成しました。 プログラム import pandas as pd from diagrams import Diagram from diagrams.onprem.client import Client # Excelの読み込み df = pd.read_excel('読み込むExcelファイル') # フォントの設定 node_attr = { 'fontsize': '20' } with Diagram(filename='システム構成図', node_attr=node_attr): systemlist = [] for i in range(len(df)): # ノードを宣言 systemlist.append(Client(df.iloc[i,0])) # ノードをつなぐ for i in range(len(df)): for j in range(3): for k in range(len(df)): if df.iloc[i,1+j] == df.iloc[k,0]: print(f'{df.iloc[k,0]}←{df.iloc[i,0]}') systemlist[k]<<systemlist[i] 参考サイト
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Windows10ローカルにPython環境構築

Windows10ローカルにPython環境構築 環境 Windows10 Pro Pythonをダウンロード 「Add Python 3.x to PATH」にチェックをいれる。 PowerShellで以下を実行する。 # PowerShellでスクリプトの実行を許可 Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force パッケージのインストール pip インストール python -m pip install [name] アンインストール python -m pip uninstall [name] 仮想環境 フォルダ作成 コマンド実行 python -m venv .venv PowerShell > .\activate.ps1 仮想環境にて パッケージインストール >python -m pip install requests パッケージの一覧出力 >python -m pip freeze > requirements.txt パッケージの一括インストール >python -m pip install -r requirements.txt 仮想の終了 >deactivate 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PyCaret

はじめに PyCaretを使ってみたのでまとめました。 言語はPython、環境はGoogle Colaboratoryを使用しています。 pycaretとは PyCaretは、Pythonのオープンソースの機械学習ライブラリです。 ローコードでデータの準備や解析を行うことができます。 今回は、kaggleのタイタニックデータセットを使用しました。 上記サイトから、 train.csv test.csv gender_submission.csv をダウンロードして任意のディレクトリに保存しておきましょう。 実装 PyCaretを使用するには、pipでインストールします。 !pip install pycaret また、タイタニックのデータをdataframeとして読み込んでおきます。 import numpy as np import pandas as pd train = pd.read_csv('train.csv') test = pd.read_csv('test.csv') sub = pd.read_csv('gender_submission.csv') 念の為、データの中身を確認しておきます。 train.head() では、早速PyCaretを使っていきます。 以下でPyCaretのライブラリをインポートしておきます。 from pycaret.classification import * まず、データの前処理をしてみましょう。 clf1 = setup(data = train, target = 'Survived', numeric_imputation = 'mean', categorical_features = ['Sex', 'Embarked'], ignore_features = ['Name', 'Ticket', 'Cabin'], silent = True) ここでは、trainデータに対して以下の処理をしています。 目的変数をSurvivedと指定 数値の欠損値は平均値で補完 カテゴリ変数の特徴量は、'Sex', 'Embarked'があることを指定 無視する特徴量として、'Name', 'Ticket', 'Cabin'を指定 次に、どのモデル(アルゴリズム)を使用するかを比較・検討します。 compare_models() Accuracyの高い順に並んでいるようです。 今回は、kaggleでも定番のlightGBM(上記5行目)を使ってみます。 それでは、lightGBMによるモデルを作成してみましょう。 lgbm = create_model('lightgbm') 次に、このモデルのハイパーパラメータのチューニングをしてみましょう。 tuned_lightgbm = tune_model(lgbm) では、モデルの精度を確かめてみましょう。 plot_model(estimator = tuned_lightgbm, plot = 'learning') plot_model(estimator = tuned_lightgbm, plot = 'auc') plot_model(estimator = tuned_lightgbm, plot = 'confusion_matrix') Feature Importance(どの特徴量が予測によく効くか)は以下で確認できます。 plot_model(estimator=tuned_lightgbm, plot='feature') 特徴量のなかでも、特に年齢や運賃が生存の可否に効いていることがわかります。 上記を含め、モデルに関するあらゆる情報は以下で見ることができます(以下はThresholdタブを選んだ場合)。 evaluate_model(tuned_lightgbm) それでは実際に予測した結果を見てみましょう。 predictions = predict_model(tuned_lightgbm, data=test) predictions.head() submitするには、以下のようにsubmission.csvを作成します。 sub['Survived'] = round(predictions['Score']).astype(int) sub.to_csv('submission.csv',index=False) sub.head() では、最後に複数のモデルを組み合わせるアンサンブルをしてみます。 以下では、上記で作成したtuned_lightgbm、Adaboost、ロジスティック回帰の3つを組み合わせています。 ※create_modelの引数は、compare_models()での表記(adaboostでなくadaなど)に合わせる必要があります。 ada = create_model('ada') logr = create_model('lr') blend = blend_models(estimator_list=[tuned_lightgbm, ada, logr]) まとめ 今回は、PyCaretを使用してタイタニックの生存予測モデルをつくってみました。 PyCaretを使用することで、データの前処理からモデルの作成まで簡単に行うことができました。 参考文献
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PyCaret】ローコードで前処理からモデル作成まで

はじめに PyCaretの使い方についてまとめました。 言語はPython、環境はGoogle Colaboratoryを使用しています。 PyCaretとは PyCaretは、Pythonのオープンソースの機械学習ライブラリです。 ローコードでデータの準備や解析を行うことができます。 今回は、kaggleのタイタニックデータセットを使用しました。 上記サイトから、 train.csv test.csv gender_submission.csv をダウンロードして任意のディレクトリに保存しておきましょう。 実装 PyCaretを使用するには、pipでインストールします。 !pip install pycaret また、タイタニックのデータをdataframeとして読み込んでおきます。 import numpy as np import pandas as pd train = pd.read_csv('train.csv') test = pd.read_csv('test.csv') sub = pd.read_csv('gender_submission.csv') 念の為、データの中身を確認しておきます。 train.head() 親の顔より見慣れたデータ。 では、早速PyCaretを使っていきます。 以下でPyCaretのライブラリをインポートしておきます。 from pycaret.classification import * まず、データの前処理をしてみましょう。 clf1 = setup(data = train, target = 'Survived', numeric_imputation = 'mean', categorical_features = ['Sex', 'Embarked'], ignore_features = ['Name', 'Ticket', 'Cabin'], silent = True) ここでは、trainデータに対して以下の処理をしています。 目的変数をSurvivedと指定 数値の欠損値は平均値で補完 カテゴリ変数の特徴量は、'Sex', 'Embarked'があることを指定 無視する特徴量として、'Name', 'Ticket', 'Cabin'を指定 次に、どのモデル(アルゴリズム)を使用するかを比較・検討します。 compare_models() Accuracyの高い順に並んでいるようです。 今回は、kaggleでも定番のlightGBM(上記5行目)を使ってみます。 ※kaggleでの初手lightGBMについては、以下のサイトが参考になります。 それでは、lightGBMによるモデルを作成してみましょう。 lgbm = create_model('lightgbm') 次に、このモデルのハイパーパラメータのチューニングをしてみましょう。 tuned_lightgbm = tune_model(lgbm) では、モデルの精度を確かめてみましょう。 plot_model(estimator = tuned_lightgbm, plot = 'learning') plot_model(estimator = tuned_lightgbm, plot = 'auc') plot_model(estimator = tuned_lightgbm, plot = 'confusion_matrix') Feature Importance(どの特徴量が予測によく効くか)は以下で確認できます。 plot_model(estimator=tuned_lightgbm, plot='feature') 特徴量のなかでも、特に年齢や運賃がバチクソ効いているのがわかります。 上記を含め、さまざまな情報は以下で見ることができます(以下はPrecision Recallタブを選んだ場合)。 evaluate_model(tuned_lightgbm) それでは実際に予測した結果を見てみましょう。 predictions = predict_model(tuned_lightgbm, data=test) predictions.head() submitするには、以下のようにしてsubmission.csvを作成しましょう。 sub['Survived'] = round(predictions['Score']).astype(int) sub.to_csv('submission.csv',index=False) sub.head() では、最後に複数のモデルを組み合わせるアンサンブルをしてみます。 以下では、上記で作成したtuned_lightgbm、Adaboost、ロジスティック回帰の3つを組み合わせています。 ※create_modelの引数は、compare_models()での表記(adaboostでなくadaなど)に合わせる必要があります。 ada = create_model('ada') logr = create_model('lr') blend = blend_models(estimator_list=[tuned_lightgbm, ada, logr]) まとめ 今回は、PyCaretを使用してタイタニックの生存予測モデルをつくってみました。 PyCaretを使用することで、データの前処理からモデルの作成まで簡単に行うことができました。 ぜひ、日々のデータ分析、kaggleのおともにご利用下さい。 参考文献
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【DRF】Django REST Frameworkでランダムなページネーションを実装する方法

今回のテーマは、DRFで全件取得をする時、ページネーションをしつつもランダムなリストを取得することです。もちろん、違うページごとには要素は一個も被らないようにします。 また、今回はViewsetではなく、Generic Viewを使います。 デフォルトのページネーションをsettings.pyに設定してある前提で進みます。 どうやって実装するか 今回はいきなり方法を説明するのではなく、色々な方法を議論しながら進めていくスタイルでいこうと思います。 querysetにorder_by('?')を使う 真っ先に思いつくのはこの方法かもしれません。例えば、 views.py class ListView(generics.ListAPIView): queryset = Post.objects.order_by('?') serializer = PostSerializer filter_backends = [...] ... という感じです。 これを試した方はわかると思いますが、これではページをリロードする、または次のページに行くたびに全体のオブジェクトの順番が入れ替わるため、1ページ目にあったオブジェクトが次のページにも出現してしまうということが起こります。 これは、DjangoのQueryset、DRFのlistメソッドの仕組みからわかります。 そもそも、Querysetというのはオブジェクトの配列ではなく、SQL文の集まりです。そのため、任意のページにアクセスするたびにQuerysetが評価され、新しいランダムなオブジェクトの配列が生成されてしまうのです。 ListAPIViewのlistメソッドとqueryset 次に、listメソッドの中身を見てみましょう。 listメソッドは、ListAPIViewに対してGETした時に呼ばれるメソッドです。 mixins.py class ListModelMixin: """ List a queryset. """ def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) github django-rest-frameworkより引用 ここで注意したいのが、このlistメソッド内で、querysetという変数の型はなんなのかということです。 querysetって名前なんだからQueryset型だろ!って思いませんか?僕は思いました。 しかし、実はこのqueryset、オブジェクトのリストなんです。 page = self.paginate_queryset(queryset) の部分に注目してみましょう。この文を呼ぶことで、最終的にはsettings.pyで設定したページネーションクラス、僕の場合にはPageNumberPaginationの、paginate_querysetというメソッドが呼ばれます。 pagination.py class PageNumberPagination(BasePagination): ... page_size = api_settings.PAGE_SIZE django_paginator_class = DjangoPaginator ... def paginate_queryset(self, queryset, request, view=None): """ Paginate a queryset if required, either returning a page object, or `None` if pagination is not configured for this view. """ page_size = self.get_page_size(request) if not page_size: return None paginator = self.django_paginator_class(queryset, page_size) # ここの行でDjangoのPaginatorがイニシャライズされる。 github django-rest-frameworkより引用 最後の行で、querysetという変数が第一引数として呼ばれ、Paginatorが初期化されます。実際の__init__メソッドを見てみると、、、 paginator.py class Paginator: def __init__(self, object_list, per_page, orphans=0, allow_empty_first_page=True): self.object_list = object_list Django docより引用 querysetやなくてオブジェクトリストやないかい! まとめると、listメソッドの中でquerysetはオブジェクトの配列になった後でページネーションが行われていたのです。 実際の方法 やりたいことをおさらいします。 今回の目的は、ユーザーごとにランダムなオブジェクトのリストを取得できること、かつ、ページを移動してもオブジェクトが被らないことです。 今までの議論から、最終的にどんなことをしたらいいのかをまとめるとこうなります。 1. 固定されたランダムなオブジェクトの配列を page = self.paginate_queryset(queryset) のquerysetの部分に渡す。 2. 固定されたランダムな配列を複数用意する。 3. ユーザーごとにランダムな配列を割り当てる。 4. 1ページ目をリロードした時は、別のランダムな配列を取得する。 今回この4つの条件を満たすために、ユーザーセッション、キャッシュを使って実装します。まずはコードをお見せします。 views.py class RandomListView(generics.ListCreateAPIView): queryset = Post.objects.all() serializer_class = PostSerializer # filter_backends = None # *1 def list(self, request, *args, **kwargs): """ ランダムでページネーションされたリスト取得 クエリパラメータに応じた挙動: reload: trueのとき、セッションのrandom_expを+1することで、擬似的にランダムなリストが更新される(0-24のパターンを循環する) page: 2ページ目移行の時はreloadはされない """ # *2 RANDOM_EXPERIENCES = 24 if not request.session.get('random_exp'): request.session['random_exp'] = random.randrange(0, RANDOM_EXPERIENCES) # *3 flag = None if 'reload' in request.GET: param = request.GET['reload'] flag = bool(param) if 'page' in request.GET: flag = False if flag: request.session['random_exp'] += 1 # *4 object_list = cache.get('random_exp_%d' % request.session['random_exp']) if not object_list: object_list = list(Music.objects.order_by('?').all()) cache.set('random_exp_%d' % request.session['random_exp'], object_list, 60*15) # *5 object_list = cache.get('random_exp_%d' % request.session['random_exp']) page = self.paginate_queryset(object_list) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(object_list, many=True) return Response(serializer.data) *1 ランダムに取得するということなので、フィルターはしません(Noneをセットしています)。 *2 まず、セッションを使って、ユーザーにrandom_expというキーで0から24の番号をランダムに割り当てます。24という数字は加減してください。 こうすることで、ユーザーは、25個のランダムなオブジェクトの配列からランダムで一つ配列を取得します。(ランダムという言葉が多いですがゆっくり読み進めてください。) *3 次に、URLのクエリパラメータの中でreloadという値がtrueになっている時、random_expの値を+1することでユーザーに異なる配列を取得させています。 ここで注意点として、この例の場合、25回目のリロードで一番初めに持っていた配列に戻ってきます。循環させることで擬似的なランダムを実現しています。完全なランダムに近づけるには、RANDOM_EXPERIENCEの数を増やしてください。 また、URLのクエリパラメータの中でpageという値が存在する場合、つまり、2ページ目以降のページにアクセスしている時、reloadは強制でFalseになります。2ページ目以降でもリロードできてしまうと、1ページ目と2ページ目では違う配列を使っていることになるので、オブジェクトが被ってしまう可能性があるからです。 *4 その後、計25個あるランダムなオブジェクトの配列の中から、ユーザーセッションのrandom_expの番号の配列を取得して(cacheに'random_exp_0から24の数字'というキーでgetすると取得できる)、 page = self.paginate_queryset(object_list) のobject_listの部分に渡しています。 最終的に、 api/posts/ -> ランダムなオブジェクトのリストを取得 api/posts/?reload=true -> 以前と異なるランダムなオブジェクトのリストを取得 api/posts/?page=2 -> 1ページ目の時と同じ配列で、2ページ目を表示 api/posts/?page=2?reload=true -> api/posts/?page=2と同じ というURLが実現できました。 *5 また、キャッシュの時間を60*5のように秒数で設定することで、全ての配列を新しくするまでの時間を設定できます。試しながら調整してみてください。 ちなみに短くしすぎると1ページ目と2ページ目で同じオブジェクトが取得できてしまう可能性が高くなるということは注意してください。 終わりに 今回初めて長い説明をしたので、わかりにくいところがあるかもしれません。いつでも質問を受け付けているので、気軽にコメントしていただけるとありがたいです。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ルジャンドル(Legendre)多項式の零点(分点)と重みを求める

この記事をざっくりと ガウス求積で必要になるルジャンドル多項式の零点と重みを求めるプログラムです。 ルジャンドル多項式は漸化式を用いて再帰的に、零点はニュートン法で求めます。 数学的な部分の解説はありません。(式の導出とかの詳細は私も知りたい…) 環境 GoogleColaboratory ライブラリはnumpyとnumba jitを使いました。 import numpy as np from numba import jit ルジャンドル多項式 ルジャンドル多項式は $$ P_n(x) = \frac{(-1)^n}{2^nn!}\frac{d^n}{dx^n}(1-x^2)^n $$で表される関数のようです。 これを解くのは骨が折れそうなので、今回は漸化式を利用します。 ルジャンドル多項式は以下の漸化式を満たします。 \begin{align} P_0(x) &= 1\\ P_1(x) &= x\\ P_n(x) &= \frac{(2n-1)xP_{n-1}(x)-(n-1)xP_{n-2}(x)}{n} \end{align} これをPythonで実装してみました。 ルジャンドル多項式 @jit def legendre(n,x): if n == 0: return 1 elif n == 1: return x else: Pox = (x*(2*n-1)*legendre(n-1,x)-(n-1)*legendre(n-2,x))/(n) return Pox こんな感じ。 ※関数を再帰的に呼び出しているので、$n$が大きくなるとめちゃめちゃ時間かかります。jitを使うとだいぶ早くなります。 零点を求める ニュートン法で零点を求めます。 ニュートン法は、わかりやすい解説があちらこちらにあるので詳しいことは割愛します。 以下の式 x_{i+1} = x_i - \frac{P_n(x_i)}{P'_n(x_i)} を、$ x_{i+1} - x_i $が十分小さくなるまで繰り返し解くことで零点が求まります。 なお、$x_{i+1}$を求めるのに$P_n'(x_i)$が必要です。ルジャンドル多項式の場合、どうやら $$ P_n'(x)=\frac{n(P_{n-1}(x)-xP_n(x))}{1-x^2} $$で求められるようです。 初期値は、 $$ x_i = \cos\biggl(\frac{i+0.75}{n+0.5}\pi\biggr), (i=0,1,2,...,n-1) $$を用いると良いようです。 プログラムは以下のようにしました。 零点を求める #ルジャンドル多項式の微分 @jit def diff_legendre(n,x): if (x == 1) or (x == -1): x += 0.001 return n*(legendre(n-1,x) - x*legendre(n,x))/(1-x**2) #ニュートン法で零点を一点求める def ZeroPont(x0, n, N=50): #Nは零点を探すための最大試行回数 for i in range(N): x1 = x0 - legendre(n,x0)/diff_legendre(n,x0) if abs(x1 - x0) < 1e-6: #差が十分に小さいことを評価 break x0 = x1 if i == 50: print("zero point not found") return x1 #Pn(x)の全ての零点を取得する def search_ZeroPoint_list(n): zero_point_list = [] for i in range(n): x = np.cos(((i+0.75)/(n+0.5))*np.pi)#初期値 ZP = ZeroPont(x, n) if abs(ZP) < 1e-10: ZP = 0.0 return zero_point_list 重みを求める 分点を零点とすると、ルジャンドル多項式の重みは $$ w_i = \frac{2(1-x_i^2)}{(nP_n(x_i))^2} $$で求められるようです。 重みの式 #重みを計算する def weight(x,n): return 2*(1-(x**2)) / ((n*legendre(n-1, x))**2) 作成したsearch_ZeroPoint_list関数を少し変えて、零点と同時に重みを計算できるようにしました。 零点と重みを求める #零点と重みを計算する def search_ZeroPoint_and_Weight(n): ZP_list = [] weight_list = [] for i in range(n): x = np.cos(((i+0.75)/(n+0.5))*np.pi)#初期値 ZP = ZeroPont(x, n) if abs(ZP) < 1e-10: ZP = 0.0 W = weight(ZP,n) ZP_list.append(x) weight_list.append(w) return ZP_list, weight_list 値を出してみる for i in range(4): zps, ws = search_ZeroPoint_and_Weight(i) print("n = " , str(i) , "\nzero_points = " , zps, "\nweights = " , ws, "\n" ) 結果 n = 0 zero_points = [] weights = [] n = 1 zero_points = [0.0] weights = [2.0] n = 2 zero_points = [0.5773502691896257, -0.5773502691896257] weights = [1.0000000000000004, 1.0000000000000004] n = 3 zero_points = [0.7745966692414841, 0.0, -0.7745966692414841] weights = [0.5555555555555541, 0.8888888888888888, 0.5555555555555541] 解析解に近い値になっていますね。 まとめ 漸化式を再帰関数で再現することで、ルジャンドル多項式を実装しました。 零点はニュートン法により求めました。特に工夫せずとも、解析解に近い値(Wikipedia参照)を得ることができました。 参考 ガウス求積-Wikipedia
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

matplotでグラフを書く.

はじめに(matplotlib) こんにちは.matplotlibとはpythonで最も有名な描画方法です.グラフを書くときに便利なので覚えておくとよいでしょう.これでexcelから脱却間違いなしです! https://matplotlib.org/ 基本的にここを見ればだいたいわかるはずですが,折角なので以下にまとめたいと思います.普段は点や線だけ書く私ですが,いろんな書き方にチャレンジしてみます. とりあえず,おなじみの pip install matplotlib をしたらスタートです. 因みに本記事はjupyter notebookをもとに作りました. matplot 初歩 (1) 超基本的なグラフ:初め3行は描画する上で必要不可欠なものです.二段落目はデータ作製です.横軸と縦軸を表現しています.そして最後にプロットに関して書いています.またlabelは各種タイトルを示しています.plt.plotで基本的に書けます.plt.show()で描画実行を表しています. 今回,線やプロットの種類もいじってみましたが,不要であれば値さえあればOKです. ####################### #絶対必要(この部分は以下のプログラムにも適用する) import matplotlib.pyplot as plt %matplotlib inline import numpy as np #データ作製 x = np.linspace(0, 5, 11) y = x ** 2 ######################## #plot準備 #(横軸,縦軸,色,太さ,線の種類,プロットの種類,プロットの大きさ, #プロットの色,プロットの周囲の色,プロットの周囲の太さ) plt.plot(x, y, color='red', lw=3, ls='--', marker='o', markersize=15, markerfacecolor='blue', markeredgewidth=5, markeredgecolor='green') plt.xlabel('X label')#x軸タイトル plt.ylabel('Y label')#y軸タイトル plt.title('noramal **2 graph')#題名 plt.text(3, 5, r"$y=x^2$", fontsize=20, color="blue")#線の名前(位置*2,名前,大きさ,色) plt.show()#実行 (2) グラフを並べる"subplot":ここでは上のようなグラフを2種類並べて表示する方法に関してまとめます.plt.subplotで複数のグラフを表示させることが可能です.また色に関して "r--"は赤色の棒線 "g*-"はプロット付き線を示しています. なので "g*--"だとプロット付きの棒線になります. # plt.subplot(列, 行, 図番号) plt.subplot(1,2,1) plt.plot(x, y, 'r--') plt.subplot(1,2,2) plt.plot(y, x, 'g*-'); ちなみに4種類のグラフを(2かけ2)で並べる場合は以下のようにする. (列, 行, 図番号)なので,(2, 2, number)の形式.点の形も色々変えられます. plt.subplot(2,2,1) plt.plot(x, y, 'r--') plt.subplot(2,2,2) plt.plot(y, x, 'g*--'); plt.subplot(2,2,3) plt.plot(y, x, 'bo--', markersize=10); plt.subplot(2,2,4) plt.plot(x, y, 'y+--', markersize=20); (3)2つのグラフを載せる"add_axis":(2)ではグラフを並べるという手法でしたが,小さいグラフを追加するときなどに有効な方法をまとめます. plt.figure()でイコール前のものに接続 add_axisで追加のグラフを設定 set_xlabelで各タイトルを設定 #追加のグラフを以下のように準備 fig = plt.figure() #left, bottom, width, height -> (0~1) axes1 = fig.add_axes([0.1, 0.1, 0.8, 0.8]) axes2 = fig.add_axes([0.2, 0.5, 0.3, 0.3]) #以下,いつものセッティング.axis1, axis2で分けるaxes1.plot(x, y, 'b') axes1.set_xlabel('X1') axes1.set_ylabel('Y1') axes1.set_title('AXIS1'); axes2.plot(x, y, 'r') axes2.set_xlabel('X2') axes2.set_ylabel('Y2') axes2.set_title('AXIS2'); ここまでは割と簡単!SHIKAMO便利機能ばかりです. ここまででも二次元グラフを各程度であれば十分ですが,応用版として次に進みます. matplot 応用 (1) グラフを並べる"subplots()":上の簡単な方法(2)subplotより(sがつくかつかないか)人によってこっちの方がよいと思う方もいるかもしれません. #subplot(行,列,画像サイズ) fig, axes = plt.subplots(1, 2, figsize=(10,4)) #ひとつめ axes[0].plot(x, x**2, x, np.exp(x)) axes[0].set_title("NORMAL") #ふたつめ axes[1].plot(x, x**2, x, np.exp(x)) axes[1].set_yscale("log") axes[1].set_title("LOG"); なので,当然subplots(2, 2)になれば,2*2の配置になります. (2)グラフのラベルをいじる:ラベルを変えたいとき(問題製作する人になったときとか)に使える機能です.マスターしなければ,Excelの方が良いかも...いやぼくはpythonで作るんだ! #前と同じ fig, ax = plt.subplots(figsize=(10, 4)) ax.plot(x, x**2, x, x**3, lw=3, ls='--', marker='o', markersize=10) #1,2,3,4,5->alpha,beta... ax.set_xticks([1, 2, 3, 4, 5]) ax.set_xticklabels([r'$\alpha$', r'$\beta$', r'$\gamma$', r'$\delta$', r'$\epsilon$'], fontsize=15) #0,50,...->小数点第一位まで yticks = [0, 50, 100, 150] ax.set_yticks(yticks) ax.set_yticklabels(["$%.1f$" % y for y in yticks], fontsize=15); #前と同じ fig, ax = plt.subplots(figsize=(10, 4)) ax.plot(x, x**2, x, x**3, lw=3, ls='--', marker='o', markersize=10) #1,2,3,4,5->alpha,beta... ax.set_xticks([1, 2, 3, 4, 5]) ax.set_xticklabels([r'$\alpha$', r'$\beta$', r'$\gamma$', r'$\delta$', r'$\epsilon$'], fontsize=15) #物理系の人なら一度は使ったことあるはずの添え字をつける. yticks = [0, 50, 100, 150] from matplotlib import ticker formatter = ticker.ScalarFormatter(useMathText=True) formatter.set_scientific(True) formatter.set_powerlimits((-1,1)) ax.yaxis.set_major_formatter(formatter) (3)グリッドつきのグラフ:見ての通り.操作は簡単です.色や幅は今まで通りに変更可能です. fig, axes = plt.subplots(1, 2, figsize=(10,5)) #simple grid(grid(True)とするだけ) axes[0].plot(x, x**2, x, x**3, lw=2, ls='--', marker='o', markersize=10) axes[0].grid(True) #custom grid(grid(色,強調?濃さ?的な,線の種類,線の幅)とするだけ) axes[1].plot(x, x**2, x, x**3, lw=2, ls='--', marker='o', markersize=10) axes[1].grid(color='b', alpha=0.6, linestyle='dashed', linewidth=1.0) (4)グラフに色を付けて見やすくする.:文系理系問わず,複数の規格が異なるデータを一つのグラフにデータをまとめたいときあると思います.今回は左を青色,右を赤でまとめて表示させてみました.グラフの軸の線の太さや色を変えられます.またなくすこともできるのでサイト等,見やすくまとめるときに便利な機能だと思います. #いつもの設定 fig, ax = plt.subplots(figsize=(6, 5)) #axに対する設定をここにまとめる(ここではグラフの軸の線の色と太さを指定) ax.spines['bottom'].set_color('green') ax.spines['bottom'].set_linewidth(4)#太さ ax.spines['top'].set_color('none') ax.spines['left'].set_color('blue') ax.spines['left'].set_linewidth(4) ax.spines['right'].set_color("red") ax.spines['right'].set_linewidth(4) #axのデータとその線の種類を指定 ax.plot(x, x**2, lw=2, color="blue") ax.set_ylabel(r"area $(m^2)$", fontsize=18, color="blue") for label in ax.get_yticklabels(): label.set_color("blue") #axにくっつけるax2を設定(ほぼ同上) ax2 = ax.twinx() ax2.spines['top'].set_color('none') ax2.spines['bottom'].set_color('none') ax2.spines['right'].set_color('none') ax2.spines['left'].set_color('none') ax2.plot(x, x**3, lw=2, color="red") ax2.set_ylabel(r"volume $(m^3)$", fontsize=18, color="red") for label in ax2.get_yticklabels(): label.set_color("red") (5)ドット,階段,棒グラフ:それぞれaxisの後についているのを付ければOKです.容易に作製できるはず. 尚,ここでは新たなデータを利用してます. n = np.array([0,1,2,3,4,5]) x = np.linspace(-1., 1., 100) fig, axes = plt.subplots(1, 3, figsize=(9,3)) axes[0].scatter(x, x + (np.random.rand(len(xx))-0.5)*1.6) axes[0].set_title("scatter") axes[1].step(n, n**2, lw=3, color='r', marker='o') axes[1].set_title("step") axes[2].bar(n, n**2, align="center", color='g', width=0.5, alpha=0.8) axes[2].set_title("bar") ここまでかけたらだいぶ網羅しているはず... 疲れたのでこのくらいで勘弁してください(笑) いくつか大げさな太さにしたり色を変えたりしたりしていますのでレポートとかで利用する場合は適宜修正して使ってください. 気が向いたら次回は色変化するタイプのグラフや三次元のグラフを紹介しようと思います.乞うご期待ください. 以上.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者がPandasのSettingWithCopyWarningで躓いた話

コードは動くのにエラー? Yahoo!ファイナンスからダウンロードしたデータを利用して前日の終値と当日の終値から対数を計算した値を列に追加する際にPandasでSettingWithCopyWarningというエラーが出た。コードはちゃんと動くのにエラー?と思いつつエラーに四苦八苦していたがプロパティをよく見たらPandas.DataFrameのプロパティにあったのである。 ただの見落としで恥ずかしいが、備忘もかねて記録しておく。 エラー表示内容 SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy 書いたコード # -*- coding: utf-8 -*- import argparse import glob import math import os import pandas as pd # log(当日の終値,前日の終値(底))として対数表示したもの # 一番最初のセルは計算できないので0を入力しておく def add_log(dataframe): if 'Close' in dataframe.columns: dataframe['LogClose'] = 0 for row_index in range(1, len(dataframe)): dataframe['LogClose'][row_index] = math.log(float(dataframe['Close'][row_index]), float(dataframe['Close'][row_index - 1])) return dataframe return None if __name__ == '__main__': # コマンドラインで使うための設定 parser = argparse.ArgumentParser(description = 'YahooファイナンスからDLしたCSVに前日終値と当日終値から対数を計算した列を追加') parser.add_argument('-d', '--dir', required = True, help = 'ダウンロードしたcsvファイルが含まれるディレクトリ') parser.add_argument('-ed', '--export_dir', default = '対数追加済み', help = '編集後のCSVを入れるディレクトリ') args = parser.parse_args() # 保存先のパス export_directory = os.path.join(args.dir, args.export_dir) # 選択したディレクトリからCSVファイルだけを検索 for file_path in glob.glob(os.path.join(args.dir, '*.csv')): # 対数計算をする stock_csv = add_log(pd.read_csv(file_path)) if stock_csv is None: continue if not os.path.exists(export_directory): os.makedirs(export_directory) # CSVの書き出し stock_csv.to_csv(os.path.join(export_directory, os.path.basename(file_path))) 問題の箇所は関数add_logの下記の部分らしい。 dataframe['LogClose'][row_index] = math.log(float(dataframe['Close'][row_index]), float(dataframe['Close'][row_index - 1])) 手順は LogCloseという列を準備し0を代入しておく LogCloseの列に上から順にClose列(終値)を参照して計算した値を入れていく というつもりだった。 https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy へ警告の詳細を見に行くと、ビュー(同じメモリ)かコピー(違うメモリだが同じ内容)かどっちを返すかPandasでは判断できないため予期せぬバグを生むからきちんとしなさいということらしい。 2種類の解決策を記載しておく。 解決方法1(こちらを優先で使うこととする) Pandas.DataFrameのプロパティを使用する Pandasで任意の位置の要素の値を取得、変更するプロパティ at 行名、列名で一つの要素を選択 iat 行番号、列番号で一つの要素を選択 loc 行名、列名で複数の要素を選択 iloc 行番号、列番号で複数の要素を選択 locとilocは範囲で指定可能で一つのセルを選択することも可能だがatやiatよりは処理が遅くなる。 解決した関数部分のコード 最初に0を全部に入れ、LogClose列を最初に作成する手間も無くなった。 def add_log(dataframe): if 'Close' in dataframe.columns: for row_index in range(1, len(dataframe)): dataframe.at[row_index, 'LogClose'] = math.log(float(dataframe['Close'][row_index]), float(dataframe['Close'][row_index - 1])) dataframe.at[0, 'LogClose'] = 0 return dataframe return None 解決方法2 リスト内包表記で計算しながらリストを作成していく プロパティを探す前に無理やりやった方法。こっちでもいいとは思うがちゃんとしたプロパティを使いたいので解決方法1を優先とする。リスト内包表記、とても便利だけど綺麗に分けて書かないと式がわかりにくくなる。 解決した関数部分のコード def add_log(dataframe): if 'Close' in dataframe.columns: for row_index in range(1, len(dataframe)): dataframe['LogClose'] = [0] + [math.log(float(dataframe['Close'][row_index]), float(dataframe['Close'][row_index - 1])) for row_index in range(1, len(stock_csv))] return dataframe return None 急に2回floatへキャストしているところが気になってきた。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】株価チャートが似ている銘柄をスクリーニングする

こんにちは,藤野です. 普段はこちらで投資やプログラミングに関する記事を書いています. 今回は,「株価チャートの形が似ている銘柄をスクリーニングする方法」について説明します. 動機 形が似ている銘柄をスクリーニングするメリットとして, ・形が同じような銘柄は避け,分散投資に役立てる ・形が同じような銘柄を買い,ひとつの個別銘柄の不調を補う ・形が同じような銘柄の共通点を分析することで投資に役立てる などがあります. 例えば,コロナショックで大暴落した銘柄の株価推移と似ている銘柄スクリーニングすることで,どんなセクターで暴落が起きたのかを分析することができます. 「形が似ている」の定量化 形の似ているチャートを抽出するには,「形が似ている」を定量化しなければなりません. 色々方法はあると思いますが,今回は株価チャートのスケールを0~1に正規化した後,MAEで比較する方法で行います. この部分については,以下の記事で説明しています. 形が似ている銘柄をスクリーニングする Pythonコードおよびその説明は以下の記事で説明しています. この記事にあるコードを実行すると,例えばUAL(ユナイテッド・エアライン・ホールディングス)のチャートと似ている銘柄をスクリーニングすると,以下のような結果が得られます. MAE(類似度) 銘柄 ALK 0.117049 BA 0.0786378 BXP 0.11543 CCL 0.142564 CNP 0.119631 COP 0.129644 DAL 0.0879942 XOM 0.120234 FRT 0.0716615 HST 0.137468 L 0.138638 MTB 0.143658 NCLH 0.0890963 OMC 0.125869 OKE 0.101436 PSX 0.139819 REG 0.0967346 RCL 0.109596 SPG 0.0745186 USB 0.14598 UAL 0 VNO 0.0915754 WFC 0.140196 このうち,例えば形が似ていると判定されたWFC(ウェルズ・ファーゴ)とUALを比較すると,以下のようになります. どちらもコロナショックで大きく下落した銘柄ですね. 形が似た銘柄をスクリーニングするソースコードはこちら
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Line Messaging API + PythonとLambdaを使って、オウム返しBotをつくるのまとめ

LineでAPIがあることを知りまして API(Application Programming Interface)とLambdaの挙動の勉強を兼ねてBOTを作成してみました 謝辞:このお二人の記事にとても助けられました というかこの2つの記事をそのまま実行したので、 詳しくはこちらをご覧くださいませ AWS Lambda + Python + LINE Botで傘が必要か教えてもらう API GatewayとLambda(Python)でLINE BOT(Messaging API)開発 [前編] 非常にわかりやすく丁寧に書かれており、BOT作成の喜びと わかりやすい記事を読んでいく快感が得られました 自分の知識不足により途方に暮れた〜ようやくできた話 まずはこちらを読み進めていき、なるほどな、と思いながら オウム返しBOTまでの手順を踏んでいきました AWS Lambda + Python + LINE Botで傘が必要か教えてもらう LINE Developersでチャンネル作成〜Lambda、APIGatewayのトリガー追加まで何事もなく行きました Botでオウム返しができなかった... 何が足りないんだろう(自分の知識なんですけど)と、途方に暮れつつ、他の記事を見ていきました そこでこの記事を見つけました API GatewayとLambda(Python)でLINE BOT(Messaging API)開発 [前編] 最初に自分が書き写した記述に一番近い内容でしたので、この記事を読み進めていきました こちらはAPIGatewayをトリガーとして追加ではなく、新規作成しており、 こちらの作り方も、なるほどな、と思いながら 「こんにちは!」と自動で返してくれるBOT作成まで、無事作ることができました! 若干手こずった自分のチャンネルを自分のトークに追加する方法もこちらでわかりやすかったです 再度オウム返しBOTを作ってみる 「こんにちは!」と自動で返してくれはしましたが、自分の作りたかったのはオウム返しBOT Lambda関数とAPIGatewayを一旦削除し、再度こちらの手順に沿って構築していき、 お世話になった2つのコードをガッチャンコしながら、試行錯誤を開始しました ようやく出来上がったのがこちら! ほーら、ホカホカのコードができましたよー。さぁ、食べましょ食べましょ!「パ〜プ〜」あ!とうふ屋さんが来てる!とうふ買うのをすっかり忘れてた!お兄ちゃん、ちょっととうふ買ってきて!いやいや言わないで、すぐだから!ねっ!おつりはあげるから!ほら、はやくしないと!とうふ屋さん行っちゃうから!...ありがとう!さ、あらためて食べましょ食べましょ! import json import urllib.request def lambda_handler(event, context): for message_event in json.loads(event['body'])['events']: url = 'https://api.line.me/v2/bot/message/reply' headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + 'あなたの取得したチャンネルアクセストークン' } body = { 'replyToken': json.loads(event['body'])['events'][0]['replyToken'], 'messages': [ { "type": "text", "text": message_event['message']['text'], } ] } req = urllib.request.Request(url, data=json.dumps(body).encode('utf-8'), method='POST', headers=headers) with urllib.request.urlopen(req) as res: logger.info(res.read().decode("utf-8")) return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') } 改行もできる 絵文字は(emoji)となる かわいい絵文字ですね〜!これどこで売っているんでしょうか???ここです!!![クリッククリック!!!] どの部分を変えたのか import urllib.request 基本的な認証、暗号化認証、リダイレクション、Cookie、その他の介在する複雑なアクセス環境において (大抵は HTTP で) URL を開くための関数とクラスを定義 を追記しました event['replyToken'] を json.loads(event['body'])['events'][0]['replyToken'] メッセージオブジェクト:送信するメッセージの内容を表すJSONオブジェクト・応答メッセージを送るには、Webhookイベントオブジェクトに含まれる応答トークンが必要 と書き換えました json.loadsとeventについて Lambdaに渡されるJSONメッセージは全て event に格納されています。 基本event['要素名'][配列の要素番号]辞書型の”キー”を指定することで”値”を取得することができます。 また、配列になっている場合はその配列の"要素番号"を指定することで配列の要素を取得できます。 【AWS】LambdaのJSON入力と仲良くする(Python版) ありがとうございます Webhookイベントオブジェクトの例のような形になっているJSONをデコードしてeventsの0番目のreplyTokenを取得したんですよね Lambdaではevent.bodyでリクエストボディを取得 デフォルトではこのbodyは文字列なので、Content-Type:application/jsonの場合も「JSON文字列」になります。 そのため、一度JSONをパースする API GatewayからLambdaを呼び出すときにevent.bodyをオブジェクトで受け取る ありがとうございます JSONをパースするとは? JSON(JavaScript Object Notation)は名前の通り、JavaScriptのオブジェクトの表記法をベースとしたデータ形式であるため、例えばブラウザ側からJavaScriptなどでサーバー側のJavaなどに渡される JavaでJSONを受け取ろうとすると、当然、そんなデータの形(JSON)は理解できないってことになるため、Javaで処理できるようにJSONをパース(解析)することが必要 JSON(JavaScript Object Notation)をパースするとは? quicktypeというサービスがアツいらしい ありがとうございます そもそもAPIとは? Application Programming Interface ソフトウェアにAPIという外部とやりとりする窓口を作り、外部アプリとコミュニケーションや連携ができる状態にする 今さら聞けないIT用語:やたらと耳にするけど「API」って何? ありがとうございます その他、調べて知ったこと一覧 応答トークンというもの(房総半島みたいな響き)があり、これはLine側が自動で割り振って送ってくれるもの(だと思う) 'Bearer 'のところの半角スペースっているのだろうか?と思ったら、いるものでした 記述をしたらデプロイボタンを押すのと、この時点でテストをしてエラーがでても、実際は動きました APIのほかにSDKというもの(あるシステムに対応したソフトウェアを開発するために必要なプログラムや文書などをひとまとめにしたパッケージ)があり、LineにもLINE Messaging API SDKというパッケージが用意されている Herokuといもの(アプリケーションの開発から実行、運用までのすべてをクラウドで完結できる PaaS(サービスとしてのプラットフォーム))があり、それを介して、LineBotを作ることもできる BotでLineスタンプを送る場合、送信可能なラインスタンプ一覧こちらにがありました かわいい絵文字ですね〜!これどこで売っているんでしょうか???ここです!!![クリッククリック!!!]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JetsonXavierでYOLOv5+DeepSortを動かす。Docker使用

元イメージ 元イメージには、nvidiaが用意しているイメージを使用します。 https://ngc.nvidia.com/catalog/containers/nvidia:l4t-pytorch FROM nvcr.io/nvidia/l4t-pytorch:r32.5.0-pth1.7-py3 apt install 必要なパッケージをインストールします。 xtermは、docker上の表示をローカルに表示するために必要です。 yoloの場合、Docker上の推論結果の画面をローカルで表示するために使います。 FROM nvcr.io/nvidia/l4t-pytorch:r32.5.0-pth1.7-py3 RUN apt update \ && apt install -y \ sudo \ vim \ xterm pip install Yoloで必要なパッケージをインストールします。 ここで注意が必要で、yoloのrequirement.txtをそのままインストールすると、torchやtorchvisionのバージョンが崩れてしまいます。Nvidaiのイメージにこれらはすでに含まれているため、txtから抜きました。 また、pipをアップグレードする必要があります。 FROM nvcr.io/nvidia/l4t-pytorch:r32.5.0-pth1.7-py3 RUN apt update \ && apt install -y \ sudo \ vim \ xterm RUN pip3 install -U pip RUN git clone https://github.com/kyasby/jetson-yolo.git requirements WORKDIR requirements RUN pip3 install -r requirements.txt おわりに さんざん苦労しましたが、以上のみで動きます。 https://github.com/kyasby/jetson-yolo.git 必要であれば、レポジトリそのままお使いもいただけます。 注意 --runtime nvidiaと指定しないと、import torchでエラーが出ます。 dockerの起動コマンドが長いため、docker-composeを使用しました。 install方法はこちら。 https://qiita.com/ryryry/items/b1d4b8604c71f638fe06
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む