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

pycopg2のインストールエラーとopensslについて

psycopg2をインストールしようとした際に下記エラーが発生 ld: library not found for -lssl clang: error: linker command failed with exit code 1 (use -v to see invocation) error: command 'clang' failed with exit status 1 どうやらpsycopg2がC言語ベースの拡張モジュールなので、コンパイル作業が行われるわけですが、その際にリンカがsslライブラリを見失っている、というのがエラーの原因のようです。 ですので、このリンカが参照する箇所を環境変数として指定してあげれば良いようです。 C言語をやっているとmakefileなんかで指定する下記のフラグ export LDFLAGS="-L/usr/local/opt/openssl/lib" export CPPFLAGS="-I/usr/local/opt/openssl/include" をそれぞれ指定します。 なおopensslなどがインストールされていない場合は適宜インストールしてください。 簡単に言うと(というか簡単にしか理解できてない)、LDFLAGSはリンカ(LD)が参照するフラグを指定し、 CPPFLAGSはC/C++が参照するフラグです ※Cのみの場合はCFLAGSですかね。psycopg2の中身を見たわけではないのでCPPFLAGSの方が無難かなと思ってます これで再度brewなりpipなりでpsycopg2をインストールすればきっと成功するはずです。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

黙々とMNISTを見るPython(MLP)

import tensorflow as tf tf.__version__ '2.5.0-rc1' 以下、黙々とMNISTのデータセットを使ってMLP(マルチ・レベル・パーセプトロン)をやっていきます。 このデータセットは0から9の10種類の数字の手書きの画像が入っています。 予測の難しい数字、正解(ラベル)が9で予測値が4などの出現数などを見ていくのがゴールです。 準備と前処理 import numpy as np from tensorflow.keras.datasets import mnist import matplotlib.pyplot as plt np.random.seed(42) (x_train, y_train), (x_test,y_test) = mnist.load_data() 含まれているラベルの数を数えてみると、各数字の画像の枚数は均等ではないようです。 unique, counts = np.unique(y_train, return_counts=True) print('Train labels:', dict(zip(unique,counts))) Train labels: {0: 5923, 1: 6742, 2: 5958, 3: 6131, 4: 5842, 5: 5421, 6: 5918, 7: 6265, 8: 5851, 9: 5949} unique, counts = np.unique(y_test, return_counts=True) print('Test labels:', dict(zip(unique,counts))) Test labels: {0: 980, 1: 1135, 2: 1032, 3: 1010, 4: 982, 5: 892, 6: 958, 7: 1028, 8: 974, 9: 1009} ランダムな25個の画像を表示。 indexes = np.random.randint(0, x_train.shape[0],size=25) indexes array([56422, 15795, 860, 38158, 54343, 44732, 11284, 54886, 6265, 16850, 37194, 21962, 47191, 44131, 16023, 41090, 1685, 769, 59735, 56101, 2433, 5311, 37819, 39188, 17568]) images = x_train[indexes] labels = y_train[indexes] plt.figure(figsize=(5,7)) for i in range(25): plt.subplot(5,5,i+1) plt.title(labels[i]) plt.imshow(images[i],cmap='gray') plt.axis('off') plt.show() すでにだいぶ読みづらいのが混ざっているねぇ。 import numpy as np from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Activation, Dropout from tensorflow.keras.utils import to_categorical, plot_model from tensorflow.keras.datasets import mnist (x_train, y_train), (x_test, y_test) = mnist.load_data() y_train array([5, 0, 4, ..., 5, 6, 8], dtype=uint8) y_test array([7, 2, 1, ..., 4, 5, 6], dtype=uint8) num_labels = len(np.unique(y_train)) num_labels 10 len(set(y_train)) 10 ラベルYを2進数表記にする(one-hot vector) y_train = to_categorical(y_train) y_train array([[0., 0., 0., ..., 0., 0., 0.], [1., 0., 0., ..., 0., 0., 0.], [0., 0., 0., ..., 0., 0., 0.], ..., [0., 0., 0., ..., 0., 0., 0.], [0., 0., 0., ..., 0., 0., 0.], [0., 0., 0., ..., 0., 1., 0.]], dtype=float32) y_test = to_categorical(y_test) y_test array([[0., 0., 0., ..., 1., 0., 0.], [0., 0., 1., ..., 0., 0., 0.], [0., 1., 0., ..., 0., 0., 0.], ..., [0., 0., 0., ..., 0., 0., 0.], [0., 0., 0., ..., 0., 0., 0.], [0., 0., 0., ..., 0., 0., 0.]], dtype=float32) データXを0から1のfloatにする (normalization) image_size = x_train.shape[1] image_size 28 input_size = image_size * image_size input_size 784 x_train = np.reshape(x_train, [-1, input_size]) x_train array([[0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], ..., [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0]], dtype=uint8) x_train = x_train.astype('float32') / 255 x_train array([[0., 0., 0., ..., 0., 0., 0.], [0., 0., 0., ..., 0., 0., 0.], [0., 0., 0., ..., 0., 0., 0.], ..., [0., 0., 0., ..., 0., 0., 0.], [0., 0., 0., ..., 0., 0., 0.], [0., 0., 0., ..., 0., 0., 0.]], dtype=float32) x_test = np.reshape(x_test, [-1, input_size]) x_test array([[0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], ..., [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0]], dtype=uint8) x_test = x_test.astype('float32') / 255 x_test array([[0., 0., 0., ..., 0., 0., 0.], [0., 0., 0., ..., 0., 0., 0.], [0., 0., 0., ..., 0., 0., 0.], ..., [0., 0., 0., ..., 0., 0., 0.], [0., 0., 0., ..., 0., 0., 0.], [0., 0., 0., ..., 0., 0., 0.]], dtype=float32) MLPモデルの構築 batch_size = 128 hidden_units = 256 dropout = 0.45 model = Sequential() model.add(Dense(hidden_units, input_dim=input_size)) model.add(Activation('relu')) model.add(Dropout(dropout)) model.add(Dense(hidden_units)) model.add(Activation('relu')) model.add(Dropout(dropout)) model.add(Dense(num_labels)) model.add(Activation('softmax')) model.summary() Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= dense (Dense) (None, 256) 200960 _________________________________________________________________ activation (Activation) (None, 256) 0 _________________________________________________________________ dropout (Dropout) (None, 256) 0 _________________________________________________________________ dense_1 (Dense) (None, 256) 65792 _________________________________________________________________ activation_1 (Activation) (None, 256) 0 _________________________________________________________________ dropout_1 (Dropout) (None, 256) 0 _________________________________________________________________ dense_2 (Dense) (None, 10) 2570 _________________________________________________________________ activation_2 (Activation) (None, 10) 0 ================================================================= Total params: 269,322 Trainable params: 269,322 Non-trainable params: 0 _________________________________________________________________ plot_model(model, show_shapes=True) model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy']) model.fit(x_train,y_train,epochs=20,batch_size=batch_size) Epoch 1/20 469/469 [==============================] - 4s 7ms/step - loss: 0.7162 - accuracy: 0.7716: Epoch 2/20 469/469 [==============================] - 3s 7ms/step - loss: 0.2036 - accuracy: 0.9380 Epoch 3/20 469/469 [==============================] - 4s 8ms/step - loss: 0.1617 - accuracy: 0.9510 Epoch 4/20 469/469 [==============================] - 3s 7ms/step - loss: 0.1259 - accuracy: 0.9616 Epoch 5/20 469/469 [==============================] - 3s 6ms/step - loss: 0.1117 - accuracy: 0.9643 Epoch 6/20 469/469 [==============================] - 3s 7ms/step - loss: 0.1022 - accuracy: 0.9688 Epoch 7/20 469/469 [==============================] - 4s 7ms/step - loss: 0.0918 - accuracy: 0.9713: 0s - l Epoch 8/20 469/469 [==============================] - 3s 7ms/step - loss: 0.0834 - accuracy: 0.9730 Epoch 9/20 469/469 [==============================] - 3s 7ms/step - loss: 0.0820 - accuracy: 0.9746 Epoch 10/20 469/469 [==============================] - 3s 7ms/step - loss: 0.0756 - accuracy: 0.9765 Epoch 11/20 469/469 [==============================] - 3s 7ms/step - loss: 0.0731 - accuracy: 0.9760 Epoch 12/20 469/469 [==============================] - 3s 7ms/step - loss: 0.0691 - accuracy: 0.9778 Epoch 13/20 469/469 [==============================] - 3s 7ms/step - loss: 0.0692 - accuracy: 0.9781 Epoch 14/20 469/469 [==============================] - 3s 7ms/step - loss: 0.0601 - accuracy: 0.9809 Epoch 15/20 469/469 [==============================] - 3s 7ms/step - loss: 0.0631 - accuracy: 0.9794 Epoch 16/20 469/469 [==============================] - 3s 7ms/step - loss: 0.0550 - accuracy: 0.9831 Epoch 17/20 469/469 [==============================] - 3s 7ms/step - loss: 0.0565 - accuracy: 0.9813 Epoch 18/20 469/469 [==============================] - 3s 7ms/step - loss: 0.0532 - accuracy: 0.9831: 0s - los Epoch 19/20 469/469 [==============================] - 3s 7ms/step - loss: 0.0515 - accuracy: 0.9830 Epoch 20/20 469/469 [==============================] - 3s 7ms/step - loss: 0.0530 - accuracy: 0.9824 <tensorflow.python.keras.callbacks.History at 0x1b5c0437cd0> _,acc = model.evaluate(x_test,y_test,batch_size=batch_size,verbose=0) acc*100 98.33999872207642 的中率98.3%の学習済みモデルが作成できた。 予測 model.predict(np.reshape(x_test[0],(1,784))).argmax() 7 y_test[0].argmax() 7 テストデータの0番目による予測値はラベルと一致しているようです。 from PIL import Image Image.fromarray((np.reshape(x_test[0],(28,28))*255).astype(np.uint8),'L') 肉眼で見ても正しく感じられます。 from sklearn import metrics metrics.confusion_matrix(y_test.argmax(axis=1), model.predict(x_test).argmax(axis=1)) array([[ 974, 1, 1, 0, 0, 1, 1, 0, 1, 1], [ 0, 1126, 2, 2, 0, 0, 2, 0, 3, 0], [ 3, 1, 1017, 2, 1, 0, 1, 5, 2, 0], [ 0, 0, 3, 995, 0, 3, 0, 5, 2, 2], [ 2, 0, 2, 1, 972, 0, 2, 1, 0, 2], [ 2, 0, 0, 9, 0, 872, 4, 0, 4, 1], [ 6, 2, 0, 1, 4, 2, 942, 0, 1, 0], [ 1, 2, 8, 2, 1, 0, 0, 1008, 2, 4], [ 7, 0, 2, 2, 5, 3, 0, 3, 947, 5], [ 2, 2, 0, 3, 14, 2, 1, 4, 0, 981]], dtype=int64) Confusion Matrixをみると対角成分が大きくなっているのは正解が多いからです。14という数字の位置はラベルが9に対して4を予測したという誤りが多いことを示しています。 小ネタですが、Sympyを使うと、行列が見やすくなりますのでオススメです。(Jupyterを使用) from sympy import Matrix Matrix(metrics.confusion_matrix(y_test.argmax(axis=1), model.predict(x_test).argmax(axis=1))) $\displaystyle \left[\begin{matrix}974 & 1 & 1 & 0 & 0 & 1 & 1 & 0 & 1 & 1\\0 & 1126 & 2 & 2 & 0 & 0 & 2 & 0 & 3 & 0\\3 & 1 & 1017 & 2 & 1 & 0 & 1 & 5 & 2 & 0\\0 & 0 & 3 & 995 & 0 & 3 & 0 & 5 & 2 & 2\\2 & 0 & 2 & 1 & 972 & 0 & 2 & 1 & 0 & 2\\2 & 0 & 0 & 9 & 0 & 872 & 4 & 0 & 4 & 1\\6 & 2 & 0 & 1 & 4 & 2 & 942 & 0 & 1 & 0\\1 & 2 & 8 & 2 & 1 & 0 & 0 & 1008 & 2 & 4\\7 & 0 & 2 & 2 & 5 & 3 & 0 & 3 & 947 & 5\\2 & 2 & 0 & 3 & 14 & 2 & 1 & 4 & 0 & 981\end{matrix}\right]$ 誤りの箇所をよくみる 正解できる場合より、正しく予測できない場合をよく観察したほうが勉強になる気がしました。 fails = [(i,(np.reshape(x_test[i],(28,28))*255).astype(np.uint8),x.argmax(),y.argmax()) for i,(x,y) in enumerate(zip(model.predict(x_test),y_test)) if x.argmax()!=y.argmax()] len(fails) 166 誤っているときの予測値とラベルの組み合わせとその出現数をまとめてみました。 dict(zip(*np.unique([str({'predict':x.argmax(),'label':y.argmax()}) for x,y in zip(model.predict(x_test),y_test) if x.argmax()!=y.argmax()],return_counts=True))) { "{'predict': 0, 'label': 2}": 3, "{'predict': 0, 'label': 4}": 2, "{'predict': 0, 'label': 5}": 2, "{'predict': 0, 'label': 6}": 6, "{'predict': 0, 'label': 7}": 1, "{'predict': 0, 'label': 8}": 7, "{'predict': 0, 'label': 9}": 2, "{'predict': 1, 'label': 0}": 1, "{'predict': 1, 'label': 2}": 1, "{'predict': 1, 'label': 6}": 2, "{'predict': 1, 'label': 7}": 2, "{'predict': 1, 'label': 9}": 2, "{'predict': 2, 'label': 0}": 1, "{'predict': 2, 'label': 1}": 2, "{'predict': 2, 'label': 3}": 3, "{'predict': 2, 'label': 4}": 2, "{'predict': 2, 'label': 7}": 8, "{'predict': 2, 'label': 8}": 2, "{'predict': 3, 'label': 1}": 2, "{'predict': 3, 'label': 2}": 2, "{'predict': 3, 'label': 4}": 1, "{'predict': 3, 'label': 5}": 9, "{'predict': 3, 'label': 6}": 1, "{'predict': 3, 'label': 7}": 2, "{'predict': 3, 'label': 8}": 2, "{'predict': 3, 'label': 9}": 3, "{'predict': 4, 'label': 2}": 1, "{'predict': 4, 'label': 6}": 4, "{'predict': 4, 'label': 7}": 1, "{'predict': 4, 'label': 8}": 5, "{'predict': 4, 'label': 9}": 14, "{'predict': 5, 'label': 0}": 1, "{'predict': 5, 'label': 3}": 3, "{'predict': 5, 'label': 6}": 2, "{'predict': 5, 'label': 8}": 3, "{'predict': 5, 'label': 9}": 2, "{'predict': 6, 'label': 0}": 1, "{'predict': 6, 'label': 1}": 2, "{'predict': 6, 'label': 2}": 1, "{'predict': 6, 'label': 4}": 2, "{'predict': 6, 'label': 5}": 4, "{'predict': 6, 'label': 9}": 1, "{'predict': 7, 'label': 2}": 5, "{'predict': 7, 'label': 3}": 5, "{'predict': 7, 'label': 4}": 1, "{'predict': 7, 'label': 8}": 3, "{'predict': 7, 'label': 9}": 4, "{'predict': 8, 'label': 0}": 1, "{'predict': 8, 'label': 1}": 3, "{'predict': 8, 'label': 2}": 2, "{'predict': 8, 'label': 3}": 2, "{'predict': 8, 'label': 5}": 4, "{'predict': 8, 'label': 6}": 1, "{'predict': 8, 'label': 7}": 2, "{'predict': 9, 'label': 0}": 1, "{'predict': 9, 'label': 3}": 2, "{'predict': 9, 'label': 4}": 2, "{'predict': 9, 'label': 5}": 1, "{'predict': 9, 'label': 7}": 4, "{'predict': 9, 'label': 8}": 5 } Confusion Matrixを見慣れていないので、自分にはコッチのほうがわかりやすいです。縦横のどっちがラベルなのかこんがらがります。 さいごに、一体どんな画像を読み誤っていたのかを列挙します。これは人の目で見ても判断がつかんだろうと思うものも多いです。 plt.figure(figsize=(6,8),dpi=200) for n,(i,img,x,y) in enumerate(fails): plt.subplot(10,17,n+1) plt.title(str(i)+':\npred:'+str(x)+'\nlabel:'+str(y),fontsize=4) plt.imshow(img,cmap='gray') plt.axis('off') plt.show() よく見るとむしろラベルの間違いを疑うような無理ゲーっぽいのがあるので、的中率100%は必ずしもいいモデルとは言えないでしょうね(汎化性能が落ちる)。 機械学習の初歩中の初歩はこれでクリアです。ランダムシードを固定しないと結果が毎回変わってしまうことを勉強するのにも役立ちました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Naive Bayesをざっと理解する

みなさん、こんちには 【watnowテックカレンダーの14日目】 今日はwatnowのantonyが担当します よろしくお願いします! はじめに テキスト分類によく使われるアルゴリズムである、ナイーブベイズを簡単にまとめてみました。ナイーブベイズ は文章の分類やスパムメール判定など、自然言語の分類でよく利用されます。 概要 ナイーブベイズは確率に基づいて予測を行うアルゴリズムの一つです。データがあるラベルである確率を計算し、確率が最大になるラベルに分類する方法です。 今回は、ナイーブベイズを用いて、架空のニュース記事タイトルを「映画」と「宇宙」という2つのカテゴリに分類してみましょう。 学習データ 学習データ カテゴリ あの名作映画が蘇る 映画 ド派手アクション映画が封切り 映画 蘇った名作に世界が感動 映画 砂嵐が火星を襲う 宇宙 火星探索ついに再会 宇宙 VRでみる火星の砂嵐に感動 宇宙 検証データ 検証データ カテゴリ 復活した名作アクションに感動 ? 学習データをもとに、未知データのカテゴリが映画である確率と宇宙である確率を計算する方法を考えていきます。 まず、検証データの「感動」という単語に着目してみます。「感動」という単語が出現する確率を素ぞれのカテゴリで見ると以下の表のようになります。 カテゴリ 「感動」という単語が出現する条件付き確立 映画 2/3 宇宙 1/3 表の条件付き確立を見ると、「感動」という単語が現れる確率は、宇宙よりも映画の方が高いことがわかります。 ナイーブベイズでは、文章中の出現確率だけでなく、単語ごとの条件付き確立も用いて、文章内の単語の情報を使い求める確率の精度を上げています。 アルゴリズム 前処理 前処理では、文章をBag of Wardsに変換し、特徴量からなるベクトルとラベルの組みにします。まず、与えられた学習データの文章について名詞のみを抜き出します。学習データから名詞のみを抜き出した結果を下の表に示します。 学習データ カテゴリ "名作","映画" 映画 "派手","アクション","映画" 映画 "名作","世界","感動" 映画 "砂嵐","火星" 宇宙 "火星","探索","再会" 宇宙 "VR","火星","砂嵐","感動" 宇宙 次に、学習データとカテゴリをデータとして扱いやすいように、学習データは、全体の単語集合から学習データに単語が含まれている時に1とし、学習データに含まれていない時に0とします。また、カテゴリは映画を1、宇宙を0とします。 上記を以下の表に示します。 学習データ 名作 映画 派手 アクション 世界 感動 砂嵐 火星 探査 再会 VR カテゴリ あの名作が蘇る 1 1 0 0 0 0 0 0 0 0 0 1 ド派手アクション映画が封切り 0 1 1 1 0 0 0 0 0 0 0 1 蘇った名作に世界が感動 1 0 0 0 1 1 0 0 0 0 0 1 砂嵐が火星を襲う 0 0 0 0 0 0 1 1 0 0 0 0 火星探査ついに再会 0 0 0 0 0 0 0 1 1 1 0 0 VRで見る火星の砂嵐に感動 0 0 0 0 0 1 0 1 1 0 1 0 同様に、検証データの文章の方もBag of Wardsで表現していきます。ここで、検証データには「復活」という単語が含まれています。本来であれば、学習データのBag of Wardsにも復活を含ませ、ラベルを0とするのですが、今回は簡単のため無視します。 検証データ 名作 映画 派手 アクション 世界 感動 砂嵐 火星 探査 再会 VR カテゴリ 復活した名作アクションに感動 1 0 0 1 0 1 0 0 0 0 0 ? 確率の計算 ここからが本番です。ナイーブベイズでは、学習データを用いて、それぞれのラベルごとに単語が現れる確立を学習していきます。分類時にはラベルごとに確立を求め、確率が高い方を分類結果とします。(これは、ラベルの数が増えても変わりません。) ナイーブベイズ では、それぞれのラベルが出現する確率と、各ラベルでのそれぞれの単語が出現する条件付き確立を求めます。 ※本来であればスムージングと言って、確立が0となるものを0.01を割り振ると言った処理を行いますが、この説明は簡単のため省かせていただきます。 この二つの確立を掛け合わせ比較することで分類を行います。また、ナイーブベイズは、それぞれの単語が独立であるという単純な仮定をおきます。 Pythonで実行 これまでの処理を、実際にコードで見てみましょう。 今回は、scikit-learnを使用します。 from sklearn.naive_bayes import MultinomailNB X_train = [[1,1,0,0,0,0,0,0,0,0,0], [0,1,1,1,0,0,0,0,0,0,0], [1,0,0,0,1,1,0,0,0,0,0], [0,0,0,0,0,0,1,1,0,0,0], [0,0,0,0,0,0,0,1,1,1,0], [0,0,0,0,0,1,0,1,1,0,1]] y_train = [1,1,1,0,0,0] model = MultinomialNB() #モデルの学習 model.fit(X_train, y_train) #モデルの評価 model.predict([[1,0,0,1,0,1,0,0,0,0,0]]) 実行結果を以下に示します。 array([1]) 上記の実行結果のように、「映画」と判定されています。 最後に 今回は、コードの説明というよりは、ナイーブベイズを簡単に理解することを目的としました。 サンプルコードからわかるように、とても簡単なので、皆さん是非試してみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PySide2をゼロから学んでいく~#5 ラジオボタン~

はじめに PythonでGUI開発をするためのライブラリ「PySide2」の基本的な使い方を、いくつかの段階に分けて説明していきます。 当ページではPySide2のラジオボタンについて説明しています。 環境 以下の通りになります。 Windows 10 Python 3.8以降 ベースプログラム 以下のウィンドウを表示するだけのプログラムをベースにして実装していきます。 # PySide2のモジュールを読み込む from PySide2 import QtWidgets # ウィンドウの見た目と各機能(今はウィンドウだけ) class MainWindow(QtWidgets.QWidget): def __init__(self): super().__init__() # アプリの実行と終了 app = QtWidgets.QApplication() window = MainWindow() window.show() app.exec_() PySide2でラジオボタン ラジオボタンを表示する 3つの選択肢を持つラジオボタンを表示します。ここでは応用が効くようにラジオボタンのグループ化も行います。 サンプルプログラム # PySide2のモジュールを読み込む from PySide2 import QtWidgets # ウィンドウの見た目と各機能 class MainWindow(QtWidgets.QWidget): def __init__(self): super().__init__() # ラジオボタンのグループ登録用オブジェクト self.radioGroup = QtWidgets.QButtonGroup() # ラジオボタンオブジェクトの生成 radioButton1 = QtWidgets.QRadioButton("Radio Button 1", self) radioButton2 = QtWidgets.QRadioButton("Radio Button 2", self) radioButton3 = QtWidgets.QRadioButton("Radio Button 3", self) # ラジオボタンオブジェクトのグループ登録 self.radioGroup.addButton(radioButton1, 1) self.radioGroup.addButton(radioButton2, 2) self.radioGroup.addButton(radioButton3, 3) # ラジオボタンの配置設定 radioButton1.move(10, 0) radioButton2.move(10, 30) radioButton3.move(10, 60) # アプリの実行と終了 app = QtWidgets.QApplication() window = MainWindow() window.show() app.exec_() 実行結果 プログラム解説 ● ラジオボタンのグループ管理 「あるグループの中から一つ選ぶ」というのがラジオボタンの基本な考え方です。そのグループに選択肢を登録して管理するためのオブジェクトをQButtonGroup()クラスを使用して生成します。 書式: QtWidgets.QButtonGroup() 引数: 無し サンプルプログラムでは単にQButtonGroup()クラスで生成したボタングループオブジェクトをradioGroupに格納しているだけです。 # ラジオボタンのグループ登録用オブジェクト radioGroup = QtWidgets.QButtonGroup() ● ラジオボタンの生成 QRadioButton()クラスでラジオボタンのオブジェクトを生成します。 書式: QtWidgets.QRadioButton("string", window_object) 引数: string: ラジオボタンに表示させる文字列 window_object: ラジオボタンを表示させるウィンドウのオブジェクト サンプルプログラムでは各々のラジオボタンにRadio Button <連番>という名前付けでウィンドウselfに配置しています。 # ラジオボタンオブジェクトの生成 radioButton1 = QtWidgets.QRadioButton("Radio Button 1", self) radioButton2 = QtWidgets.QRadioButton("Radio Button 2", self) radioButton3 = QtWidgets.QRadioButton("Radio Button 3", self) ● グループ登録と識別番号の割り当て ラジオボタンをグループ登録するにはaddButton()メソッドを使用します。 書式: QtWidgets.QButtonGroup().addButton(button_object, id) 引数: button_object: QtWidgets.QRadioButton()メソッドで生成したオブジェクト id: 「-1」を除く数字(-1は未入力時の値 ⇒ エラー値) button_objectにはQtWidgets.QRadioButton()クラスで生成したラジオボタンのオブジェクトを指定します。 idには-1を除く識別番号を指定します。選択したラジオボタンに応じて異なる機能を与えたい場合、このidを使用します。ちなみにidを指定しなければ-2から始まり、-3、-4、-5、…とデクリメントされて識別番号が割り振られます。 サンプルプログラムでは各ラジオボタンオブジェクトradioButton1~3をradioGroupに登録しています。 # ラジオボタンオブジェクトのグループ登録 self.radioGroup.addButton(radioButton1, 1) self.radioGroup.addButton(radioButton2, 2) self.radioGroup.addButton(radioButton3, 3) ● ラジオボタンの配置 配置設定をしないとラジオボタン同士で上書きされます。それを防ぐために各々のラジオボタンの配置場所を決めます。 書式: QtWidgets.QRadioButton("string", window_object).move(x, y) 引数: x: x座標(横方向)に配置する値(px単位) y: y座標(縦方向)に配置する値(px単位) サンプルプログラムではx座標は全て10pxの位置に、y座標は30pxずつズラして配置しています。 # ラジオボタンの配置設定 radioButton1.move(10, 0) radioButton2.move(10, 30) radioButton3.move(10, 60) ラジオボタンの初期入力を決定する ラジオボタンはいずれか一つを選択するボタンなので、未入力状態はおかしいです。そこで最初から「2番目のボタン」が入力されている状態にしてみます。 サンプルプログラム 先ほどのサンプルプログラムに1文加わっただけです。 # PySide2のモジュールを読み込む from PySide2 import QtWidgets # ウィンドウの見た目と各機能 class MainWindow(QtWidgets.QWidget): def __init__(self): super().__init__() # ラジオボタンのグループ self.radioGroup = QtWidgets.QButtonGroup() # ラジオボタンオブジェクトの生成 radioButton1 = QtWidgets.QRadioButton("Radio Button 1", self) radioButton2 = QtWidgets.QRadioButton("Radio Button 2", self) radioButton3 = QtWidgets.QRadioButton("Radio Button 3", self) # ラジオボタンオブジェクトのグループ登録 self.radioGroup.addButton(radioButton1, 1) self.radioGroup.addButton(radioButton2, 2) self.radioGroup.addButton(radioButton3, 3) # 2番目のラジオボタンを初期入力として設定 radioButton2.setChecked(True) # ラジオボタンの配置設定 radioButton1.move(10, 0) radioButton2.move(10, 30) radioButton3.move(10, 60) # アプリの実行と終了 app = QtWidgets.QApplication() window = MainWindow() window.show() app.exec_() 実行結果 プログラム解説 ● 初期入力のボタンを選択 setChecked()メソッドで複数あるラジオボタンの一つを初期入力状態にできます。 書式: QtWidgets.QRadioButton("string", window_object).setChecked(bool) 引数: bool: 「True」または「False」を指定。 初期入力状態にする場合はTrueを引数に指定。 サンプルプログラムの初期入力を設定している所です。特に説明するところはないです。 # 2番目のラジオボタンを初期入力として設定 radioButton2.setChecked(True) 選択したラジオボタンのIDを取得する 選択したラジオボタンのIDを取得するプッシュボタンを実装します。 サンプルプログラム # PySide2のモジュールを読み込む from PySide2 import QtWidgets # ウィンドウの見た目と各機能 class MainWindow(QtWidgets.QWidget): def __init__(self): super().__init__() # ラジオボタンのグループ登録用オブジェクト self.radioGroup = QtWidgets.QButtonGroup() # ラジオボタンオブジェクトの生成 radioButton1 = QtWidgets.QRadioButton("Radio Button 1", self) radioButton2 = QtWidgets.QRadioButton("Radio Button 2", self) radioButton3 = QtWidgets.QRadioButton("Radio Button 3", self) # ラジオボタンオブジェクトのグループ登録 self.radioGroup.addButton(radioButton1, 1) self.radioGroup.addButton(radioButton2, 2) self.radioGroup.addButton(radioButton3, 3) # 2番目のラジオボタンを初期入力として設定 radioButton2.setChecked(True) # ラジオボタンの配置設定 radioButton1.move(10, 0) radioButton2.move(10, 30) radioButton3.move(10, 60) # 選択中のラジオボタンのIDを取得するボタン button = QtWidgets.QPushButton("IDを取得", self) button.clicked.connect(self.print_radiobutton_id) button.move(10, 90) # ラジオボタンを表示するメソッド def print_radiobutton_id(self): button_id = self.radioGroup.checkedId() # 選択中のラジオボタンID print("Radio Button ID:", button_id) # ID表示 # アプリの実行と終了 app = QtWidgets.QApplication() window = MainWindow() window.show() app.exec_() 実行結果 プログラム解説 ラジオボタンがQButtonGroup()クラスでグループ登録されていることが前提です。グループ登録されていればcheckedId()でラジオボタンの識別番号IDを取得できます。 書式: QtWidgets.QButtonGroup().checkedId() 引数: なし サンプルプログラムではボタンをクリックしたらprint_radiobutton_idメソッドを実行し、選択されている def __init__(self): ...(略)... # 選択中のラジオボタンのIDを取得するボタン button = QtWidgets.QPushButton("IDを取得", self) button.clicked.connect(self.print_radiobutton_id) button.move(10, 90) # ラジオボタンを表示するメソッド def print_radiobutton_id(self): button_id = self.radioGroup.checkedId() # 選択中のラジオボタンID print("Radio Button ID:", button_id) # ID表示 ラジオボタンを使ったサンプルプログラム ラジオボタンを使用した簡単なサンプルプログラムをいくつかご披露。 選択したアプリを起動する PySide2をゼロから学んでいく~#2 ボタン~でも紹介したサンプルプログラムのラジオボタン版になります。以下のアプリを起動するラジオボタンになります。 Chrome サクラエディタ Kindle import subprocess # PySide2のモジュールを読み込む from PySide2 import QtWidgets # ウィンドウの見た目と各機能 class MainWindow(QtWidgets.QWidget): def __init__(self): super().__init__() self.setGeometry(200, 200, 500, 150) # アプリの案内を表示 label = QtWidgets.QLabel(self) label.setStyleSheet("""QLabel { font-size: 24px; /* 文字サイズ */ font-weight: bold; /* 太文字 */ font-family: umeboshi; /* 梅干フォント */ }""") label.setText("起動したいアプリを選択してください:") # ラジオボタンのグループ登録用オブジェクト self.radioGroup = QtWidgets.QButtonGroup() # ラジオボタンオブジェクトの生成 chromeButton = QtWidgets.QRadioButton("Chrome", self) sakuraButton = QtWidgets.QRadioButton("サクラエディタ", self) kindleButton = QtWidgets.QRadioButton("Kindle", self) # ラジオボタンオブジェクトのグループ登録 self.radioGroup.addButton(chromeButton, 1) self.radioGroup.addButton(sakuraButton, 2) self.radioGroup.addButton(kindleButton, 3) # Chromeを初期入力として設定 chromeButton.setChecked(True) # ラジオボタンの配置設定 chromeButton.move(10, 30) sakuraButton.move(10, 60) kindleButton.move(10, 90) # 起動ボタン runButton = QtWidgets.QPushButton("アプリを起動", self) runButton.clicked.connect(self.run_application) runButton.move(10, 120) def run_application(self): appId = { "Chrome": 1, "Sakura": 2, "Kindle": 3 } if appId["Chrome"] == self.radioGroup.checkedId(): subprocess.Popen(R"C:\Program Files\Google\Chrome\Application\chrome.exe") elif appId["Sakura"] == self.radioGroup.checkedId(): subprocess.Popen(R"C:\Program Files (x86)\sakura\sakura.exe") elif appId["Kindle"] == self.radioGroup.checkedId(): subprocess.Popen(R"C:\Users\からくり\AppData\Local\Amazon\Kindle\application\Kindle.exe") else: print("Unknown") # アプリの実行と終了 app = QtWidgets.QApplication() window = MainWindow() window.show() app.exec_() パソコンのリソース情報を取得する 事前にpsutilをインストールしてください。 pip install psutil 使用しているパソコンのリソース情報を表示するラジオボタンになります。 CPUコア数 メモリ容量 ストレージ容量 などなど import multiprocessing import psutil # PySide2のモジュールを読み込む from PySide2 import QtWidgets # ウィンドウの見た目と各機能 class MainWindow(QtWidgets.QWidget): def __init__(self): super().__init__() self.setGeometry(200, 200, 600, 300) # アプリの案内を表示 label = QtWidgets.QLabel(self) label.setStyleSheet("""QLabel { font-size: 24px; /* 文字サイズ */ font-weight: bold; /* 太字 */ }""") label.setText("確認したいリソース情報を選択してください:") # ラジオボタンのグループ登録用オブジェクト self.radioGroup = QtWidgets.QButtonGroup() # ラジオボタンオブジェクトの生成 cpuCore = QtWidgets.QRadioButton("CPUコア数", self) cpuUseRate = QtWidgets.QRadioButton("CPU使用率", self) ramCapacity = QtWidgets.QRadioButton("メモリ容量", self) ramUseRate = QtWidgets.QRadioButton("メモリ使用率", self) storageCapacity = QtWidgets.QRadioButton("ストレージ容量", self) storageUseRate = QtWidgets.QRadioButton("ストレージ使用率", self) storageFreeSpace = QtWidgets.QRadioButton("ストレージ空き容量", self) # ラジオボタンオブジェクトのグループ登録 self.radioGroup.addButton(cpuCore, 1) self.radioGroup.addButton(cpuUseRate, 2) self.radioGroup.addButton(ramCapacity, 3) self.radioGroup.addButton(ramUseRate, 4) self.radioGroup.addButton(storageCapacity, 5) self.radioGroup.addButton(storageUseRate, 6) self.radioGroup.addButton(storageFreeSpace, 7) # CPUコア数を初期入力として設定 cpuCore.setChecked(True) # ラジオボタンの配置設定 cpuCore.move(10, 30) cpuUseRate.move(10, 60) ramCapacity.move(10, 90) ramUseRate.move(10, 120) storageCapacity.move(10, 150) storageUseRate.move(10, 180) storageFreeSpace.move(10, 210) # 表示ボタン showButton = QtWidgets.QPushButton("リソース情報表示", self) showButton.clicked.connect(self.get_pc_resource) showButton.move(10, 240) def get_pc_resource(self): resourceId = { "cpu_core": 1, # CPUコア数 "cpu_use_rate": 2, # CPU使用率 "ram_capacity": 3, # メモリ容量 "ram_use_rate": 4, # メモリ使用率 "storage_capacity": 5, # ストレージ容量 "storage_use_rate": 6, # ストレージ使用率 "storage_free_space": 7 # ストレージ空き容量 } # メモリ情報の取得 ram = psutil.virtual_memory() # ストレージ情報の取得 storage = psutil.disk_usage("/") # リソース情報を表示 if resourceId["cpu_core"] == self.radioGroup.checkedId(): print("CPUコア数:", multiprocessing.cpu_count()) elif resourceId["cpu_use_rate"] == self.radioGroup.checkedId(): print("CPU使用率:", psutil.cpu_percent(interval=1)) elif resourceId["ram_capacity"] == self.radioGroup.checkedId(): print("メモリ容量:", ram.total) elif resourceId["ram_use_rate"] == self.radioGroup.checkedId(): print("メモリ使用率:", ram.used) elif resourceId["storage_capacity"] == self.radioGroup.checkedId(): print("ストレージ容量:", storage.total) elif resourceId["storage_use_rate"] == self.radioGroup.checkedId(): print("ストレージ使用率:", storage.used) elif resourceId["storage_free_space"] == self.radioGroup.checkedId(): print("ストレージ空き容量:", storage.free) else: print("Unknown") # アプリの実行と終了 app = QtWidgets.QApplication() window = MainWindow() window.show() app.exec_()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PySide2をゼロから学んでいく~#4 ラジオボタン~

はじめに PythonでGUI開発をするためのライブラリ「PySide2」の基本的な使い方を、いくつかの段階に分けて説明していきます。 当ページではPySide2のラジオボタンについて説明しています。 環境 以下の通りになります。 Windows 10 Python 3.8以降 ベースプログラム 以下のウィンドウを表示するだけのプログラムをベースにして実装していきます。 # PySide2のモジュールを読み込む from PySide2 import QtWidgets # ウィンドウの見た目と各機能(今はウィンドウだけ) class MainWindow(QtWidgets.QWidget): def __init__(self): super().__init__() # アプリの実行と終了 app = QtWidgets.QApplication() window = MainWindow() window.show() app.exec_() PySide2でラジオボタン ラジオボタンを表示する 3つの選択肢を持つラジオボタンを表示します。ここでは応用が効くようにラジオボタンのグループ化も行います。 サンプルプログラム # PySide2のモジュールを読み込む from PySide2 import QtWidgets # ウィンドウの見た目と各機能 class MainWindow(QtWidgets.QWidget): def __init__(self): super().__init__() # ラジオボタンのグループ登録用オブジェクト self.radioGroup = QtWidgets.QButtonGroup() # ラジオボタンオブジェクトの生成 radioButton1 = QtWidgets.QRadioButton("Radio Button 1", self) radioButton2 = QtWidgets.QRadioButton("Radio Button 2", self) radioButton3 = QtWidgets.QRadioButton("Radio Button 3", self) # ラジオボタンオブジェクトのグループ登録 self.radioGroup.addButton(radioButton1, 1) self.radioGroup.addButton(radioButton2, 2) self.radioGroup.addButton(radioButton3, 3) # ラジオボタンの配置設定 radioButton1.move(10, 0) radioButton2.move(10, 30) radioButton3.move(10, 60) # アプリの実行と終了 app = QtWidgets.QApplication() window = MainWindow() window.show() app.exec_() 実行結果 プログラム解説 ● ラジオボタンのグループ管理 「あるグループの中から一つ選ぶ」というのがラジオボタンの基本な考え方です。そのグループに選択肢を登録して管理するためのオブジェクトをQButtonGroup()クラスを使用して生成します。 書式: QtWidgets.QButtonGroup() 引数: 無し サンプルプログラムでは単にQButtonGroup()クラスで生成したボタングループオブジェクトをradioGroupに格納しているだけです。 # ラジオボタンのグループ登録用オブジェクト radioGroup = QtWidgets.QButtonGroup() ● ラジオボタンの生成 QRadioButton()クラスでラジオボタンのオブジェクトを生成します。 書式: QtWidgets.QRadioButton("string", window_object) 引数: string: ラジオボタンに表示させる文字列 window_object: ラジオボタンを表示させるウィンドウのオブジェクト サンプルプログラムでは各々のラジオボタンにRadio Button <連番>という名前付けでウィンドウselfに配置しています。 # ラジオボタンオブジェクトの生成 radioButton1 = QtWidgets.QRadioButton("Radio Button 1", self) radioButton2 = QtWidgets.QRadioButton("Radio Button 2", self) radioButton3 = QtWidgets.QRadioButton("Radio Button 3", self) ● グループ登録と識別番号の割り当て ラジオボタンをグループ登録するにはaddButton()メソッドを使用します。 書式: QtWidgets.QButtonGroup().addButton(button_object, id) 引数: button_object: QtWidgets.QRadioButton()メソッドで生成したオブジェクト id: 「-1」を除く数字(-1は未入力時の値 ⇒ エラー値) button_objectにはQtWidgets.QRadioButton()クラスで生成したラジオボタンのオブジェクトを指定します。 idには-1を除く識別番号を指定します。選択したラジオボタンに応じて異なる機能を与えたい場合、このidを使用します。ちなみにidを指定しなければ-2から始まり、-3、-4、-5、…とデクリメントされて識別番号が割り振られます。 サンプルプログラムでは各ラジオボタンオブジェクトradioButton1~3をradioGroupに登録しています。 # ラジオボタンオブジェクトのグループ登録 self.radioGroup.addButton(radioButton1, 1) self.radioGroup.addButton(radioButton2, 2) self.radioGroup.addButton(radioButton3, 3) ● ラジオボタンの配置 配置設定をしないとラジオボタン同士で上書きされます。それを防ぐために各々のラジオボタンの配置場所を決めます。 書式: QtWidgets.QRadioButton("string", window_object).move(x, y) 引数: x: x座標(横方向)に配置する値(px単位) y: y座標(縦方向)に配置する値(px単位) サンプルプログラムではx座標は全て10pxの位置に、y座標は30pxずつズラして配置しています。 # ラジオボタンの配置設定 radioButton1.move(10, 0) radioButton2.move(10, 30) radioButton3.move(10, 60) ラジオボタンの初期入力を決定する ラジオボタンはいずれか一つを選択するボタンなので、未入力状態はおかしいです。そこで最初から「2番目のボタン」が入力されている状態にしてみます。 サンプルプログラム 先ほどのサンプルプログラムに1文加わっただけです。 # PySide2のモジュールを読み込む from PySide2 import QtWidgets # ウィンドウの見た目と各機能 class MainWindow(QtWidgets.QWidget): def __init__(self): super().__init__() # ラジオボタンのグループ self.radioGroup = QtWidgets.QButtonGroup() # ラジオボタンオブジェクトの生成 radioButton1 = QtWidgets.QRadioButton("Radio Button 1", self) radioButton2 = QtWidgets.QRadioButton("Radio Button 2", self) radioButton3 = QtWidgets.QRadioButton("Radio Button 3", self) # ラジオボタンオブジェクトのグループ登録 self.radioGroup.addButton(radioButton1, 1) self.radioGroup.addButton(radioButton2, 2) self.radioGroup.addButton(radioButton3, 3) # 2番目のラジオボタンを初期入力として設定 radioButton2.setChecked(True) # ラジオボタンの配置設定 radioButton1.move(10, 0) radioButton2.move(10, 30) radioButton3.move(10, 60) # アプリの実行と終了 app = QtWidgets.QApplication() window = MainWindow() window.show() app.exec_() 実行結果 プログラム解説 ● 初期入力のボタンを選択 setChecked()メソッドで複数あるラジオボタンの一つを初期入力状態にできます。 書式: QtWidgets.QRadioButton("string", window_object).setChecked(bool) 引数: bool: 「True」または「False」を指定。 初期入力状態にする場合はTrueを引数に指定。 サンプルプログラムの初期入力を設定している所です。特に説明するところはないです。 # 2番目のラジオボタンを初期入力として設定 radioButton2.setChecked(True) 選択したラジオボタンのIDを取得する 選択したラジオボタンのIDを取得するプッシュボタンを実装します。 サンプルプログラム # PySide2のモジュールを読み込む from PySide2 import QtWidgets # ウィンドウの見た目と各機能 class MainWindow(QtWidgets.QWidget): def __init__(self): super().__init__() # ラジオボタンのグループ登録用オブジェクト self.radioGroup = QtWidgets.QButtonGroup() # ラジオボタンオブジェクトの生成 radioButton1 = QtWidgets.QRadioButton("Radio Button 1", self) radioButton2 = QtWidgets.QRadioButton("Radio Button 2", self) radioButton3 = QtWidgets.QRadioButton("Radio Button 3", self) # ラジオボタンオブジェクトのグループ登録 self.radioGroup.addButton(radioButton1, 1) self.radioGroup.addButton(radioButton2, 2) self.radioGroup.addButton(radioButton3, 3) # 2番目のラジオボタンを初期入力として設定 radioButton2.setChecked(True) # ラジオボタンの配置設定 radioButton1.move(10, 0) radioButton2.move(10, 30) radioButton3.move(10, 60) # 選択中のラジオボタンのIDを取得するボタン button = QtWidgets.QPushButton("IDを取得", self) button.clicked.connect(self.print_radiobutton_id) button.move(10, 90) # ラジオボタンを表示するメソッド def print_radiobutton_id(self): button_id = self.radioGroup.checkedId() # 選択中のラジオボタンID print("Radio Button ID:", button_id) # ID表示 # アプリの実行と終了 app = QtWidgets.QApplication() window = MainWindow() window.show() app.exec_() 実行結果 プログラム解説 ラジオボタンがQButtonGroup()クラスでグループ登録されていることが前提です。グループ登録されていればcheckedId()でラジオボタンの識別番号IDを取得できます。 書式: QtWidgets.QButtonGroup().checkedId() 引数: なし サンプルプログラムではボタンをクリックしたらprint_radiobutton_idメソッドを実行し、選択されている def __init__(self): ...(略)... # 選択中のラジオボタンのIDを取得するボタン button = QtWidgets.QPushButton("IDを取得", self) button.clicked.connect(self.print_radiobutton_id) button.move(10, 90) # ラジオボタンを表示するメソッド def print_radiobutton_id(self): button_id = self.radioGroup.checkedId() # 選択中のラジオボタンID print("Radio Button ID:", button_id) # ID表示 ラジオボタンを使ったサンプルプログラム ラジオボタンを使用した簡単なサンプルプログラムをいくつかご披露。 選択したアプリを起動する PySide2をゼロから学んでいく~#2 ボタン~でも紹介したサンプルプログラムのラジオボタン版になります。以下のアプリを起動するラジオボタンになります。 Chrome サクラエディタ Kindle import subprocess # PySide2のモジュールを読み込む from PySide2 import QtWidgets # ウィンドウの見た目と各機能 class MainWindow(QtWidgets.QWidget): def __init__(self): super().__init__() self.setGeometry(200, 200, 500, 150) # アプリの案内を表示 label = QtWidgets.QLabel(self) label.setStyleSheet("""QLabel { font-size: 24px; /* 文字サイズ */ font-weight: bold; /* 太文字 */ font-family: umeboshi; /* 梅干フォント */ }""") label.setText("起動したいアプリを選択してください:") # ラジオボタンのグループ登録用オブジェクト self.radioGroup = QtWidgets.QButtonGroup() # ラジオボタンオブジェクトの生成 chromeButton = QtWidgets.QRadioButton("Chrome", self) sakuraButton = QtWidgets.QRadioButton("サクラエディタ", self) kindleButton = QtWidgets.QRadioButton("Kindle", self) # ラジオボタンオブジェクトのグループ登録 self.radioGroup.addButton(chromeButton, 1) self.radioGroup.addButton(sakuraButton, 2) self.radioGroup.addButton(kindleButton, 3) # Chromeを初期入力として設定 chromeButton.setChecked(True) # ラジオボタンの配置設定 chromeButton.move(10, 30) sakuraButton.move(10, 60) kindleButton.move(10, 90) # 起動ボタン runButton = QtWidgets.QPushButton("アプリを起動", self) runButton.clicked.connect(self.run_application) runButton.move(10, 120) def run_application(self): appId = { "Chrome": 1, "Sakura": 2, "Kindle": 3 } if appId["Chrome"] == self.radioGroup.checkedId(): subprocess.Popen(R"C:\Program Files\Google\Chrome\Application\chrome.exe") elif appId["Sakura"] == self.radioGroup.checkedId(): subprocess.Popen(R"C:\Program Files (x86)\sakura\sakura.exe") elif appId["Kindle"] == self.radioGroup.checkedId(): subprocess.Popen(R"C:\Users\からくり\AppData\Local\Amazon\Kindle\application\Kindle.exe") else: print("Unknown") # アプリの実行と終了 app = QtWidgets.QApplication() window = MainWindow() window.show() app.exec_() パソコンのリソース情報を取得する 事前にpsutilをインストールしてください。 pip install psutil 使用しているパソコンのリソース情報を表示するラジオボタンになります。 CPUコア数 メモリ容量 ストレージ容量 などなど import multiprocessing import psutil # PySide2のモジュールを読み込む from PySide2 import QtWidgets # ウィンドウの見た目と各機能 class MainWindow(QtWidgets.QWidget): def __init__(self): super().__init__() self.setGeometry(200, 200, 600, 300) # アプリの案内を表示 label = QtWidgets.QLabel(self) label.setStyleSheet("""QLabel { font-size: 24px; /* 文字サイズ */ font-weight: bold; /* 太字 */ }""") label.setText("確認したいリソース情報を選択してください:") # ラジオボタンのグループ登録用オブジェクト self.radioGroup = QtWidgets.QButtonGroup() # ラジオボタンオブジェクトの生成 cpuCore = QtWidgets.QRadioButton("CPUコア数", self) cpuUseRate = QtWidgets.QRadioButton("CPU使用率", self) ramCapacity = QtWidgets.QRadioButton("メモリ容量", self) ramUseRate = QtWidgets.QRadioButton("メモリ使用率", self) storageCapacity = QtWidgets.QRadioButton("ストレージ容量", self) storageUseRate = QtWidgets.QRadioButton("ストレージ使用率", self) storageFreeSpace = QtWidgets.QRadioButton("ストレージ空き容量", self) # ラジオボタンオブジェクトのグループ登録 self.radioGroup.addButton(cpuCore, 1) self.radioGroup.addButton(cpuUseRate, 2) self.radioGroup.addButton(ramCapacity, 3) self.radioGroup.addButton(ramUseRate, 4) self.radioGroup.addButton(storageCapacity, 5) self.radioGroup.addButton(storageUseRate, 6) self.radioGroup.addButton(storageFreeSpace, 7) # CPUコア数を初期入力として設定 cpuCore.setChecked(True) # ラジオボタンの配置設定 cpuCore.move(10, 30) cpuUseRate.move(10, 60) ramCapacity.move(10, 90) ramUseRate.move(10, 120) storageCapacity.move(10, 150) storageUseRate.move(10, 180) storageFreeSpace.move(10, 210) # 表示ボタン showButton = QtWidgets.QPushButton("リソース情報表示", self) showButton.clicked.connect(self.get_pc_resource) showButton.move(10, 240) def get_pc_resource(self): resourceId = { "cpu_core": 1, # CPUコア数 "cpu_use_rate": 2, # CPU使用率 "ram_capacity": 3, # メモリ容量 "ram_use_rate": 4, # メモリ使用率 "storage_capacity": 5, # ストレージ容量 "storage_use_rate": 6, # ストレージ使用率 "storage_free_space": 7 # ストレージ空き容量 } # メモリ情報の取得 ram = psutil.virtual_memory() # ストレージ情報の取得 storage = psutil.disk_usage("/") # リソース情報を表示 if resourceId["cpu_core"] == self.radioGroup.checkedId(): print("CPUコア数:", multiprocessing.cpu_count()) elif resourceId["cpu_use_rate"] == self.radioGroup.checkedId(): print("CPU使用率:", psutil.cpu_percent(interval=1)) elif resourceId["ram_capacity"] == self.radioGroup.checkedId(): print("メモリ容量:", ram.total) elif resourceId["ram_use_rate"] == self.radioGroup.checkedId(): print("メモリ使用率:", ram.used) elif resourceId["storage_capacity"] == self.radioGroup.checkedId(): print("ストレージ容量:", storage.total) elif resourceId["storage_use_rate"] == self.radioGroup.checkedId(): print("ストレージ使用率:", storage.used) elif resourceId["storage_free_space"] == self.radioGroup.checkedId(): print("ストレージ空き容量:", storage.free) else: print("Unknown") # アプリの実行と終了 app = QtWidgets.QApplication() window = MainWindow() window.show() app.exec_()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JupyterのDocker個人的まとめ

jupyter/datascience-notebookなど、JupyterをDockerで使える(Jupyter Docker Stacks)についてまとめてみます。 このdockerイメージは、例えば普段使っているPCはWindowsやMacだけど、Linuxでしか動かないモジュールを使いたい人や、Jupyterで開発しつつ、Python環境を揃えたい人などはよく使っていると思います。ただこのdockerイメージは少し癖が強くデフォルトの状態で起動するとapt-getすらできないとか使い方がよくわからない部分があると思います。そこでこの記事で個人的にTipsをまとめてみました。 また私が使っているDocker + VSCode + Remote Containerのリポジトリもアップします。 SolKul/vscode-jupyter-container2 既にJupyteのDocker(Jupyter Docker Stacks)についての記事やサイトは山程出ています。まずそれらについてまとめ、そこにかかれていない私が役に立つと思うポイントをその後にまとめようと思います。 JupyterのDockerについての過去の記事や参考サイト Dockerで基本的なData Science環境(Jupyter, Python, R, Julia, 定番ライブラリ)を構築する。 この記事でパスワードの設定や、ポートフォワーディング、ボリュームの共有など基本的なことはすべて抑えられると思います。ただ、この記事はコンテナの起動をコマンドラインで行っています。dockerの知識がないうちはこれでdockerに慣れるのもいいですが、毎回この長いコマンドを打つのも大変です。慣れてきたらDockerfile+docker-composeを使ったほうが効率的です。 Docker + VSCode + Remote Containerで作る快適Jupyter Lab(Python)分析環境 この記事はVS CodeのRemote Containerでコンテナを動かしています。VS Codeであればファイルの配置もグラフィカルに表示され楽ですし、ファイルの中身もテキストエディタで編集できます。docker-composeも使っているので毎回コマンドを打つ必要がないです。何よりもVS Codeの強力なデバック機能や拡張機能が使えるのは魅力的です。 Jupyter Docker Stacksについてメモ 以下、Jupyter Docker Stacksを使う際のTipsをまとめます。 docker runよりdocker-composeを使う dockerに慣れてきたらdocker-composeを使うほうが断然楽です。次のdocker runコマンドと、その下のdocker-compose.ymlは同じ働きをします。 docker run \ --user root \ -e GRANT_SUDO=yes \ -e TZ=Asia/Tokyo \ -p 8888:8888 \ --name notebook \ -v ./work:/home/jovyan/work \ start-notebook.sh \ --NotebookApp.password='sha1:YOUR_PASSWORD_HASH_VALUE' docker-compose.yml version: '3' services: notebook: image: jupyter/datascience-notebook user: root ports: - '8888:8888' environment: - GRANT_SUDO=yes - TZ=Asia/Tokyo volumes: - ./work:/home/jovyan/work command: NotebookApp.password='sha1:YOUR_PASSWORD_HASH_VALUE' コマンドだと間違ってEnterを押してしまった時点で起動してしまいますが、docker-compose.ymlであれば落ち着いて編集し、最後に立ち上げる場合はdocker-compose upとすればいいだけです。またdocker-composeはVS CodeのRemote Containerでも使えます。 docker runのどのオプションがdocker-compose.ymlのどの項目に対応しているかなどは 複数のDockerコンテナを自動で立ち上げる構成管理ツール「Docker Compose」(Dockerの最新機能を使ってみよう:第7回) などを参照してください。 また、.envファイルを追加し、docker-compose.ymlを適切に書き換えればプロキシ下などでも使えます。詳しくはdocker-composeでのプロキシ設定を一つのファイルにまとめるを参考にしてください。 VSCode+Remote Containerで使う さらに言えば、VSCode+Remote Containerで使えば更に快適になります。上で述べたように、VSCodeであれば使うファイルの配置もグラフィカルに表示されるので、例えば、分析対象のデータを配置するときもわかりやすいです。ファイルの中身もテキストエディタで編集できます。ポートフォワーディングも後からできます。docker-compose.ymlも使っているので毎回コマンドを打つ必要がないです。何よりもVSCodeの強力なデバック機能や拡張機能が使えるのは魅力的です。なんならipynbファイルもそのまま開けます。 私がVSCode+Remote Containerで使う場合のリポジトリ、SolKul/vscode-jupyter-container2のファイル構造と各ファイルは次のようになります。 /  ├ .devcontainer/ │ ├ .env │ ├ devcontainer.json │ ├ docker-compose.yml │ └ Dockerfile  └ work/ ※1 docker-compose.yml version: '3.3' services: jupyter-minimal: ※2 build: . #ビルド対象のDockerfileが同じフォルダ内にあるためピリオド(.)を打つ environment: # - JUPYTER_ENABLE_LAB=yes - GRANT_SUDO=yes working_dir: /home/jovyan/work user: root volumes: # ホストとのボリューム共有。../workは上のフォルダ構造で示した※1の一つ上の階層のworkフォルダを指し示す。 - ../work:/home/jovyan/work ポートフォワーディング下のようにVSCode上で後からできるのdocker-compose.ymlにはポートフォワーディングの設定は書いてません。 devcontainer.json { "name": "jupyter-devcontainer-project", "dockerComposeFile": [ "./docker-compose.yml" ], "service": "jupyter-minimal",//ここのサービス名は上のdocker-compose.ymlで示した※2のサービス名と一致させる "extensions": [ "ms-python.python" ], "workspaceFolder": "/home/jovyan/work" } Dockerfile FROM jupyter/minimal-notebook # RUN echo "c.NotebookApp.password='sha1:... としたほうがセキュリティ的に安全 RUN echo "c.NotebookApp.token=''" >> ~/.jupyter/jupyter_notebook_config.py RUN conda install -y jupyter_contrib_nbextensions VSCode+Remote Containerの詳しい使い方については「VSCode Remote Container」と検索するか、公式リファレンス(英語)を参考にしてください bashを起動したい場合 新しいモジュールをインストールしたり、ファイル操作やapt-getなどbashを操作したい場合があると思います。その場合はdocker psでコンテナIDを調べ、 docker exec -it コンテナID /bin/bash などすれば、bashを起動できます。またはVScodeでRemote containerしている場合は新しくターミナルを開けばいいです。 docker psやdocker execなどのコマンドがわからない場合は Docker ハンズオン - 基本コマンド編 などを参照してください。 ただ、デフォルトで使っている場合、ユーザーはjovyanになり、権限がないのでapt-get updateやapt-get installはできないです。これについては後述します。 デフォルトコマンドについて jupyter/datascience-notebookなど Jupyter Docker StacksのDockerコンテナは立ち上がると同時にデフォルトでstart-notebook.shが走り、各種設定を行います。(GRAT_SUDOの有効化など)。start-notebook.shが必要な設定を行うのでデフォルトコマンドは変更しないほうがいいです。 Jupyter Labについて 徐々に推奨エディタがJupyter Labに移り変わっていると思われます。 loacalhost:8888/treeにアクセスすると、従来のjupyter notebookに、loacalhost:8888/labにアクセスすると、jupyter labにアクセスします。ただ、環境変数JUPYTER_ENABLE_LABにyesを指定、つまり docker-compose.yml environment: - JUPYTER_ENABLE_LAB=yes とすると、localhost:8888にアクセスした時点でjupyter labにアクセスするするようになります。 nbextensionsについて 様々な拡張機能があるjupyter_contrib_nbextensionsはjupyter notebookでしか使えません。私は選択中の文字列について他の場所の同じ文字列も強調するHighlit Selected Wordを使いたいため、jupyter notebook+nbextensionsを使い続けています。いまのところ同じ機能はjupyter labは対応していません。 しかも、先程のように環境変数JUPYTER_ENABLE_LABにyesを指定すると、jupyter notebookがjupyter lab管理下のものになってしまい、nbextensionsは使えなくなります。nbextensionsを使いたい場合は環境変数JUPYTER_ENABLE_LABはいじらないようにしてください。 ユーザーと権限について ユーザーは管理者であるrootと、jovyanがいます。jupyter notebookを管理者権限で実行するのはセキュリティ的にまずいので、jupyter notebookはjovyanで起動するようになっています。 デフォルトではユーザーはjovyanになります。なのでデフォルトの状態で docker exec -it コンテナID /bin/bash とbashを実行しても、ユーザーはjovyanとなり、権限がないのでapt-get updateやapt-get installはできません。 しかしDockerfile中で USER root もしくは、docker-compose.ymlで docker-compose.yml environment: - JUPYTER_ENABLE_LAB=yes とユーザーをrootに切り替えた場合、bashはrootで起動し、apt-getなどが使えるようになります。 この状態で、su jovyanでjovyanに切り替えることができますが、このjovyanでは/opt/conda/bin/のパスが通っていないため、pipコマンドやcondaコマンドが使えまえん。export PATH=$PATH:/opt/conda/binなどとして、環境変数のPATHに/opt/conda/bin/を加えれば、pipコマンドやcondaコマンドが使えるようになります。 またユーザーをrootに切り替え、bashはrootで起動するようにした上で、環境変数GRANT_SUDOにyesを指定、つまりdocker-compose.ymlで、 docker-compose.yml - GRANT_SUDO=yes としていれば、su jovyanとしjovyanに切り替えた後のjovyanはsudoが使えるようになり、sudo apt-getなどができます。 つまりGRANT_SUDO=yes かつUSER rootならjovyanはsudoを使えるということです。詳しくは、 を参照してください。 モジュールのインストールとユーザー権限について 使っていて、conda installやpip installはrootとjovyanどちらのユーザーで実行しても、Pythonのプログラミングには問題ないように感じます。 しかし唯一jupyter_contrib_nbextensionsのインストールはjovyanで行ったほうがいいです。 rootで root $ conda install -y jupyter_contrib_nbextensions としてしまうと、nbextensions関連のファイルがroot権限になってしまいます。jupyter上にnbextensionsのタブが現れはしますが、jupyter起動ユーザーがjovyanなため、その状態でどのエクステンションを有効にしても実際は権限の問題で裏の設定ファイルが書き換わらずに、F5で更新すると無効になってしまいます。 なので、jupyter_contrib_nbextensionsのインストールはDockerfile内jovyanユーザーで予めやっておいたほうがいいです。 RUN conda install -y jupyter_contrib_nbextensions 詳しくは jupyter notebookが使えるdockerにnbextensionsが導入できない を参照してくださいい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jupyter/datascience-notebookなどのJupyterのDockerのTipsまとめ

jupyter/datascience-notebookなど、JupyterをDockerで使える(Jupyter Docker Stacks)についてまとめてみます。 このdockerイメージは、例えば普段使っているPCはWindowsやMacだけど、Linuxでしか動かないモジュールを使いたい人や、データ分析のタスクで、本番環境と開発環境を同じにしつつJupyterで開発したい人などはよく使っていると思います。ただこのdockerイメージは少し癖が強くデフォルトの状態で起動するとapt-getすらできないとか使い方がよくわからない部分があると思います。そこでこの記事で個人的にTipsをまとめてみました。 また私が使っているDocker + VSCode + Remote Containerのリポジトリもアップします。 SolKul/vscode-jupyter-container2 既にJupyteのDocker(Jupyter Docker Stacks)についての記事やサイトは山程出ています。まずそれらについてまとめ、そこにかかれていない私が役に立つと思うポイントをその後にまとめようと思います。 JupyterのDockerについての過去の記事や参考サイト Dockerで基本的なData Science環境(Jupyter, Python, R, Julia, 定番ライブラリ)を構築する。 この記事でパスワードの設定や、ポートフォワーディング、ボリュームの共有など基本的なことはすべて抑えられると思います。ただ、この記事はコンテナの起動をコマンドラインで行っています。dockerの知識がないうちはこれでdockerに慣れるのもいいですが、毎回この長いコマンドを打つのも大変です。慣れてきたらDockerfile+docker-composeを使ったほうが効率的です。 Docker + VSCode + Remote Containerで作る快適Jupyter Lab(Python)分析環境 この記事はVS CodeのRemote Containerでコンテナを動かしています。VS Codeであればファイルの配置もグラフィカルに表示され楽ですし、ファイルの中身もテキストエディタで編集できます。docker-composeも使っているので毎回コマンドを打つ必要がないです。何よりもVS Codeの強力なデバック機能や拡張機能が使えるのは魅力的です。 Jupyter Docker Stacksについてメモ 以下、Jupyter Docker Stacksを使う際のTipsをまとめます。 docker runよりdocker-composeを使う dockerに慣れてきたらdocker-composeを使うほうが断然楽です。次のdocker runコマンドと、その下のdocker-compose.ymlは同じ働きをします。 docker run \ --user root \ -e GRANT_SUDO=yes \ -e TZ=Asia/Tokyo \ -p 8888:8888 \ --name notebook \ -v ./work:/home/jovyan/work \ start-notebook.sh \ --NotebookApp.password='sha1:YOUR_PASSWORD_HASH_VALUE' docker-compose.yml version: '3' services: notebook: image: jupyter/datascience-notebook user: root ports: - '8888:8888' environment: - GRANT_SUDO=yes - TZ=Asia/Tokyo volumes: - ./work:/home/jovyan/work command: NotebookApp.password='sha1:YOUR_PASSWORD_HASH_VALUE' コマンドだと間違ってEnterを押してしまった時点で起動してしまいますが、docker-compose.ymlであれば落ち着いて編集し、最後に立ち上げる場合はdocker-compose upとすればいいだけです。またdocker-composeはVS CodeのRemote Containerでも使えます。 docker runのどのオプションがdocker-compose.ymlのどの項目に対応しているかなどは 複数のDockerコンテナを自動で立ち上げる構成管理ツール「Docker Compose」(Dockerの最新機能を使ってみよう:第7回) などを参照してください。 また、.envファイルを追加し、docker-compose.ymlを適切に書き換えればプロキシ下などでも使えます。詳しくはdocker-composeでのプロキシ設定を一つのファイルにまとめるを参考にしてください。 VSCode+Remote Containerで使う さらに言えば、VSCode+Remote Containerで使えば更に快適になります。上で述べたように、VSCodeであれば使うファイルの配置もグラフィカルに表示されるので、例えば、分析対象のデータを配置するときもわかりやすいです。ファイルの中身もテキストエディタで編集できます。ポートフォワーディングも後からできます。docker-compose.ymlも使っているので毎回コマンドを打つ必要がないです。何よりもVSCodeの強力なデバック機能や拡張機能が使えるのは魅力的です。なんならipynbファイルもそのまま開けます。 私がVSCode+Remote Containerで使う場合のリポジトリ、SolKul/vscode-jupyter-container2のファイル構造と各ファイルは次のようになります。 /  ├ .devcontainer/ │ ├ .env │ ├ devcontainer.json │ ├ docker-compose.yml │ └ Dockerfile  └ work/ ※1 docker-compose.yml version: '3.3' services: jupyter-minimal: ※2 build: . #ビルド対象のDockerfileが同じフォルダ内にあるためピリオド(.)を打つ environment: # - JUPYTER_ENABLE_LAB=yes - GRANT_SUDO=yes working_dir: /home/jovyan/work user: root volumes: # ホストとのボリューム共有。../workは上のフォルダ構造で示した※1の一つ上の階層のworkフォルダを指し示す。 - ../work:/home/jovyan/work ポートフォワーディング下のようにVSCode上で後からできるのdocker-compose.ymlにはポートフォワーディングの設定は書いてません。 devcontainer.json { "name": "jupyter-devcontainer-project", "dockerComposeFile": [ "./docker-compose.yml" ], "service": "jupyter-minimal",//ここのサービス名は上のdocker-compose.ymlで示した※2のサービス名と一致させる "extensions": [ "ms-python.python" ], "workspaceFolder": "/home/jovyan/work" } Dockerfile FROM jupyter/minimal-notebook # RUN echo "c.NotebookApp.password='sha1:... としたほうがセキュリティ的に安全 RUN echo "c.NotebookApp.token=''" >> ~/.jupyter/jupyter_notebook_config.py RUN conda install -y jupyter_contrib_nbextensions VSCode+Remote Containerの詳しい使い方については「VSCode Remote Container」と検索するか、公式リファレンス(英語)を参考にしてください bashを起動したい場合 新しいモジュールをインストールしたり、ファイル操作やapt-getなどbashを操作したい場合があると思います。その場合はdocker psでコンテナIDを調べ、 docker exec -it コンテナID /bin/bash などすれば、bashを起動できます。またはVScodeでRemote containerしている場合は新しくターミナルを開けばいいです。 docker psやdocker execなどのコマンドがわからない場合は Docker ハンズオン - 基本コマンド編 などを参照してください。 ただ、デフォルトで使っている場合、ユーザーはjovyanになり、権限がないのでapt-get updateやapt-get installはできないです。これについては後述します。 デフォルトコマンドについて jupyter/datascience-notebookなど Jupyter Docker StacksのDockerコンテナは立ち上がると同時にデフォルトでstart-notebook.shが走り、各種設定を行います。(GRAT_SUDOの有効化など)。start-notebook.shが必要な設定を行うのでデフォルトコマンドは変更しないほうがいいです。 Jupyter Labについて 徐々に推奨エディタがJupyter Labに移り変わっていると思われます。 loacalhost:8888/treeにアクセスすると、従来のjupyter notebookに、loacalhost:8888/labにアクセスすると、jupyter labにアクセスします。ただ、環境変数JUPYTER_ENABLE_LABにyesを指定、つまり docker-compose.yml environment: - JUPYTER_ENABLE_LAB=yes とすると、localhost:8888にアクセスした時点でjupyter labにアクセスするするようになります。 nbextensionsについて 様々な拡張機能があるjupyter_contrib_nbextensionsはjupyter notebookでしか使えません。私は選択中の文字列について他の場所の同じ文字列も強調するHighlit Selected Wordを使いたいため、jupyter notebook+nbextensionsを使い続けています。いまのところ同じ機能はjupyter labは対応していません。 しかも、先程のように環境変数JUPYTER_ENABLE_LABにyesを指定すると、jupyter notebookがjupyter lab管理下のものになってしまい、nbextensionsは使えなくなります。nbextensionsを使いたい場合は環境変数JUPYTER_ENABLE_LABはいじらないようにしてください。 ユーザーと権限について ユーザーは管理者であるrootと、jovyanがいます。jupyter notebookを管理者権限で実行するのはセキュリティ的にまずいので、jupyter notebookはjovyanで起動するようになっています。 デフォルトではユーザーはjovyanになります。なのでデフォルトの状態で docker exec -it コンテナID /bin/bash とbashを実行しても、ユーザーはjovyanとなり、権限がないのでapt-get updateやapt-get installはできません。 しかしDockerfile中で USER root もしくは、docker-compose.ymlで docker-compose.yml environment: - JUPYTER_ENABLE_LAB=yes とユーザーをrootに切り替えた場合、bashはrootで起動し、apt-getなどが使えるようになります。 この状態で、su jovyanでjovyanに切り替えることができますが、このjovyanでは/opt/conda/bin/のパスが通っていないため、pipコマンドやcondaコマンドが使えまえん。export PATH=$PATH:/opt/conda/binなどとして、環境変数のPATHに/opt/conda/bin/を加えれば、pipコマンドやcondaコマンドが使えるようになります。 またユーザーをrootに切り替え、bashはrootで起動するようにした上で、環境変数GRANT_SUDOにyesを指定、つまりdocker-compose.ymlで、 docker-compose.yml - GRANT_SUDO=yes としていれば、su jovyanとしjovyanに切り替えた後のjovyanはsudoが使えるようになり、sudo apt-getなどができます。 つまりGRANT_SUDO=yes かつUSER rootならjovyanはsudoを使えるということです。詳しくは、 を参照してください。 モジュールのインストールとユーザー権限について 使っていて、conda installやpip installはrootとjovyanどちらのユーザーで実行しても、Pythonのプログラミングには問題ないように感じます。 しかし唯一jupyter_contrib_nbextensionsのインストールはjovyanで行ったほうがいいです。 rootで root $ conda install -y jupyter_contrib_nbextensions としてしまうと、nbextensions関連のファイルがroot権限になってしまいます。jupyter上にnbextensionsのタブが現れはしますが、jupyter起動ユーザーがjovyanなため、その状態でどのエクステンションを有効にしても実際は権限の問題で裏の設定ファイルが書き換わらずに、F5で更新すると無効になってしまいます。 なので、jupyter_contrib_nbextensionsのインストールはDockerfile内jovyanユーザーで予めやっておいたほうがいいです。 RUN conda install -y jupyter_contrib_nbextensions 詳しくは jupyter notebookが使えるdockerにnbextensionsが導入できない を参照してくださいい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jupyter/datascience-notebookなどのJupyterのDockerのTips

jupyter/scipy-notebookや、jupyter/datascience-notebookなど、JupyterをDockerで使える(Jupyter Docker Stacks)についてまとめてみます。 このdockerイメージは、例えば普段使っているPCはWindowsやMacだけど、Linuxでしか動かないモジュールを使いたい人や、データ分析のタスクで、本番環境と開発環境を同じにしつつJupyterで開発したい人などはよく使っていると思います。ただこのdockerイメージは少し癖が強くデフォルトの状態で起動するとapt-getすらできないとか使い方がよくわからない部分があると思います。そこでこの記事で個人的にTipsをまとめてみました。 また私が使っているDocker + VSCode + Remote Containerのリポジトリもアップします。 SolKul/vscode-jupyter-container2 既にJupyteのDocker(Jupyter Docker Stacks)についての記事やサイトは山程出ています。まずそれらについてまとめ、そこにかかれていない私が役に立つと思うポイントをその後にまとめようと思います。 JupyterのDockerについての過去の記事や参考サイト Dockerで基本的なData Science環境(Jupyter, Python, R, Julia, 定番ライブラリ)を構築する。 この記事でパスワードの設定や、ポートフォワーディング、ボリュームの共有など基本的なことはすべて抑えられると思います。ただ、この記事はコンテナの起動をコマンドラインで行っています。dockerの知識がないうちはこれでdockerに慣れるのもいいですが、毎回この長いコマンドを打つのも大変です。慣れてきたらDockerfile+docker-composeを使ったほうが効率的です。 Docker + VSCode + Remote Containerで作る快適Jupyter Lab(Python)分析環境 この記事はVS CodeのRemote Containerでコンテナを動かしています。VS Codeであればファイルの配置もグラフィカルに表示され楽ですし、ファイルの中身もテキストエディタで編集できます。docker-composeも使っているので毎回コマンドを打つ必要がないです。何よりもVS Codeの強力なデバック機能や拡張機能が使えるのは魅力的です。 Jupyter Docker Stacksについてメモ 以下、Jupyter Docker Stacksを使う際のTipsをまとめます。 docker runよりdocker-composeを使う dockerに慣れてきたらdocker-composeを使うほうが断然楽です。次のdocker runコマンドと、その下のdocker-compose.ymlは同じ働きをします。 docker run \ --user root \ -e GRANT_SUDO=yes \ -e TZ=Asia/Tokyo \ -p 8888:8888 \ --name notebook \ -v ./work:/home/jovyan/work \ start-notebook.sh \ --NotebookApp.password='sha1:YOUR_PASSWORD_HASH_VALUE' docker-compose.yml version: '3' services: notebook: image: jupyter/datascience-notebook user: root ports: - '8888:8888' environment: - GRANT_SUDO=yes - TZ=Asia/Tokyo volumes: - ./work:/home/jovyan/work command: start-notebook.sh --NotebookApp.password='sha1:YOUR_PASSWORD_HASH_VALUE' コマンドだと間違ってEnterを押してしまった時点で起動してしまいますが、docker-compose.ymlであれば落ち着いて編集し、最後に立ち上げる場合はdocker-compose upとすればいいだけです。またdocker-composeはVS CodeのRemote Containerでも使えます。 docker runのどのオプションがdocker-compose.ymlのどの項目に対応しているかなどは 複数のDockerコンテナを自動で立ち上げる構成管理ツール「Docker Compose」(Dockerの最新機能を使ってみよう:第7回) などを参照してください。 また、docker runコマンド-eオプションや、docker-compose.ymlのenvironmentでコンテナ中に環境変数を設定しています。 docker runの-eオプションについては公式リファレンスを参照してください。 run — Docker-docs-ja 19.03 ドキュメント docker-composeの環境変数についても公式リファレンスを参照するか、私が過去まとめた記事があるのでそちらを参照してください。 docker-composeのenv_fileと.envファイルの違い また、.envファイルを追加し、docker-compose.ymlを適切に書き換えればプロキシ下などでも使えます。詳しくはdocker-composeでのプロキシ設定を一つのファイルにまとめるを参考にしてください。 VSCode+Remote Containerで使う さらに言えば、VSCode+Remote Containerで使えば更に快適になります。上で述べたように、VSCodeであれば使うファイルの配置もグラフィカルに表示されるので、例えば、分析対象のデータを配置するときもわかりやすいです。ファイルの中身もテキストエディタで編集できます。ポートフォワーディングも後からできます。docker-compose.ymlも使っているので毎回コマンドを打つ必要がないです。何よりもVSCodeの強力なデバック機能や拡張機能が使えるのは魅力的です。なんならipynbファイルもそのまま開けます。 私がVSCode+Remote Containerで使う場合のリポジトリ、SolKul/vscode-jupyter-container2のファイル構造と各ファイルは次のようになります。 /  ├ .devcontainer/ │ ├ .env │ ├ devcontainer.json │ ├ docker-compose.yml │ └ Dockerfile  └ work/ ※1 docker-compose.yml version: '3.3' services: jupyter-minimal: ※2 build: . #ビルド対象のDockerfileが同じフォルダ内にあるためピリオド(.)を打つ environment: # - JUPYTER_ENABLE_LAB=yes - GRANT_SUDO=yes working_dir: /home/jovyan/work user: root volumes: # ホストとのボリューム共有。../workは上のフォルダ構造で示した※1の一つ上の階層のworkフォルダを指し示す。 - ../work:/home/jovyan/work ポートフォワーディング下のようにVSCode上で後からできるのdocker-compose.ymlにはポートフォワーディングの設定は書いてません。 devcontainer.json { "name": "jupyter-devcontainer-project", "dockerComposeFile": [ "./docker-compose.yml" ], "service": "jupyter-minimal",//ここのサービス名は上のdocker-compose.ymlで示した※2のサービス名と一致させる "extensions": [ "ms-python.python" ], "workspaceFolder": "/home/jovyan/work" } Dockerfile FROM jupyter/minimal-notebook # RUN echo "c.NotebookApp.password='sha1:... としたほうがセキュリティ的に安全 RUN echo "c.NotebookApp.token=''" >> ~/.jupyter/jupyter_notebook_config.py RUN conda install -y jupyter_contrib_nbextensions VSCode+Remote Containerの詳しい使い方については「VSCode Remote Container」と検索するか、公式リファレンス(英語)を参考にしてください bashを起動したい場合 新しいモジュールをインストールしたり、ファイル操作やapt-getなどbashを操作したい場合があると思います。その場合はdocker psでコンテナIDを調べ、 docker exec -it コンテナID /bin/bash などすれば、bashを起動できます。またはVScodeでRemote containerしている場合は新しくターミナルを開けばいいです。 docker psやdocker execなどのコマンドがわからない場合は Docker ハンズオン - 基本コマンド編 などを参照してください。 ただ、デフォルトで使っている場合、ユーザーはjovyanになり、権限がないのでapt-get updateやapt-get installはできないです。これについては後述します。 デフォルトコマンドについて jupyter/datascience-notebookなど Jupyter Docker StacksのDockerコンテナは立ち上がると同時にデフォルトでstart-notebook.shが走り、各種設定を行います。(GRAT_SUDOの有効化など)。start-notebook.shが必要な設定を行うのでデフォルトコマンドは変更しないほうがいいです。 Jupyter Labについて 徐々に推奨エディタがJupyter Labに移り変わっていると思われます。 loacalhost:8888/treeにアクセスすると、従来のjupyter notebookに、loacalhost:8888/labにアクセスすると、jupyter labにアクセスします。ただ、環境変数JUPYTER_ENABLE_LABにyesを指定、つまり docker-compose.yml environment: - JUPYTER_ENABLE_LAB=yes とすると、localhost:8888にアクセスした時点でjupyter labにアクセスするするようになります。 nbextensionsについて 様々な拡張機能があるjupyter_contrib_nbextensionsはjupyter notebookでしか使えません。私は選択中の文字列について他の場所の同じ文字列も強調するHighlight Selected Wordを使いたいため、jupyter notebook+nbextensionsを使い続けています。いまのところ同じ機能はjupyter labは対応していません。 しかも、先程のように環境変数JUPYTER_ENABLE_LABにyesを指定すると、jupyter notebookがjupyter lab管理下のものになってしまい、nbextensionsは使えなくなります。nbextensionsを使いたい場合は環境変数JUPYTER_ENABLE_LABはいじらないようにしてください。 ユーザーと権限について ユーザーは管理者であるrootと、jupyter notebookを実行するjovyanがいます。jupyter notebookを管理者権限で実行するのはセキュリティ的にまずいので、jupyter notebookはjovyanで起動するようになっています。 デフォルトではユーザーはjovyanになります。なのでデフォルトの状態で docker exec -it コンテナID /bin/bash とbashを実行しても、ユーザーはjovyanとなり、権限がないのでapt-get updateやapt-get installはできません。 しかしDockerfile中で USER root もしくは、docker-compose.ymlで docker-compose.yml user: root とユーザーをrootに切り替えた場合、bashはrootで起動し、apt-getなどが使えるようになります。 この状態で、su jovyanでjovyanに切り替えることができますが、このjovyanでは/opt/conda/bin/のパスが通っていないため、pipコマンドやcondaコマンドが使えまえん。export PATH=$PATH:/opt/conda/binなどとして、環境変数のPATHに/opt/conda/bin/を加えれば、pipコマンドやcondaコマンドが使えるようになります。 またユーザーをrootに切り替え、bashはrootで起動するようにした上で、環境変数GRANT_SUDOにyesを指定、つまりdocker-compose.ymlで、 docker-compose.yml environment: - GRANT_SUDO=yes としていれば、su jovyanとしjovyanに切り替えた後のjovyanはsudoが使えるようになり、sudo apt-getなどができます。 つまりGRANT_SUDO=yes かつUSER rootならjovyanはsudoを使えるということです。詳しくは、 を参照してください。 モジュールのインストールとユーザー権限について rootでconda installなどでモジュールを管理者権限でインストールしてしまうと、jupyter notebookを実行するjovyanユーザーで不具合が起きるように思えますが、使っていて、conda installやpip installなどモジュールのインストールをrootとjovyanどちらのユーザーで実行しても、ほぼほぼ、Pythonのプログラミングには問題ないように感じます。 しかしjupyter_contrib_nbextensionsのインストールはjovyanで行ったほうがいいです。 rootで root $ conda install -y jupyter_contrib_nbextensions としてしまうと、nbextensions関連のファイルがroot権限になってしまいます。jupyter上にnbextensionsのタブが現れはしますが、jupyter起動ユーザーがjovyanなため、その状態でどのエクステンションを有効にしても実際は権限の問題で裏の設定ファイルが書き換わらずに、F5で更新すると無効になってしまいます。 なので、jupyter_contrib_nbextensionsのインストールはDockerfile内jovyanユーザーで予めやっておいたほうがいいです。 RUN conda install -y jupyter_contrib_nbextensions 詳しくは jupyter notebookが使えるdockerにnbextensionsが導入できない を参照してくださいい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Galleryのんびり見よう005(Broken Barh) (matplotlib)

概要 matplotlibのGalleryをのんびり見よう。 私個人に分かりやすいようにまとめたりしながら。 元ページ まとめページ 環境 Python 3.7 matplotlib 3.4.1 内容 準備 import matplotlib.pyplot as plt plot fig, ax = plt.subplots() ax.broken_barh([(110, 30), (150, 10)], (10, 9), facecolors='tab:blue') ax.broken_barh([(10, 50), (100, 20), (130, 10)], (20, 9), facecolors=('tab:orange', 'tab:green', 'tab:red')) ax.set_ylim(5, 35) ax.set_xlim(0, 200) ax.set_xlabel('seconds since start') ax.set_yticks([15, 25]) ax.set_yticklabels(['Bill', 'Jim']) ax.grid(True) ax.annotate('race interrupted', (61, 25), xytext=(0.8, 0.9), textcoords='axes fraction', arrowprops=dict(facecolor='black', shrink=0.05), fontsize=16, horizontalalignment='right', verticalalignment='top') fig.show() ax.broken_barh()については下記蛇足参照。 ax.annotate()についても下記蛇足参照。 'tab:blue'はTableau Colorsのこと。(参考) 蛇足 ax.broken_barh(xranges, yrange, *, data=None, **kwargs) xranges: (xmin, xwidth)`のtuple. yrange: (ymin, yheight). ax.annotate(text, xy, *args, **kwargs) Annotate the point xy with text. text: str テキスト xy: (float, float) アノテートする場所の(x, y) xytext: (float, float) テキストをおく場所の(x, y) 以下略. 参考にさせていただいた頁 https://matplotlib.org/stable/gallery/lines_bars_and_markers/broken_barh.html#sphx-glr-gallery-lines-bars-and-markers-broken-barh-py 感想 使うと良さそう。 今後 活用していきたい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Galleryのんびり見よう004(Horizontal bar) (matplotlib)

概要 matplotlibのGalleryをのんびり見よう。 私個人に分かりやすいようにまとめたりしながら。 元ページ まとめページ 環境 Python 3.7 matplotlib 3.4.1 内容 準備 import matplotlib.pyplot as plt import numpy as np # Fixing random state for reproducibility np.random.seed(19680801) # Example data people = ('Tom', 'Dick', 'Harry', 'Slim', 'Jim') y_pos = np.arange(len(people)) performance = 3 + 10 * np.random.rand(len(people)) error = np.random.rand(len(people)) plot fig, ax = plt.subplots() ax.barh(y_pos, performance, xerr=error, align='center') ax.set_yticks(y_pos) ax.set_yticklabels(people) ax.invert_yaxis() # labels read top-to-bottom ax.set_xlabel('Performance') ax.set_title('How fast do you want to go today?') fig.show() ax.barh()で、horizontal barを描画できる。 ax.invert_yaxis()で、y軸を逆転(上下を入れ換えて)いる。 参考にさせていただいた頁 https://matplotlib.org/stable/gallery/lines_bars_and_markers/barh.html#sphx-glr-gallery-lines-bars-and-markers-barh-py 感想 ax.invert_yaxis()は使ったことがなかった。 今後 活用していきたい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Galleryのんびり見よう003(Bar Label) (matplotlib)

概要 matplotlibのGalleryをのんびり見よう。 私個人に分かりやすいようにまとめたりしながら。 元ページ まとめページ 環境 Python 3.7 matplotlib 3.4.1 内容 準備 import matplotlib import matplotlib.pyplot as plt import numpy as np N = 5 menMeans = (20, 35, 30, 35, -27) womenMeans = (25, 32, 34, 20, -25) menStd = (2, 3, 4, 1, 2) womenStd = (3, 5, 2, 3, 3) ind = np.arange(N) # the x locations for the groups width = 0.35 # the width of the bars: can also be len(x) sequence plot fig, ax = plt.subplots() p1 = ax.bar(ind, menMeans, width, yerr=menStd, label='Men') p2 = ax.bar(ind, womenMeans, width, bottom=menMeans, yerr=womenStd, label='Women') ax.axhline(0, color='grey', linewidth=0.8) ax.set_ylabel('Scores') ax.set_title('Scores by group and gender') ax.set_xticks(ind) ax.set_xticklabels(('G1', 'G2', 'G3', 'G4', 'G5')) ax.legend() # Label with label_type 'center' instead of the default 'edge' ax.bar_label(p1, label_type='center') ax.bar_label(p2, label_type='center') ax.bar_label(p2) fig.show() bar_labelでラベルを表示。label_type='center'で棒グラフの真ん中に表示。 参考にさせていただいた頁 https://matplotlib.org/stable/gallery/lines_bars_and_markers/bar_label_demo.html#sphx-glr-gallery-lines-bars-and-markers-bar-label-demo-py 感想 ax.bar_label(p2, label_type='center')と ax.bar_label(p2)とで表示されるラベルも変わっているのはすごいなあと思った。 今後 活用していきたい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Galleryのんびり見よう002(Grouped bar) (matplotlib)

概要 matplotlibのGalleryをのんびり見よう。 私個人に分かりやすいようにまとめたりしながら。 元ページ まとめページ 環境 Python 3.7 matplotlib 3.4.1 内容 準備 import matplotlib import matplotlib.pyplot as plt import numpy as np labels = ['G1', 'G2', 'G3', 'G4', 'G5'] men_means = [20, 34, 30, 35, 27] women_means = [25, 32, 34, 20, 25] x = np.arange(len(labels)) # the label locations width = 0.35 # the width of the bars plot fig, ax = plt.subplots() rects1 = ax.bar(x - width/2, men_means, width, label='Men') rects2 = ax.bar(x + width/2, women_means, width, label='Women') # Add some text for labels, title and custom x-axis tick labels, etc. ax.set_ylabel('Scores') ax.set_title('Scores by group and gender') ax.set_xticks(x) ax.set_xticklabels(labels) ax.legend() ax.bar_label(rects1, padding=3) ax.bar_label(rects2, padding=3) fig.tight_layout() fig.show() ax.bar(x - width/2,およびax.bar(x + width/2,で並べて表示できる rects1 = ax.bar(のように、ax.bar()はBarContainerを返す。 ax.bar_label(rects1, padding=3)でラベルを表示させられる。 参考にさせていただいた頁 https://matplotlib.org/stable/gallery/lines_bars_and_markers/bar_stacked.html#sphx-glr-gallery-lines-bars-and-markers-bar-stacked-py 感想 グラフを並べるのに悩んでいたので、助かる。 グラフの上方にラベルを置く際の使い方がわかったので、よかった。 今後 活用していきたい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Galleryのんびり見よう index (matplotlib)

概要 matplotlibに関して、まとめたものを整列させておく。 内容 Galleryのんびり見よう Galleryのんびり見よう001(Stacked bar) (matplotlib) Galleryのんびり見よう002(Grouped bar) (matplotlib) Galleryのんびり見よう003(Bar Label) (matplotlib) Galleryのんびり見よう004(Horizontal bar) (matplotlib) Galleryのんびり見よう005(Broken Barh) (matplotlib)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Galleryのんびり見よう001(Stacked bar) (matplotlib)

概要 matplotlibのGalleryをのんびり見よう。 私個人に分かりやすいようにまとめたりしながら。 元ページ まとめページ 環境 Python 3.7 matplotlib 3.2.1 内容 準備 import matplotlib.pyplot as plt labels = ['G1', 'G2', 'G3', 'G4', 'G5'] men_means = [20, 35, 30, 35, 27] women_means = [25, 32, 34, 20, 25] men_std = [2, 3, 4, 1, 2] women_std = [3, 5, 2, 3, 3] width = 0.35 # the width of the bars: can also be len(x) sequence plot fig, ax = plt.subplots() ax.bar(labels, men_means, width, yerr=men_std, label='Men') ax.bar(labels, women_means, width, yerr=women_std, bottom=men_means, label='Women') ax.set_ylabel('Scores') ax.set_title('Scores by group and gender') ax.legend() fig.show() bottom=を指定することで、stacked bar chartができる yerr=を指定することで、error barがつけられる 参考にさせていただいた頁 https://matplotlib.org/stable/gallery/lines_bars_and_markers/bar_stacked.html#sphx-glr-gallery-lines-bars-and-markers-bar-stacked-py 感想 グラフを積み重ねるときに使える。 今後 活用していきたい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

python-pptxのマニュアルにはない何気にニッチなスライドの削除

python-pptxのマニュアルにはない何気にニッチなスライドの削除は以下でできるはずです。 〜スライド1枚目(ページ0)とスライド2枚目(ページ1)のスライド削除〜 ※prs等を定義 xml_slides = prs.slides._sldIdLst slides = list(xml_slides) xml_slides.remove(slides[0]) xml_slides.remove(slides[1]) ※prs.save(prsのパス) shiracamusさんありがとうございます?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

picoCTF Practice Writeup 3

picoCTF Practice Writeup 3 picoGym Practice Challenges page=3 の11問を勉強した記録 このページの難問は, 626 solves の New Caesar (Cryptographyというよりpython力を問う問題) 362 solves の ARMssembly 1 (ARM Assembly) 特に 126 solves の Cache Me Outside はお手上げ。なので11問だけ掲載。 Warmed Up Category: General Skills Description: What is 0x3D (base 16) in decimal (base 10)? Hints: 1. Submit your answer in our flag format. For example, if your answer was '22', you would submit 'picoCTF{22}' as the flag. Solution: 計算するだけ The Numbers Category: Cryptography Description: The numbers... what do they mean? Hints: 1. The flag is in the format PICOCTF{} Solution: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 1 2 3 4 5 6 7 8 9 1011121314151617181920212223242526 PICOCTF{THENUMBERSMASON} 2Warm Category: General Skills Description: Can you convert the number 42 (base 10) to binary (base 2)? Hints: Submit your answer in our competition's flag format. For example, if your answer was '11111', you would submit 'picoCTF{11111}' as the flag. Solution: 計算するだけ Wireshark doo dooo do doo... Category: Forensics Description: Can you find the flag? shark1.pcapng. Hints: (None) Solution: プロトコル階層 Line-based text data が目立つ ROT13ビンゴ speeds and feeds Category: Reverse Engineering Description: There is something on my shop network running at nc mercury.picoctf.net 59953, but I can't tell what it is. Can you? Hints: What language does a CNC machine use? Solution: 工作機械の言語なんて初めて見ました。 CNCにはGコードだけでなくMコード,Sコード,Tコード,Fコードとかもあるみたいです。 Shop Category: Reverse Engineering Description: Best Stuff - Cheap Stuff, Buy Buy Buy... Store Instance: source. The shop is open for business at nc mercury.picoctf.net 42159. Hints: 1. Always check edge cases when programming $ nc mercury.picoctf.net 42159 Welcome to the market! ===================== You have 40 coins Item Price Count (0) Quiet Quiches 10 12 (1) Average Apple 15 8 (2) Fruitful Flag 100 1 (3) Sell an Item (4) Exit Choose an option: Solution: Fruitful Flagは100円だが40円しか持っていない。 Quiet Quichesを-100個買うと,お金が1000円増える。 残高 = 残高 - ( 10 * -100 ) Fruitful Flagを買う $ nc mercury.picoctf.net 42159 Welcome to the market! ===================== You have 40 coins Item Price Count (0) Quiet Quiches 10 12 (1) Average Apple 15 8 (2) Fruitful Flag 100 1 (3) Sell an Item (4) Exit Choose an option: 0 How many do you want to buy? -100 You have 1040 coins Item Price Count (0) Quiet Quiches 10 112 (1) Average Apple 15 8 (2) Fruitful Flag 100 1 (3) Sell an Item (4) Exit Choose an option: 2 How many do you want to buy? 1 Flag is: [112 105 99 111 67 84 70 123 98 52 100 95 98 114 111 103 114 97 109 109 101 114 95 55 57 55 98 50 57 50 99 125] Scavenger Hunt Category: Web Exploitation Description: There is some interesting information hidden around this site http://mercury.picoctf.net:39491/. Can you find it? Hints: You should have enough hints to find the files, don't run a brute forcer. Solution: ソース CSS js ヒントだけ How can I keep Google from indexing my website? robots.txt 次のヒント I think this is an apache server... can you Access the next flag? apache独自といえば 次のヒント I love making websites on my Mac, I can Store a lot of information there. Macで編集した時に残るのは? 自動バックアップか .DocumentRevisions-V100 だめ .DS_Store ビンゴ MacroHard WeakEdge Category: Forensics Description: I've hidden a flag in this file. Can you find it? Forensics is fun.pptm Hints (None) Solution: pptm = PowerPoint マクロ有効プレゼンテーション 常とう手段,拡張子をzipに変える hiddenなる怪しいファイル発見 ビンゴ New Caesar 良問です。 picoCTF 2021 New Caesar Writeup ARMssembly 1 Category: Reverse Engineering Description: For what argument does this program print win with variables 79, 7 and 3? File: chall_1.S Flag format: picoCTF{XXXXXXXX} -> (hex, lowercase, no 0x, and 32 bits. ex. 5614267 would be picoCTF{0055aabb}) Hints: Shifts .arch armv8-a .file "chall_1.c" .text .align 2 .global func .type func, %function func: sub sp, sp, #32 str w0, [sp, 12] mov w0, 79 str w0, [sp, 16] mov w0, 7 str w0, [sp, 20] mov w0, 3 str w0, [sp, 24] ldr w0, [sp, 20] ldr w1, [sp, 16] lsl w0, w1, w0 str w0, [sp, 28] ldr w1, [sp, 28] ldr w0, [sp, 24] sdiv w0, w1, w0 str w0, [sp, 28] ldr w1, [sp, 28] ldr w0, [sp, 12] sub w0, w1, w0 str w0, [sp, 28] ldr w0, [sp, 28] add sp, sp, 32 ret .size func, .-func .section .rodata .align 3 .LC0: .string "You win!" .align 3 .LC1: .string "You Lose :(" .text .align 2 .global main .type main, %function main: stp x29, x30, [sp, -48]! add x29, sp, 0 str w0, [x29, 28] str x1, [x29, 16] ldr x0, [x29, 16] add x0, x0, 8 ldr x0, [x0] bl atoi str w0, [x29, 44] ldr w0, [x29, 44] bl func cmp w0, 0 bne .L4 adrp x0, .LC0 add x0, x0, :lo12:.LC0 bl puts b .L6 .L4: adrp x0, .LC1 add x0, x0, :lo12:.LC1 bl puts .L6: nop ldp x29, x30, [sp], 48 ret .size main, .-main .ident "GCC: (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0" .section .note.GNU-stack,"",@progbits Solution: まず,問題から 第1引数 79 第2引数 7 第3引数 3 の情報が与えられている。 lsl と sdiv が目にとまる 重要な部分だけ解説すると mov w0, 79 str w0, [sp, 16] # sp+16に 79 が保存される mov w0, 7 str w0, [sp, 20] # sp+20に 7 が保存される mov w0, 3 str w0, [sp, 24] # sp+24に 3 が保存される ldr w0, [sp, 20] ldr w1, [sp, 16] lsl w0, w1, w0 # wo(10112) = w1(79) << w0(7) str w0, [sp, 28] # sp+28に 10112 が保存される ldr w1, [sp, 28] ldr w0, [sp, 24] sdiv w0, w1, w0 # w0 = w1(10112) / w0(3) ( 79 << 7 ) / 3 = 3370 = 0xd2a picoCTF{00000d2a} Some Assembly Required 1 Category: Web Exploitation Description: http://mercury.picoctf.net:26318/index.html Hints: (None) Solution: ページのソース <html> <head> <meta charset="UTF-8"> <script src="G82XCw5CX3.js"></script> </head> <body> <h4>Enter flag:</h4> <input type="text" id="input"/> <button onclick="onButtonPress()">Submit</button> <p id="result"></p> </body> </html> javascryptを見てみる G82XCw5CX3.js const _0x402c = ['value', '2wfTpTR', 'instantiate', '275341bEPcme', 'innerHTML', '1195047NznhZg', '1qfevql', 'input', '1699808QuoWhA', 'Correct!', 'check_flag', 'Incorrect!', './JIFxzHyW8W', '23SMpAuA', '802698XOMSrr', 'charCodeAt', '474547vVoGDO', 'getElementById', 'instance', 'copy_char', '43591XxcWUl', '504454llVtzW', 'arrayBuffer', '2NIQmVj', 'result']; const _0x4e0e = function (_0x553839, _0x53c021) { _0x553839 = _0x553839 - 0x1d6; let _0x402c6f = _0x402c[_0x553839]; return _0x402c6f; }; (function (_0x76dd13, _0x3dfcae) { const _0x371ac6 = _0x4e0e; while (!![]) { try { const _0x478583 = -parseInt(_0x371ac6(0x1eb)) + parseInt(_0x371ac6(0x1ed)) + -parseInt(_0x371ac6(0x1db)) * -parseInt(_0x371ac6(0x1d9)) + -parseInt(_0x371ac6(0x1e2)) * -parseInt(_0x371ac6(0x1e3)) + -parseInt(_0x371ac6(0x1de)) * parseInt(_0x371ac6(0x1e0)) + parseInt(_0x371ac6(0x1d8)) * parseInt(_0x371ac6(0x1ea)) + -parseInt(_0x371ac6(0x1e5)); if (_0x478583 === _0x3dfcae) break; else _0x76dd13['push'](_0x76dd13['shift']()); } catch (_0x41d31a) { _0x76dd13['push'](_0x76dd13['shift']()); } } }(_0x402c, 0x994c3)); let exports; (async() => { const _0x48c3be = _0x4e0e; let _0x5f0229 = await fetch(_0x48c3be(0x1e9)), _0x1d99e9 = await WebAssembly[_0x48c3be(0x1df)](await _0x5f0229[_0x48c3be(0x1da)]()), _0x1f8628 = _0x1d99e9[_0x48c3be(0x1d6)]; exports = _0x1f8628['exports']; })(); function onButtonPress() { const _0xa80748 = _0x4e0e; let _0x3761f8 = document['getElementById'](_0xa80748(0x1e4))[_0xa80748(0x1dd)]; for (let _0x16c626 = 0x0; _0x16c626 < _0x3761f8['length']; _0x16c626++) { exports[_0xa80748(0x1d7)](_0x3761f8[_0xa80748(0x1ec)](_0x16c626), _0x16c626); } exports['copy_char'](0x0, _0x3761f8['length']), exports[_0xa80748(0x1e7)]() == 0x1 ? document[_0xa80748(0x1ee)](_0xa80748(0x1dc))[_0xa80748(0x1e1)] = _0xa80748(0x1e6) : document[_0xa80748(0x1ee)](_0xa80748(0x1dc))[_0xa80748(0x1e1)] = _0xa80748(0x1e8); } WebAssemblyって何だ? 「wasm ファイルを取得、ロード、実行するため先述した WebAssembly JavaScript API を呼び出すロジックも含んでいます。」とか書かれています。 デベロッパーツールで見ていると wasmを見つけることができます。 よくわからないけどフラグ見えた。(ESP?)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonでPLCソケット通信プログラムに終焉を

PLCとのソケット通信問題に終焉を 「PCからプログラムでPLCと通信したいけど、よくわかりません」というインターネット情報が多いので私が終止符を打ちたいと思います。 設定環境 私が試した環境はこちらになります。 python3.7.7  PLC型番:キーエンス kv5000  (キーエンスのPLC通信コマンドが利用可能なら型番が違っても可能。) 注意点 大量のデバイスで利用する場合は注意が必要です。 time.sleep(0.1)で処理を停止する必要があります。 コマンドの大量リクエスト要求でPLCが反応が鈍ります。 またPC側のCPU処理が少し上がります。 0.3秒停止ほどが色んな状況でも安定します。 ソースコード こちらをコピペしてください。 使い方は後ほど plc_connect.py import socket import binascii import time #10進数をバイナリーデータに変換させるクラス class TransDigit10ToVariousDigit: def __init__ (self,decimal_number,digit_format): self.decimal_number = decimal_number plane_bit = "bit" ascii_code = 'ascii_code' digit16 = '16decimal_bit' d10b16 = '10decimal_16bit' d10b32 = '10decimal_32bit' zero5 = "00000" self.trans_data ="" # 「00000」のasciiコードの場合は何もないことを示す。 # # 「0」ではなく何もない。None,null if self.decimal_number == zero5 and digit_format == ascii_code: self.trans_data = "" #アスキーコードに変換・洗浄 elif digit_format == ascii_code: self.trans_data = self.bit10_to_ascii_via_bit16() #16進数に変換・洗浄 elif digit_format == digit16: self.trans_data = self.digit10_to_binadigit16() #10進数に変換・洗浄 elif digit_format == d10b16 \ or digit_format == d10b32 \ or digit_format == plane_bit: self.trans_data = str(int(self.decimal_number)) #10進数から16進数に変換してstring型変換 def digit10_to_digit16(self): bit16_str = str(hex(int(self.decimal_number))) #余計なデータを取り除く noise_datas = [' ','0x','\t','\r\n'] to_nothing = "" for noise_data in noise_datas: bit16_str = (bit16_str.replace(noise_data,to_nothing)) return bit16_str #10進数から16進数に変換後、asciiコードに変換 def bit10_to_ascii_via_bit16(self): try: clean_bit16_str = self.digit10_to_digit16() ascii_data = binascii.unhexlify(clean_bit16_str.encode()) del_character = ['b',"'",r"\x00",r"\x000"] for del_chara in del_character: ascii_data = (str(ascii_data).replace(del_chara,'')) return ascii_data except: return "0" #10進数から16進数に変換後、16新数のバイナリデータに変換 def digit10_to_binadigit16(self): clean_bit16_str = self.digit10_to_digit16() if int(clean_bit16_str) < 100: clean_bit16_str = "00" + clean_bit16_str elif int(clean_bit16_str) < 1000: clean_bit16_str = "0" + clean_bit16_str return clean_bit16_str # デバイス名の値をすべて取り出す。 class RecievePLCPlaneData(object) : def __init__(self,ip,port): self.ip = ip self.port = int(port) # 問い合わせのコマンドを送る。自動でPLCが返送する。 def socket_send(self,command): s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect((self.ip,self.port)) s.send(command.encode('ascii')) recv_data = s.recv(1024).decode("ascii") return recv_data #値を取り出す def recv_from(self,device_list): try: device_list[1] = str(device_list[1]) command = "RDS {}.S {}\r".format(device_list[0],device_list[1]) recv_data = self.socket_send(command) return str(recv_data) except: return -1 #ビットで取り出したいときデバイス名MRなど def recv_as_bit_from(self,device_list): try: device_list[1] = str(device_list[1]) command = "RDS {} {}\r".format(device_list[0],device_list[1]) recv_data = self.socket_send(command) return str(recv_data) except: return -1 # 10進数32ビットで取り出したい場合 def recv_as_10digit32bit_from(self,device_list): try: device_list[1] = str(device_list[1]) command = "RDS {}.D {}\r".format(device_list[0],device_list[1]) recv_data = self.socket_send(command) recv_data = recv_data.replace(" ","") recv_data = int(recv_data) return str(recv_data) except: return -1 #フォーマットによりコマンド名が違うので場合分け def recv_10digi16bit_or_32bit(self,device_list): if device_list[2] == '10decimal_32bit': recv_data = self.recv_as_10digit32bit_from(device_list) elif device_list[2] == 'bit': recv_data = self.recv_as_bit_from(device_list) else: recv_data = self.recv_from(device_list) return recv_data # 取り出してきたデータを指定のフォーマットに変える class ReceiveTruePLCValue(RecievePLCPlaneData): """ 引数はdevice_data_list = [["デバイス名",ワード数,"format"]] リストのリストの構造で受け付ける。 formatは"bit",'ascii_code','16decimal_bit', '10decimal_16bit','10decimal_32bit' # 辞書{デバイス名:格納している値}を返す """, # 辞書型{デバイス名:格納している値}を返す def receive_plc_data(self,plc_list): if type(plc_list[0]) == list: device_data_lists = plc_list plc_datas = {device_data_list[0]:self.clean_recv_data(device_data_list) for device_data_list in device_data_lists} else: device_data_lists = plc_list plc_datas = self.clean_recv_data(device_data_lists) return plc_datas #データを取得してバイナリーデータに変換 def clean_recv_data(self,device_list): word_length = device_list[1] format_name = device_list[2] plan_bit = "bit" ascii_code = 'ascii_code' digit16 = '16decimal_bit' d10b16 = '10decimal_16bit' d10b32 = '10decimal_32bit' zero5 = "00000" # デバイス名とワード数 recv_data = self.recv_10digi16bit_or_32bit(device_list) if recv_data != -1 :#返信が確認できる場合 #データをリスト結合させる。 # str型「+00001 +00000」形式で取得するので余計なものを除去してリスト化 PLC_data_list = [recv_data[7*i:7*i+6].replace("+","") for i in range(int(word_length))] changed_data_list = [] for PLC_data in PLC_data_list: tr = TransDigit10ToVariousDigit(PLC_data,format_name) changed_data_list.append(tr.trans_data) value = "".join(changed_data_list)#リストを全て結合 return value else: return -1 #PLCに値を送信するクラス class SendMessageToPLC(object): def __init__ (self,facility_info_list): self.ip = facility_info_list[0] self.port = int(facility_info_list[1]) #デバイス1つのみ対応 def message_to(self,device_name,message): try: message = str(message) command_sentence = "WR {}.S {}\r".format(device_name,message) s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect((self.ip,self.port)) s.send(command_sentence.encode('ascii')) recv_data = s.recv(1024).decode("ascii") return recv_data except: return -1 # フォーマットを指定して値をもらう。 # 以下の文字列で指定できる。右からビット、アスキー,16進数,10進数16ビット,10進数32ビット # 'bit','ascii_code','16decimal_bit','10decimal_16bit','10decimal_32bit' def recv_plc(ip, port, device_name, data_length,format_name): recv = ReceiveTruePLCValue(ip, port) recv_data = recv.receive_plc_data([device_name, data_length, format_name]) return recv_data # ビットで値をもらう def recv_bit(ip, port, device_name, data_length): recv = ReceiveTruePLCValue(ip, port) recv_data = recv.receive_plc_data([device_name, data_length, 'bit']) return recv_data # asciiで値をもらう def recv_ascii(ip, port, device_name, data_length): recv = ReceiveTruePLCValue(ip, port) recv_data = recv.receive_plc_data([device_name, data_length, 'ascii_code']) return recv_data # 16進数で値をもらう def recv_16deci(ip, port, device_name, data_length): recv = ReceiveTruePLCValue(ip, port) recv_data = recv.receive_plc_data([device_name, data_length, '16decimal_bit']) return recv_data # 10進数16ビットで値をもらう def recv_10deci16bit(ip, port, device_name, data_length): recv = ReceiveTruePLCValue(ip, port) recv_data = recv.receive_plc_data([device_name, data_length, '10decimal_16bit']) return recv_data # 10進数32ビットで値をもらう def recv_10deci32bit(ip, port, device_name, data_length): recv = ReceiveTruePLCValue(ip, port) recv_data = recv.receive_plc_data([device_name, data_length, '10decimal_32bit']) return recv_data # PLCに値を送る。フォーマットの指定はしなくてよい。 def send_to_plc(ip, port, device_name,message): smp = SendMessageToPLC([ip, port]) smp.message_to(device_name, message) if __name__ =='__main__': # テスト的に利用 device_names = ["EM1320","EM1360","EM1380","EM1400","EM1420","EM1440"] ip = '10.50.100.11' port = 8501 for device_name in device_names: time.sleep(0.1) ascii_data = recv_ascii(ip,port,device_name,1) deci16_data = recv_16deci(ip,port,device_name,2) print(ascii_data,deci16_data) 使い方 簡単に利用する場合はこちらです。 ipはPLCのIPアドレスを入力してください。string型です。 portはPLCの上位リンク通信欄のポート番号です。通常は8501です。int型です device_nameは"EM1234"などデバイス名です。string型です。 data_length はPLCのワード数です。int型です。 format_nameはPLC側で設定しているビット、アスキー,16進数,10進数16ビット,10進数32ビットなります。文字列は以下のようになっています。 'bit','ascii_code','16decimal_bit','10decimal_16bit','10decimal_32bit' 送信の関数はip,port,device_nameで利用可能です。 私の環境が値を数字しか利用していないのasciiコードでどうなるか確認していません。 usage.py #利用はこんな感じです。 import time device_names = ["EM1320","EM1360","EM1380"] ip = '10.50.100.11' port = 8501 for device_name in device_names: time.sleep(0.1) ascii_data = recv_ascii(ip,port,device_name,1) deci16_data = recv_16deci(ip,port,device_name,2) print(ascii_data,deci16_data) 後々はPyPIに登録してpipインストールできるようにしようと思います。 以上です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】pandasというライブラリを使ってCSVファイルを読み込み、折れ線グラフを作成する。

pythonを使用してExcelファイルの操作を勉強しています。 本日の気づき(復習)は、CSVの読み込みとグラフ作成に関しての続きです。 pythonでExcelを操作するため、openpyxlというパッケージを使用しています。 売上.csv 部門,1月,2月,3月 商品A,600,700,800 商品B,4100,3800,4500 商品C,2900,1800,3000 商品D,800,1000,1200 商品E,600,450,300 前回とほぼ同様、上記のようなCSVファイルを読み込み (グラフの見易さ向上の為、少し数値を変更しました。) この様な折れ線グラフを作り貼り付けたいです。 LineChartオブジェクト 折れ線グラフ作成にはLineChartオブジェクトを使用します。 グラフの作成方法自体は棒グラフと同じですが 今回も気を付けるポイントがあります。 月ごとの売上金額を、折れ線グラフにする 売上金額は各数値を1000倍する 正直、ネタが尽きてきたので、1000倍は無理矢理付け足しました。 余談:openpyxlで使えるグラフ BarChart:棒グラフ PieChart:円グラフ LineChart:折れ線グラフ AreaChart:面グラフ BarChart3D:3D棒グラフ RadarChart:レーダーチャート BubbleChartバブルチャート DoughnutChartドーナツチャート 目的に合ったグラフを選ぶようにしたいですね。 使える使えないは別として 個人的には、レーダーチャートが好みです。 グラフの参照データを、行/列で切り替え bar.add_data(Referenceオブジェクト, from_rows=True, titles_from_data=True) 今回も上記のように「from_rows=True」を記述して グラフの参照データを、行/列で切り替えます。 売上金額を1000倍する DataFrame[列名] *= 1000 pandasのDataFrameの機能を使い、列名を指定するとその列のデータを取得できるので 上記のように演算子を用いると計算も出来ちゃいます。 が、今回はすべての売上データ(1月~3月の売り上げが記載されている列)なので DataFrame[['1月', '2月', '3月']] *= 1000 と、記述してみました。 DataFrameの指定列にリストを入れ込んでみるイメージです。 すると、上手く動作してくれました。 でも、これあってるのかな??? if文で記述した方が良かったのでしょうか。 ちゃんと動いてるから、いいなかな??? 最終的なコード import pandas as pd from openpyxl import Workbook from openpyxl.chart import LineChart, Reference from openpyxl.utils.dataframe import dataframe_to_rows wb = Workbook() ws = wb.active # CSVファイル読込み df = pd.read_csv('売上.csv', encoding='utf-8') # 各売上数値を1000倍 df[['1月', '2月', '3月']] *= 1000 for row in dataframe_to_rows(df, index=None, header=True): # データを1行ずつ追加 ws.append(row) # 折れ線グラフを選択 line = LineChart() # 円グラフのスタイル設定 line.style = 10 # グラフのデータ範囲を設定 data = Reference(ws, min_col=1, min_row=2, max_col=ws.max_column, max_row=ws.max_row) # ラベルの範囲を設定 labels = Reference(ws, min_col=2, min_row=1, max_col=ws.max_column) # グラフにデータを追加(参照方法を行列で切り替え) line.add_data(data, from_rows=True, titles_from_data=True) # 折れ線グラフのラベルを追加 line.set_categories(labels) # 横軸タイトルを追加 line.x_axis.title = '月度' # 縦軸タイトルを追加 line.y_axis.title = '売上高' # グラフのタイトルを追加 line.title = '売上推移' ws.add_chart(line, 'A9') wb.save('部門別売上_折れ線グラフ.xlsx') 余談:line.styleの種類 pie.style同様、line.styleも 1~48までの整数値を指定することでスタイルを変更できます 前回同様一覧表を作ってみました。 import pandas as pd from openpyxl import Workbook from openpyxl.chart import LineChart, Reference from openpyxl.utils.dataframe import dataframe_to_rows wb = Workbook() ws = wb.active # CSVファイル読込み df = pd.read_csv('売上.csv', encoding='utf-8') # 各売上数値を1000倍 df[['1月', '2月', '3月']] *= 1000 for row in dataframe_to_rows(df, index=None, header=True): # データを1行ずつ追加 ws.append(row) for i in range(1, 49): line = LineChart() line.style = i data = Reference(ws, min_col=1, min_row=2, max_col=ws.max_column, max_row=ws.max_row) labels = Reference(ws, min_col=2, min_row=1, max_col=ws.max_column) line.add_data(data, from_rows=True, titles_from_data=True) line.set_categories(labels) line.x_axis.title = '月度' line.y_axis.title = '売上高' line.title = F'{i}' ws.add_chart(line, f'A{(i-1)*16+9}') wb.save('部門別売上_折れ線グラフ一覧.xlsx') ただ、円グラフと違って色分けが同色のグラデーションだと見づらい・・・。 ここ数日グラフの作成ばかり勉強していたのですが 「見せ方」って、大事なんだな~と改めて感じる事が出来ました。 自己満足じゃなく相手が見てどう思うかを頭に入れながら コードもデザインも作っていけるようになりたいですね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

BTSのメンバーを分類するアプリを作成

0.アプリを作成した経緯 自分は元々SEとして、プログラミングやテスト実施、セキュリティ脆弱診断などをしていました。これから先のキャリアを考えたときに自分の強みがないこと、本当にやりたいことを考えたとき、機械学習エンジニアやデータサイエンティストなどの仕事に興味を持ったのがきっかけで、Aidemyで基礎から勉強してみようと思いました。 成果物としてBTSのメンバーを識別するアプリケーションを作成しました。 1.画像収集 BTSのメンバーの顔写真を集めるため、googleとNaverからスクレイピングなどを行い、各メンバー200枚ほど画像を集めました。 2.画像加工 BTSのメンバーは髪色をよく変えているので、髪色によってモデルの精度が変わってしまうことを考え、集めた画像を白黒画像に変換しました。 change_grayscale.py import cv2 import numpy as np #画像を白黒に変換する for i in range(1,201): img = cv2.imread('member/V/V_' + str(i) + '.jpg') img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) cv2.imwrite('member_gray/V/V_' + str(i) + '.jpg', img_gray) さらに髪型(明るめ、暗め、前髪分けなど)に左右されないようにするため、顔だけ検出するコードも作成しました。 face_detection.py #google colaboratoryで実行 #!pip install face_recognition import os from pathlib import Path import subprocess import sys import cv2 import numpy as np import face_recognition input_path = '/content/drive/MyDrive/JIN' output_path = '/content/drive/MyDrive/JIN_change' os.makedirs(output_path, exist_ok=True) path_obj = Path(input_path) files_path = path_obj.glob('*') def get_face_location(img_path): img = cv2.imread(img_path) height = img.shape[0] width = img.shape[1] image = face_recognition.load_image_file(img_path) location = face_recognition.face_locations(image, model='cnn',number_of_times_to_upsample=2) if location == []: location = [(0,width,height,0)] top = location[0][0] right = location[0][1] bottom = location[0][2] left = location[0][3] return top, right, bottom, left file_path_posix = [file_path.as_posix() for file_path in files_path] for file_path in file_path_posix: file_name = file_path.split('/')[-1] top, right, bottom, left = get_face_location(file_path) img = cv2.imread(file_path) img_face = img[top:bottom, left:right] cv2.imwrite(f'{output_path}/{file_name}',img_face) 3. モデルの作成、学習 model_make.py ''' BTSのメンバーを分類するモデルを作成 ''' MEMBER = ["J-HOPE","JIMIN","JIN","JUNGKOOK","RM","SUGA","V"] #画像とラベルを保存 X = [] y = [] #メンバーの画像を読み込み def read_dir_img(member,label): for i in os.listdir("member_0409/" + member + "/"): X.append(image.img_to_array(image.load_img("member_0409/" + member+"/"+i,target_size=(150,150,1),grayscale=True))) y.append(label) for j in range(len(MEMBER)): read_dir_img(MEMBER[j],j) # データのロード X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.2,random_state=42) #list型をnumpy配列に変換 X_train = np.array(X_train) X_test = np.array(X_test) #正解ラベルをOnne-hot形式に変換 y_train = to_categorical(y_train) y_test = to_categorical(y_test) # モデルの定義 model = Sequential() model.add(Conv2D(filters=64, kernel_size=(3, 3),input_shape=(150,150,1))) model.add(Activation('relu')) model.add(Conv2D(filters=64, kernel_size=(3, 3))) model.add(Activation('relu')) model.add(Dropout(0.25)) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Activation('relu')) model.add(Conv2D(filters=32, kernel_size=(5, 5))) model.add(Activation('relu')) model.add(Dense(64)) model.add(Dropout(0.25)) model.add(Flatten()) model.add(Dense(32)) model.add(Dropout(0.25)) model.add(Dense(7,activation='softmax')) model.compile(loss='categorical_crossentropy', optimizer='adadelta', metrics=['accuracy']) history = model.fit(X_train, y_train, batch_size=128, epochs=50, verbose=1, validation_data=(X_test, y_test)) # 精度の評価 scores = model.evaluate(X_test, y_test, verbose=1) print('Test loss:', scores[0]) print('Test accuracy:', scores[1]) # 予測(テストデータの先頭の10枚) pred = np.argmax(model.predict(X_test[0:10]), axis=1) y_pred = model.predict_classes(X_test) 一番精度が良かったモデルを上記に記載しました。30パターン試した結果です。 0.45と低かったのですが、混同行列などを見るとまあまあメンバーの顔の識別ができているのかなと思われます。 model_make.py(混同行列) #混同行列表示 y_true = np.argmax(y_test,axis=1) cm = confusion_matrix(y_true,y_pred) cm = pd.DataFrame(data=cm,index=MEMBER,columns=MEMBER) sns.heatmap(cm,square=True,cbar=True,annot=True,cmap='Blues') plt.show() 4. 考察 各メンバーの髪型や表情、メイクなどが結構な頻度で変わってしまうためか、なかなか精度が上がらず、難しかったです。 1クラスあたりの画像数が少ないのが原因と思われます。 5. 最後に 8割~9割の精度を目指していたので、これからも精度を上げるために試行錯誤を繰り返していきたいと思います。 現在、画像数を増やしてモデルの作成中です。 herokuにアプリをデプロイしたので、ご興味があれば見ていただけると幸いです(精度が低いので当てることは難しいですが...)。 https://bts-judgment.herokuapp.com/ https://github.com/natsupon/BTS_Judgment 参考URL 参考にさせていただきました、ありがとうございます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CPythonのPyObjectの実装を簡潔に確認する。(Python 3.x)

概要 CPythonの基本となるPyObjectについて、簡潔に確認する。 内容 Python 3.x PyObject PyObject自体を使うことはないが、すべてのPython objectはPyObject*にcastできる。手作業での継承。 PyObject(cpython/Include/object.h) typedef struct _object { _PyObject_HEAD_EXTRA Py_ssize_t ob_refcnt; PyTypeObject *ob_type; } PyObject; _PyObject_HEAD_EXTRA: デバッグ版でなければ、空白に置き換えられる。 ob_refcnt: 参照カウント。 ob_type: PyTypeObjectへのポインタ。 PyTypeObject PyTypeObject(cpython/Include/object.h) typedef struct _typeobject PyTypeObject; _typeobject(cpython/Include/cpython/object.h) struct _typeobject { PyObject_VAR_HEAD const char *tp_name; /* For printing, in format "<module>.<name>" */ Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */ /* Methods to implement standard operations */ destructor tp_dealloc; Py_ssize_t tp_vectorcall_offset; getattrfunc tp_getattr; setattrfunc tp_setattr; PyAsyncMethods *tp_as_async; /* formerly known as tp_compare (Python 2) or tp_reserved (Python 3) */ reprfunc tp_repr; /* Method suites for standard classes */ PyNumberMethods *tp_as_number; PySequenceMethods *tp_as_sequence; PyMappingMethods *tp_as_mapping; /* More standard operations (here for binary compatibility) */ hashfunc tp_hash; ternaryfunc tp_call; reprfunc tp_str; getattrofunc tp_getattro; setattrofunc tp_setattro; /* Functions to access object as input/output buffer */ PyBufferProcs *tp_as_buffer; /* Flags to define presence of optional/expanded features */ unsigned long tp_flags; const char *tp_doc; /* Documentation string */ /* Assigned meaning in release 2.0 */ /* call function for all accessible objects */ traverseproc tp_traverse; /* delete references to contained objects */ inquiry tp_clear; /* Assigned meaning in release 2.1 */ /* rich comparisons */ richcmpfunc tp_richcompare; /* weak reference enabler */ Py_ssize_t tp_weaklistoffset; /* Iterators */ getiterfunc tp_iter; iternextfunc tp_iternext; /* Attribute descriptor and subclassing stuff */ struct PyMethodDef *tp_methods; struct PyMemberDef *tp_members; struct PyGetSetDef *tp_getset; struct _typeobject *tp_base; PyObject *tp_dict; descrgetfunc tp_descr_get; descrsetfunc tp_descr_set; Py_ssize_t tp_dictoffset; initproc tp_init; allocfunc tp_alloc; newfunc tp_new; freefunc tp_free; /* Low-level free-memory routine */ inquiry tp_is_gc; /* For PyObject_IS_GC */ PyObject *tp_bases; PyObject *tp_mro; /* method resolution order */ PyObject *tp_cache; PyObject *tp_subclasses; PyObject *tp_weaklist; destructor tp_del; /* Type attribute cache version tag. Added in version 2.6 */ unsigned int tp_version_tag; destructor tp_finalize; vectorcallfunc tp_vectorcall; }; ここに様々な振る舞いが描かれている。 PyObject_VAR_HEAD PyObject_VAR_HEAD(cpython/Include/object.h) #define PyObject_VAR_HEAD PyVarObject ob_base; ... typedef struct { PyObject ob_base; Py_ssize_t ob_size; /* Number of items in variable part */ } PyVarObject; 使用例(PyDictObject) PyDictObject(cpython/Include/cpython/dictobject.h) typedef struct { PyObject_HEAD /* Number of items in the dictionary */ Py_ssize_t ma_used; /* Dictionary version: globally unique, value change each time the dictionary is modified */ uint64_t ma_version_tag; PyDictKeysObject *ma_keys; /* If ma_values is NULL, the table is "combined": keys and values are stored in ma_keys. If ma_values is not NULL, the table is splitted: keys are stored in ma_keys and values are stored in ma_values */ PyObject **ma_values; } PyDictObject; PyObject_HEADは下記の通りであり、実際に継承している。 #define PyObject_HEAD PyObject ob_base; PyDict_Type(cpython/Objects/dictobject.h) PyTypeObject PyDict_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict", sizeof(PyDictObject), 0, (destructor)dict_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dict_repr, /* tp_repr */ &dict_as_number, /* tp_as_number */ &dict_as_sequence, /* tp_as_sequence */ &dict_as_mapping, /* tp_as_mapping */ PyObject_HashNotImplemented, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_DICT_SUBCLASS, /* tp_flags */ dictionary_doc, /* tp_doc */ dict_traverse, /* tp_traverse */ dict_tp_clear, /* tp_clear */ dict_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dict_iter, /* tp_iter */ 0, /* tp_iternext */ mapp_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ dict_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ dict_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ .tp_vectorcall = dict_vectorcall, }; 辞書生成は、下記の通り。 (cpython/Objects/dictobject.c) /* Consumes a reference to the keys object */ static PyObject * new_dict(PyDictKeysObject *keys, PyObject **values) { PyDictObject *mp; assert(keys != NULL); struct _Py_dict_state *state = get_dict_state(); ... if (state->numfree) { mp = state->free_list[--state->numfree]; assert (mp != NULL); assert (Py_IS_TYPE(mp, &PyDict_Type)); _Py_NewReference((PyObject *)mp); } else { mp = PyObject_GC_New(PyDictObject, &PyDict_Type); if (mp == NULL) { dictkeys_decref(keys); if (values != empty_values) { free_values(values); } return NULL; } } mp->ma_keys = keys; mp->ma_values = values; mp->ma_used = 0; mp->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(mp); return (PyObject *)mp; } 蛇足 (cpython/Include/object.h) #define _PyObject_CAST_CONST(op) ((const PyObject*)(op)) ... static inline Py_ssize_t _Py_REFCNT(const PyObject *ob) { return ob->ob_refcnt; } #define Py_REFCNT(ob) _Py_REFCNT(_PyObject_CAST_CONST(ob)) static inline Py_ssize_t _Py_SIZE(const PyVarObject *ob) { return ob->ob_size; } #define Py_SIZE(ob) _Py_SIZE(_PyVarObject_CAST_CONST(ob)) static inline PyTypeObject* _Py_TYPE(const PyObject *ob) { return ob->ob_type; } #define Py_TYPE(ob) _Py_TYPE(_PyObject_CAST_CONST(ob)) Py_REFCNT(ob):ob_refcntを得るマクロ。 Py_SIZE(ob):ob_sizeを得るマクロ。 Py_TYPE(ob):ob_typeを得るマクロ。 (const PyObject*)にキャストした後に得ている。 参考にさせて頂いた本・ページ https://github.com/python/cpython/tree/3.9 感想 落ち着いてまとめられたことがなかったので、よかった。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

開発していた予定定期報知アプリが完成したので、djangoとpythonanywhereで躓いた細々としたことのまとめ

失礼しました。ただいまエラーが発生しています。 ありがとうございます。きっとおそらく解決しました。 記事も追加されました。 エラーが起きた場合はお手数ですがこちらまでお知らせください。 開発していたアプリが完成したので、djangoで躓いた細々としたことをまとめていきます。詳しい理由はわからないことがほとんどなので、質問にはおそらく答えられないことが多いです。参考にしたurlをそれぞれつけておくのでそちらをご覧ください。 記念日やイベントなどの予定を事前に何度もメールで報せるwebアプリです。 予定表との違いは、普段は意識しないで埋もれてしまうような長期的な予定や周期的な予定に特にむいています。 たとえば記念日の50日前、30日前、15日前、5日前を毎年、のように設定できます。 個人的には果物や魚を旬の時期にあまり食べられなかったりすることがあるので、旬の終わりを予定日に、それから何日前を旬の始まりに設定して使うつもりです。 ほかにも、行ったことのない場所の定休日や営業日を毎週で登録し、前日に送るようにすると行くきっかけになります。 このような予定を何回でも繰り返し続けられます。 また、メールは同日の予定は一本にまとめて送ります。そして、デフォルトのurlを設定でき、天気予報などの予定を実行する際には頻繁に見るサイトなども登録できます。ほかにも、デフォルトのタイトルや詳細を登録することで、予定の入力時にページの文章から抽出することも可能です。 もしこういう使い方もしたいというご意見があればお待ちしています ただ、いまはサーバーなどの無料の範囲内で行っているため、容量やメール数に限りがあります。なので対応は人気が出てからになる場合があります。 例えば画像ファイルは大きいので現在では登録できません。かわりにdropboxなどの共有リンクか、その端末だけになりますが、画像ファイルのパスを登録すればブラウザから確認できるでしょう。 よろしくお願いします。 以下からdjangoで躓いた細々としたことのまとめです。 開発はローカルでやる人が多いでしょうが、そのときlocal_setting.pyを作るとデバッグに便利です。ただし、セキュリティ関連の設定はlocal_setting.pyで明確に打ち消さないと面倒なことが起こります。たとえばhttpsを強制する設定があるのですが、それをローカルで適用してしまうとアクセスできなくなり、キャッシュなどを消す必要が出てきます。 https://qiita.com/jp_ibis/items/0ac1ba8aba1398f2a492 もしくは以下のように完全に分けるのもいいでしょう。 https://qiita.com/okoppe8/items/e60d35f55188c0ab9ecc axesとかrulesも使う場合 login(request, user) axesとかrulesも使う場合 login(request, user,backend='django.contrib.auth.backends.ModelBackend') このときloginにはbackend='django.contrib.auth.backends.ModelBackend'を渡さないと、 multiple authentication backends configured and therefore must provide the backend argument or set the backend attribute on the user が発生します。AUTHENTICATION_BACKENDSに問題が無いときはこちらを見てください。 formsetにkwargsをわたす #古いやり方 modelformset_factory(FirmPartner, form=FirmPartnerForm, can_delete=True, extra=1, form_kwargs={'request': request, 'application': application}) #2020年 ConceptFormSet = modelformset_factory( model = Concept, form = ConceptForm, fields = show_fields ) #分けて書く formset = ConceptFormSet(form_kwargs={'action': 'detail'}) 書き方に新旧があるようです。現在は下の書き方を使います。 json_scriptフィルタ view def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context.update({ "update":"update" }) html {{ update |json_script:"update-data" }} document.getElementById('update-data').innerText=='"update"' true document.getElementById('update-data').innerText=="update" false viewからjson_scriptで値を渡すとき、上のようにしないと正誤判定ができませんでした。 真偽値は"true"でだけで大丈夫です。 raise ValidationError こういうのを追加するとき、object has no attribute 'object'が起こるときがあります。 もしそれがget_context_dataでなら、以下が必要です。 def post(self, request, *args, **kwargs): #ValidationErrorのため self.object = self.get_object() idやpkを伴うリンク <a class="box" href="{% url 'app:form_detail' form.id %}">detail</a> <a class="box" href="{% url 'app:form_detail' form.id.value %}">detail</a> <a class="box" href="{% url 'app:form_detail' 1 %}">idが1のdetail</a> 上のような感じで書いてあるページは多いのですが、value をつけないと、NoReverseMatch がおきる場合があります。一度idをadminでしらべて、上の三番目のように直接form.idの位置に書いても動きはするので、エラーの原因が分からなかったら試してみたり、アドレスバーに直接入力してみるのもいいかもしれません。 updateviewでのidやpk app_form = AppForm(request.POST) if app_form.is_valid():     form = app_form.save(commit=False)     form.pk = self.kwargs['pk'] こういう書き方をするときに最後のを忘れると新規作成になってしまう。 inputをtextareaに metaに追加する。keyは変更したいフィールド名。ただしレイアウト的にはcolsは指定しないか、widthを使ったほうがいいです。 from django.forms import ModelForm, Textarea class PostModelForm(ModelForm): class Meta: model = Post widgets = { 'content': Textarea(attrs={'cols': 80, 'rows': 20}), } datetime.datetimeとdatetime.date date=datetime.datetime.strptime("2000-02-02", '%Y-%m-%d') #これで作られるのはdatetime.datetime datetime.dateがほしいときは date=datetime.datetime.strptime("2000-02-02", '%Y-%m-%d').date() この二つは一見同じように見えるときがありますが別物なので、開発中日付がどうしても==にならないときはtyoe()などで確認してみてください。 cssでtransformとanimationを併用するとき @keyframes slideIn { 0%, 100% { transform: translate(10px) rotate(0deg); color: red; } 25% { transform: translate(125px) rotate(360deg); color: green; } } transformを併用するときはkeyframes にまとめて書きます。 pythonanywhereのScheduled Task from django.core.management.base import BaseCommand class Command(BaseCommand): def handle(self, *args, **options): command... どんなbashの命令でも実行できそうですが、djangoを利用する場合はpython manage.py Command_file_name にしたほうが無難です。そうでないと例えばDJANGO_SETTINGS_MODULEかsettings.configure()してほしいというエラーがでます。 https://qiita.com/checkpoint/items/b6947501774b4008e077 https://help.pythonanywhere.com/pages/environment-variables-for-web-apps/ https://stackoverflow.com/questions/7598793/setting-django-settings-module-under-virtualenv また、仮想環境を使用している場合は ファイルパスに /home/myusername/myproject/mytask.py こうではなく /home/myusername/.virtualenvs/myvenv/bin/python /home/myusername/myproject/mytask.py と入力する。 pythonanywhereのtaskでmodelを使う場合 modelにはapp名のlabelをつける必要があります。ないと doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS. class TopicsTr(models.Model): class Meta: abstract = True # specify this model as an Abstract Model app_label = 'blogs'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】スケールの異なる時系列データの類似度をMAEで比較する

形が似ているグラフ(時系列データ)を見つけたいときってありますよね. 今回は,スケールが違うけど形は似ている時系列データを見つけるために,スケールを正規化してMAE(平均絶対誤差)で比較する方法を書きます. 動機 普段はこちらの記事で株式投資について書いていますが,形が似ている銘柄をスクリーニングしたいと考えました(例えば,コロナショックで大きく変動した銘柄を抽出する). 形が似ている銘柄をスクリーニングするメリットとして, ・形が同じような銘柄は避け,分散投資に役立てる ・形が同じような銘柄を買い,ひとつの個別銘柄の不調を補う ・形が同じような銘柄の共通点を分析することで投資に役立てる などがあります. しかし,株価というものは形が似ていてもスケールがバラバラです. 例えば,以下のUAL(ユナイテッド・エアライン・ホールディングス)とDAL(デルタ航空)はコロナショックで大きく下落したので形は似ていますが,スケールが違うのでスクリーニングするときに何を基準に比較するか困ってしまいます. そこでこの記事では,スクリーニングの準備のために同じスケールに正規化して比較するコードを用意します. 株価データの準備 すでに比較したいデータがある場合は,この章は飛ばしてください. 今回は,Pythonのpandas-datareaderでUAL,DAL,AAPLの株価を取得します. ※今回の内容には関係ないですが,取得したらCSVなどに保存することをおすすめします.APIのサーバーの負担になるからです. import datetime import pandas_datareader.data as web start = datetime.date(2018,1,1) end = datetime.date.today() ual = web.DataReader('UAL', 'yahoo', start, end)['Adj Close'] dal = web.DataReader('DAL', 'yahoo', start, end)['Adj Close'] aapl = web.DataReader('AAPL', 'yahoo', start, end)['Adj Close'] ※調整後終値(Adj Close)のみを取得しています. これらの株価データはpandasのDataFrameで与えられます. この時点では,以下のデータようなデータになっています. 正規化する データの形だけを比較するために,スケールを0~1に正規化します. 正規化には,Min-Max Normalizationを使います. mmn(x) = \frac{x_i-\min(x)}{\max(x)-\min(x)} pandasのDataFrameは,以下で正規化できます. def mmn(ticker): mmn = (ticker - ticker.min()) / (ticker.max() - ticker.min()) return mms ual = mmn(ual) dal = mmn(dal) aapl = mmn(aapl) これで,以下のようにデータが0~1に正規化されていると思います. UALとDALの株価の形が似ていることが分かりやすくなりました. MAEで比較する 最後に,類似度を定量的に計算します. 類似度には平均絶対誤差(MAE)を使うことにします. MAE = \frac{1}{N}\sum_{i=1}^{N}|x_{1i} - x_{2i}| 今回の場合,MAEは小さいほど似ている形のデータ系列といくことになります.MAEが0だと完全に一致していることになります. MAEは以下で計算できます. from sklearn.metrics import mean_absolute_error MSE_dal_ual = mean_absolute_error(dal, ual) MSE_ual_aapl = mean_absolute_error(ual, aapl) MSE_aapl_dal = mean_absolute_error(aapl, dal) MSE_dal_ual = 0.08767186335370042 MSE_ual_aapl = 0.5756471586956278 MSE_aapl_dal = 0.5380791440469056 UALとDALが似ていることが確認できました. むすび 後日,これを使って「似た形の株価チャートをスクリーニング」したいと思います.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Aidemy Premium PlanでE資格対策講座を受講してみた。【python基礎〜教師なし学習まで】

はじめに E資格を取得する上でE資格認定プログラムを受講しなければいけないが、実際に受講した人の投稿が中々見つけられなかったので、今回の受講体験を載せることで誰かの参考になればという目的で記載します。 ちなみに今回は「Aidemy Premium Plan(E資格試験対策講座)」を受講したことをまとめます。 スペック 某電機メーカのエンジニア(HPC分野) 独身 都内大学出身、大学院修了(情報工学) C, C++, Perl(少々), Verilog-HDL, Python(少々)を嗜む ※ただし、pythonを0から記載できる自信無し 情報処理資格保持、G検定合格者 SIGNATEコンペに一度だけ参加経験あり(一応、intermediate) Aidemyを選んだ理由 pandasやmatplotlibに関して個人の理解が非常に乏しいので、ちゃんと学ぼうと思った 1講座申し込めば基礎から応用まで全部ついてくる 他の講座でもpythonの基礎などつけると金額的に変わらない 講義内容 今回は以下の講義内容の1〜10までに関しての個人的な感想を纏めます。 1. python入門 2. 機械学習概要 3. Pandas 4. Matplotlib 5. 基礎数学 6. 応用数学 7. Numpy 8. 教師あり学習(回帰) 9. 教師あり学習(分類) 10. 教師なし学習 11. サブノード(前半講義の復習的なもの) 12. ディープラーニング基礎 13. 順伝搬型ネットワーク 14. 生成モデル 15. 深層モデルのための最適化 16. 深層モデルのための正則化 17. CNNを使った画像認識 18. 畳み込みニューラルネットワーク 19. RNN 20. 強化学習 pythonの基礎的な部分についてのまとめ pythonの基礎的な部分に関しては、初心者にもわかりやすい内容でまとめられているという印象です。 ただし、Pandasに関しては、分析コンペで基本的に利用しそうな部分をピックアップして説明している印象なので、これだけでは内容として不十分な感じもします。 初心者の入り口としては問題ないですが、あくまでスタートラインとして問題ないというだけで、オライリーの「入門Python3」(オライリ社, 2015年)や「Python3スキルアップ教科書」(技術評論社, 2019年)で勉強することをお薦めします。 数学の部分についてのまとめ 教材の流れが不自然で文系の行列など全く分からない人が読んで、本当に分かるかは難しいという印象です。 特に行列に関しては、計算方法はわかると思いますが、個々の理論の繋がりは分からないと思います。 本当にこれでいいのかは、理系出身者としては腑に落ちないところです。 また、演習問題に関して言えば、行列の問題で教材だけでは解けない問題があります。 ある意味ひっかけな感じで、理系でも覚えていれば気づけるけど、文系は気づかず、教材通りといて、解答と異なると言った事態になると思います。 そのため、気持ち高校・大学の教養科目レベルでの線形代数の知識は多少必要だと思っています。 確率・統計分野に関しては、この本講座だけでも十分だと思いますが、できれば入門レベルでの確率統計の本は手元に持っておいた方がいいと思います。 教師あり学習、教師なし学習に関する部分のまとめ ここに関しては機械学習の入門となる部分でわかりやすく内容がまとめられています。 教師あり学習、教師なし学習共に、基礎的に利用するモデルとソースコードの解説があります。 また課題もコンペのように目標値を設定され、それを目指す形式であったりするため、個人的に面白かったですが、 簡単に目標値が達成できるため、もう少し難易度を上げてもいいと思いました。 「東京大学のデータサイエンティスト育成講座」(マイナビ出版, 2019)が非常に参考になります。 ここまでの総括 pythonプログラム周りでは、わかりやすい印象はあるものの、数学など理論に関する部分では教材として弱いと思います。 また全体として、E資格合格に注力した感じの内容ですので、現実世界で実践として役に立つかは難しいところです。 ですが、Aidemyは質問サポートが丁寧で、小さいことでも真面目に回答して頂けます。 また、大した課題でもないのに、添削結果を返す時のコメントで褒めてくれるため、悪い気はしません。 そのためこれらサポートを活用することで、文系の方にもカリキュラムを進めることができると思っています。 ※あくまで個人的の感想ですので、興味がある方は無料相談で相談することをお薦めします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python で "n 日前" と基準日から日付を得るメモ

背景 "5 days 12 hours ago" みたいな文字列と, 基準日から, 実際の日付を得たい. ぺろっと文字列渡してよろしく処理してくれるライブラリは無いようです. 方法 のように, 自前でパースして, dateutil の relativedelta で差分計算します. re はめんどいので, {} でパースできる parse を使います. from dateutil.relativedelta import relativedelta from datetime import datetime import parse def ago_do_date(ago, ref_date): p = parse.parse('{} days {} hrs ago', ago) print(p) delta = relativedelta(days=int(p[0]), hours=int(p[1])) print(ref_date - delta) print(datetime.now()) s = "16 days 16 hrs ago" ago_do_date(s, datetime.now()) 2021-04-14 18:29:29.754215 <Result ('16', '16') {}> 2021-03-29 02:29:29.754248 Voila!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Google ColaboratoryをVSCodeでssh接続して使えるようにした件について

はじめに 最近は技術記事がGoogle Colaboratoryにあわせてサンプルコードを書いてくれることが多く、Colabを使う機会が格段に増えました。そこでGPUが使用制限に達してしまい、Colab proを使おうかと考えていたのですが、それならVSCodeで使えるようにしたいなと思うようになったので調べていたのですが、そもそもSSHというものをなんとなくでしか知らない状態でやっていたので、時間がかかったため、初心者に向けて手順を省略せずに説明しようと思い、本日は紹介をします。 準備 ngrokのアカウントを作成します。 https://ngrok.com/ Google Colaboratoryを開きます。 そして以下のコードを1つずつセルにコピーして実行します。 import random, string, urllib.request, json, getpass # Generate root password password = ''.join(random.choice(string.ascii_letters + string.digits) for i in range(20)) # Download ngrok ! wget -q -c -nc https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip ! unzip -qq -n ngrok-stable-linux-amd64.zip # Setup sshd ! apt-get install -qq -o=Dpkg::Use-Pty=0 openssh-server pwgen > /dev/null ngrokのサイトにログインし、Getting Started->Your Authtokenを開き、Configuration Fileの項目にあるtokenをコピーします。 # これはコードでなくngrokの画面の説明です # in ngrok.yml authtoken: XXXXXXXXXXXXX(ここをコピー) 次に以下をColabで実行します。 #Run sshd get_ipython().system_raw('/usr/sbin/sshd -D &') # ngrokで自分のトークンを確認する authtoken = "あなたのトークンをここにいれる" #Create tunnel get_ipython().system_raw('./ngrok authtoken $authtoken && ./ngrok tcp 22 &') !pip install colab_ssh --upgrade from colab_ssh import launch_ssh# in a separate cell in case you need to restart the service ngrokToken = "1r395f88TULELKkSEYktSWGLWIT_4g8VkjVdeKHqZybzzWUhd" launch_ssh(ngrokToken, password="password") with urllib.request.urlopen('http://localhost:4040/api/tunnels') as response: data = json.loads(response.read().decode()) (host, port) = data['tunnels'][0]['public_url'][6:].split(':') print(f'SSH command: ssh -p{port} root@{host}') これでSSH接続のためのColabでの準備は終了です。 VSCodeでの設定 vscodeを開きます。 ctrl+pで検索ウィンドウを開きconfigと入力して .sshにあるconfigファイルを開きます。 一番下の行にColabから出力されたものを貼り付けます。 私の場合はこちらを貼り付けました。(人によって内容は異なります) Host google_colab_ssh HostName 6.tcp.ngrok.io User root Port 11121 SSH接続 左メニューから拡張機能を選択してRemote - SSHをインストールします。 左のメニューからリモートエクスプローラー(モニターに><のマークのアイコン)を選択して、上のVをクリックして(セレクトボックスから)SSH Targetsを選択します。 google_colab_sshを見つけ、Connect to Host in New Windowをクリック (フォルダーマークに+がかかれたアイコン)します。 新たなウインドウが表示されて、パスワードの入力を求められるのでpasswordと入力します。 (Colabで設定しました) 左メニューの拡張機能からPythonをインストールします。 左のメニューからフォルダーを開く(青ボタン)を選択して、上部に/root/とでるのでOKを押す (これでColabのrootディレクトリをカレントディレクトリにできます) ctrl+shift+pでコマンドパレットを開き、Jupyter: Create New Blank Notebookを検索し、選択することで新しいnotebookを作成できます。 試しにセルに以下を入力して、Colabが動くか確認します。 print("Hello World") ファイルは保存ボタンを押さないと新規作成されませんので、作成する際には最初に上部メニューの保存ボタンを押して任意のディレクトリに保存してください。 ターミナルから接続する ターミナルから接続する場合はColabで出力されるコマンドを入力します。 私の場合は以下のように出力されています SSH command: ssh -p11121 root@6.tcp.ngrok.io sshコマンドがなかったため、私はapt-get updateapt-get install sshを行いました。 コマンドを打った後、ssh接続の確認をとられるので、yesと入力してからパスワードを打ち込めば接続ができるかと思います。 終わりに SSH接続など当たり前のようにやっていますが、私のようにやったことないかたも多いのかなと思い、手順を省略せず記事にしてみました。VSCodeの左メニューなどわからないとなったら、調べていただけると簡単にわかるかと思います。 参考サイト https://qiita.com/hazigin/items/c291adf5dc9ccc13d11f https://webbigdata.jp/ai/post-7248 https://blog.serverworks.co.jp/tech/2020/02/20/vscode-remote-ssh/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pandas.DataFrame.applyを使って、DataFrameに複数列を一度に追加したい

TL;DR pandas.DataFrame.applyのオプショナル引数に、result_type="expand"を指定する pandas.DataFrame.applyの引数の関数(ラムダ式)は、タプルまたはリストを返すようにする 代入式の左辺では、追加する列名をリストで指定する def get_values(value0): # some calculation return value1, value2 df[["column1", "column2"]] = df.apply( lambda r: get_values(r["column0"]), axis=1, result_type="expand") 解説 適当なpandas.DataFrameがあるとします。 import pandas as pd df = pd.DataFrame() df["x"] = [1, 2, 3, 4, 5] print(df) x 0 1 1 2 2 3 3 4 4 5 このxという名前の列を使って計算を行い、新たな列を作成したいとします。ベクトルのまま計算できる場合は簡単です。ここでは2乗を計算する場合を例に取ってみます。 df["x2"] = df["x"] ** 2 print(df) x x2 0 1 1 1 2 4 2 3 9 3 4 16 4 5 25 もし新たな列の求める計算が複雑な関数で定義されており、ベクトルのまま計算できない場合には、pandas.DataFrame.applyを用いることができます。 def square(x): return x ** 2 df["x2"] = df.apply(lambda r: square(r["x"]), axis=1) print(df) もちろん結果は前と同じです。 さて、以下のような複数の値を返す関数を使って、複数列を同時に追加する場合はどうなるでしょうか? def get_values(x): return x ** 2, x ** 3 次のように書くとエラーになります。 df[["x2","x3"]] = df.apply(lambda r: get_values(r["x"]), axis=1) --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-19-3039b5c1e701> in <module> 1 def get_values(x): 2 return x**2, x**3 ----> 3 df[["x2", "x3"]] = df.apply(lambda r: get_values(r["x"]), axis=1) (中略) ValueError: Must have equal len keys and value when setting with an iterable このエラーが起きている理由は、右辺の値をprintすることで分かります。 print(df.apply(lambda r: get_values(r["x"]), axis=1)) 0 (1.0, 1.0) 1 (4.0, 8.0) 2 (9.0, 27.0) 3 (16.0, 64.0) 4 (25.0, 125.0) dtype: object 2個の要素を持つタプルが入った(1列の)Seriesになってしまっています。 2列5行のDataFrameにするには、result_type="expand"を指定します。 df[["x2","x3"]] = df.apply(lambda r: get_values(r["x"]), axis=1, result_type="expand") print(df) x x2 x3 0 1 1.0 1.0 1 2 4.0 8.0 2 3 9.0 27.0 3 4 16.0 64.0 4 5 25.0 125.0 参考にしたリンク
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pyhton 変数について

初めに 初学者です。アウトプットすることによって定着するのが目的です。間違いなどあればご指摘ください。 変数について 変数とは? まず、数値や文字列などにデータ類をまとめて値と呼びます。この値ですがプログラムを書いていると同じ値を何度も使ったりします。変数とは、この値を入れておく箱みたいなものです。 要するに、事前に値を変数に入れておくことによって、繰り返し使うことができます。また、変数に入れておくことによってコードも綺麗になります。 test = 'ハロー' print(test) 上記のコードでは、ハローが出力されます。詳しく書くと変数testに文字列ハローを代入しろ。変数testを表示せよとなり変数testのなかには文字列ハローが代入されているのでハローが出力されます。 変数の命名規則について(変数名の付け方) 変数には命名規則があります。命名規則を大きく3つに分けて説明していきます。 1.数字のみ、先頭が数字の名前はだめ これは数字のみだと数値と区別できなくなるからだそうです。また名前の先頭に数字を使うこともできません。 123とか1ageなどは命名することができません。 2.変数名は原則半角の小文字のみで書く 半角じゃなくてもエラーにはならないそうです。しかし、基本的にコードは半角で書きます。また、スペースが全角になるとエラーになってしまったりするため基本的には半角で書いた方が間違いは無いと思います。 3.予約後と同じ名前は禁止 例えば、pyhtonでは条件分岐があり、条件が間違っていたらfalse,あっていればtrueを返します。これらのfalseやtrueはpythonではすでに別の目的で使うことが決まっているので使えないとうことです。主な予約語については公式のURLを乗せておきます。 公式 主に上記3点を意識してわかりやすい変数名をつけ重複しないようにしていれば、大きな間違いは無いかと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TecoGAN で低解像度の動画を高解像度化

TecoGAN で低解像度の動画を高解像度化します。 環境はUbuntu20になります。 結構重いので、GPUを使うか、事前にffmpegで動画を縮小すると良いかもしれません。 install_tecogan.sh #!/usr/bin/bash # ========================================================== # TecoGan 用に python3.6.8 を仮想環境にインストール # ========================================================== sudo echo ; sudo apt-get install ffmpeg ; sudo apt-get install python3-venv ; sudo apt install -y \ build-essential \ libffi-dev \ libssl-dev \ zlib1g-dev \ liblzma-dev \ libbz2-dev \ libreadline-dev \ libsqlite3-dev \ git ; git clone https://github.com/pyenv/pyenv.git ~/.pyenv ; echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc ; echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc ; echo 'eval "$(pyenv init -)"' >> ~/.bashrc ; source ~/.bashrc ; pyenv -v ; pyenv install 3.6.8 ; pyenv local 3.6.8 ; pyenv versions ; # ========================================================== # TecoGAN のインストール # ========================================================== git clone https://github.com/thunil/TecoGAN ; cd TecoGAN ; python3 -m venv myvenv-topgan ; source myvenv-topgan/bin/activate ; python -m pip install pip -U ; python -m pip install -r requirements.txt ; python -m pip uninstall -y Keras Keras-Applications Keras-Preprocessing ; python -m pip install keras==2.3.1 ; python -m pip install tensorflow-gpu==1.14.0 ; python runGan.py 0 ; # ライブラリ群をダウンロード python runGan.py 1 ; # サンプルとして、LR/calendar に入っている画像を results/calendar に出力します。 # checck LR/calendar/*.png # checck results/calendar/*.png # ========================================================== # 動画のダウンロードと動画を画像群化 # ========================================================== # 動画のダウンロード(できればサイズは横幅240程度が望ましい) # youtube-dl がない場合は下記コマンドでインストール # sudo wget https://yt-dl.org/downloads/latest/youtube-dl -O /usr/local/bin/youtube-dl ; # sudo chmod a+rx /usr/local/bin/youtube-dl ; youtube-dl https://www.youtube.com/watch?v=RmlSsON0oY0 ; # 一旦前回分の削除 mkdir LR/calendar_back ; cp LR/calendar/* LR/calendar_back ; rm LR/calendar/* rm results/calendar/* ; # 動画を画像群化 ffmpeg -i "ドリフ大爆笑1983年op-RmlSsON0oY0.mkv" -vcodec png LR/calendar/%d.png ; # 縮小して使用する場合 # ffmpeg -i "ドリフ大爆笑1983年op-RmlSsON0oY0.mkv" -vf scale=120:-1 "低解像度動画.mkv" ; # ffmpeg -i "低解像度動画.mkv" -vcodec png LR/calendar/%d.png ; # TecoGAN の実行 python runGan.py 1 ; # checck results/calendar/*.png # 画像群を動画化 ffmpeg -i results/calendar/output_%d.png -vcodec libx264 -pix_fmt yuv420p "最終結果動画.mp4" ; 参考リンク あとで記載します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

構造生成メモ

BRICSを用いて、種構造から新規構造を生成 test.py import pandas as pd import numpy as np import matplotlib.pyplot as plt from rdkit import Chem from rdkit.Chem import Draw, BRICS def main(): df = load_data('smiles_list.csv') components = make_fragments(df['mol']) generated_mols = generate_mols(components) generated_mols_to_csv(generated_mols) draw_generated_mols(generated_mols) def load_data(file_name): df = pd.read_csv(file_name) df['mol'] = list(map(lambda x: Chem.MolFromSmiles(x), df['SMILES'])) return df def make_fragments(mols): allfrags = set() for mol in mols: frag = BRICS.BRICSDecompose(mol) allfrags.update(frag) frags_mol = [Chem.MolFromSmiles(f) for f in allfrags] return frags_mol def generate_mols(frags_mol): builder = BRICS.BRICSBuild(frags_mol) print('building mols...') generated_mols = [] for i in range(1000): try: m = next(builder) m.UpdatePropertyCache(strict=True) generated_mols.append(m) except StopIteration: pass np.random.shuffle(generated_mols) return generated_mols def generated_mols_to_csv(generated_mols): generated_mols_df = pd.DataFrame({'mol': generated_mols}) generated_mols_df['SMILES'] = list(map(lambda x: Chem.MolToSmiles(x), generated_mols_df['mol'])) generated_mols_df.to_csv('generated_mols.csv') def draw_generated_mols(mols): fig = Draw.MolsToGridImage(mols[:50], molsPerRow=3, subImgSize=(300,150)) fig.save('generated_mols.png') fig.show() if __name__=='__main__': np.random.seed(1234) main() smiles.csvを同じ階層に準備しておく smiles.csv SMILES C1CCCCC1 c1ccccc1 N[C@@H](C)C(=O)O C/C=C/C C1=CC2=CC3=CC=C(N3)C=C4C=CC(=N4)C=C5C=CC(=N5)C=C1N2 COC(=O)C(\C)=C\C1C(C)(C)[C@H]1C(=O)O[C@@H]2C(C)=C(C(=O)C2)CC=CC=C CC[C@H](O1)CC[C@@]12CCCO2
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

mpld3でのグラフ化メモ

カーソルを当てると、構造式が表示される散布図の作り方 test.py import pandas as pd import numpy as np from rdkit import Chem from rdkit.Chem.Draw import rdDepictor from rdkit.Chem.Draw import rdMolDraw2D from rdkit.Chem import AllChem from rdkit.Chem import DataStructs from sklearn.decomposition import PCA import matplotlib.pyplot as plt from mpld3 import plugins import mpld3 # mpld3.enable_notebook() def main(): df = load_data('generated_mols.csv') df['svg'] = [moltosvg(mol) for mol in df['mol']] df = pca_for_df(df) # print(df.head(20)) interactive_plot(df) def load_data(file_name): df = pd.read_csv(file_name,index_col=0,header=0) df['mol'] = [Chem.MolFromSmiles(mol) for mol in df['SMILES']] return df def moltosvg(mol,molSize=(200,200),kekulize=True): mc = Chem.Mol(mol.ToBinary()) if kekulize: try: Chem.Kekulize(mc) except: mc = Chem.Mol(mol.ToBinary()) if not mc.GetNumConformers(): rdDepictor.Compute2DCoords(mc) drawer = rdMolDraw2D.MolDraw2DSVG(molSize[0],molSize[1]) drawer.DrawMolecule(mc) drawer.FinishDrawing() svg = drawer.GetDrawingText() return svg.replace('svg:','') def mol2fparr(mol): arr = np.zeros((0,)) fp = AllChem.GetMorganFingerprintAsBitVect(mol,2) DataStructs.ConvertToNumpyArray(fp, arr) return arr def pca_for_df(df): pca = PCA(n_components=2) X = np.asarray([mol2fparr(mol) for mol in df['mol']]) print(X.shape) res = pca.fit_transform(X) print(res.shape) df['PCA1'] = res[:,0] df['PCA2'] = res[:,1] return df def interactive_plot(df): plt.rcParams["font.size"] = 18 fig, ax = plt.subplots(figsize=(10,6)) ax.set_xlabel('PCA1') ax.set_ylabel('PCA2') ax.set_title('chemical space') points = ax.scatter(df['PCA1'], df['PCA2'],s=200,alpha=0.5,edgecolors='none') tooltip = plugins.PointHTMLTooltip(points, df['svg'].values.tolist(),hoffset=10, voffset=10) plugins.connect(fig, tooltip) mpld3.save_html(fig, 'chemicalspace_for_generated_mols.html') if __name__ == '__main__': main() SMILES式が入った下記のようなcsvファイルを用意しておく。 下記例では、すでにmolファイルの列があるが、SMILES式から作成するので不要。 generated_mols.csv ,mol,SMILES 0,<rdkit.Chem.rdchem.Mol object at 0x126093390>,C=CC=CCC1=C(C)[C@@H](OC2[C@H](OC)C2(C)C)CC1=O 1,<rdkit.Chem.rdchem.Mol object at 0x12609f3f0>,COC(=O)C(C)=C(C)C(=O)OC 2,<rdkit.Chem.rdchem.Mol object at 0x1263d34b0>,CO[C@H]1C(C(=O)O[C@H]2CC[C@@]3(CCCO3)O2)C1(C)C 3,<rdkit.Chem.rdchem.Mol object at 0x126467e10>,C=CC=C(C)C(=O)O[C@H]1C([C@H]2CC[C@@]3(CCCO3)O2)C1(C)C 4,<rdkit.Chem.rdchem.Mol object at 0x1263ccd50>,CC1(C)C(C(=O)O[C@H]2CC[C@@]3(CCCO3)O2)[C@@H]1[C@H]1CC[C@@]2(CCCO2)O1 5,<rdkit.Chem.rdchem.Mol object at 0x1263da870>,COC1[C@H](OC2[C@H]([C@H]3CC[C@@]4(CCCO4)O3)C2(C)C)C1(C)C 6,<rdkit.Chem.rdchem.Mol object at 0x125b126f0>,CC[C@H]1CC(=O)C(CC=CC2[C@H](CC)C2(C)C)=C1C 7,<rdkit.Chem.rdchem.Mol object at 0x1260b5150>,CC=C(C)C(=O)OC1[C@H](OC)C1(C)C 8,<rdkit.Chem.rdchem.Mol object at 0x126467570>,C=CC=C(C)C(=O)OC(=O)C(C)=CC=C
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む