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

djangoで複数アプリがある状態で同一htmlファイルが上手く読み込めない

起きている問題: 違うアプリのhtmlファイルが読み込まれる。

例:) test1とtest2というアプリを作成している。
    test2のディレクトリのindex.htmlを読みたいがtest1ディレクトリのindex.htmlに遷移してしまう。

解決方法


プロジェクトのurls.pyを改修
  urlpatterns = [
path('admin/', admin.site.urls),
path('test1/', include(('test1.urls', 'test1'),)),
path('test2/', include(('test2.urls', 'test2'),)),
]
includeを変更する。
include(('アプリ名.urls', 'アプリ名'),))
以上で上手く表示される。


※補足
htmlファイルでurlタグ{% url 'view関数' %}をこのように利用した場合はエラーを吐く。
なので、{% url 'アプリ名:view関数' %}に変更する事により吐かなくなる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonを実行ファイル形式にする方法

あくまでも個人用の備忘録

はじめに

Pythonスクリプト(.py)から実行ファイル(.exe等)を生成する手段としてPyInstallerを使用した.

実行環境

OS Windows 10 Home
Python Python 3.6.9 :: Anaconda, Inc.
pip==19.2.3
PyInstaller==3.5

インストール方法

pip install pyinstaller

pipでインストール可能.

実行方法

pyinstaller filename.py option1 option2 ...

実行後のディレクトリ構成は以下のようになる.

├─filename.py       #Pythonスクリプト
├─filename.ico      #アイコン用ファイル(アイコンを設定する場合のみ用意する)
├─filename.spec
├─build
│  └─filename
├─dist
│  └─filename.exe
└─__pycache__

中間ファイルも生成される. 実行ファイルはdist下に生成される.

オプション(一部)

オプション 概要
単一ファイル化 --onefile 関連するファイルを1つにまとめてバイナリ化
コンソール非表示 --noconsole コンソール表示をしないようにする
Windowsバイナリ生成 --windowed 異なるプラットフォームでもWindowsのバイナリを作成
icon設定 --icon=filename.ico アプリケーションのアイコンを設定

実行ファイルにリソースを埋め込む

実行ファイルのアイコンは上記のオプションだけで設定できるが, タイトルバーやタスクバーにアイコンを表示するためには別途設定が必要である.

  • Pythonスクリプト(.py)に以下のコードを追加する
import os
import sys

def icon_path(filename):
  if hasattr(sys, "_MEIPASS"):
      return os.path.join(sys._MEIPASS, filename)
  return os.path.join(filename)

ファイルの展開先のパスが取得できる.

  • タイトルバーやタスクバーのアイコンを設定しているコードを書きかえる
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtGui import QIcon

if __name__ == '__main__':

    app = QApplication(sys.argv)
    app.setWindowIcon(QIcon(icon_path('filename.ico'))) #ココ
    window = ExWindow()
    sys.exit(app.exec_())

例としてPyQt5を使用している.

  • 実行ファイル作成
pyinstaller filename.py --onefile --noconsole --icon=filename.ico
  • filename.specを書きかえる
# -*- mode: python ; coding: utf-8 -*-

block_cipher = None


a = Analysis(['filename.py'],
             pathex=['path'],
             binaries=[],
             datas=[],
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
a.datas += [('filename.ico', '.\\filename.ico', 'DATA')] #コレを追加
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          [],
          name='filename',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          upx_exclude=[],
          runtime_tmpdir=None,
          console=False , icon='filename.ico')
  • 以下のコマンドを実行
pyinstaller filename.spec

これで実行ファイルのみでタイトルバーやタスクバーにアイコンが表示されるようになる.

注意点

生成される実行ファイルは作成元OSにのみ対応するので, Windowsで動く実行ファイルを作成するためにはWindowsが必要である.

参考

Pythonスクリプトを単一実行ファイルにする方法
PyInstallerで実行ファイルにリソースを埋め込み

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonでマウスの現在座標を表示する

はじめに

初投稿です。至らない部分が多々あると思いますがよろしくお願いします。pythonのkihonnのkもわからないので説明不足の点も多々あります。単語の使い方も怪しいです。初心者向けです。

開発環境

visual studio 2019のpython3.7です。

動機

pythonでマクロを作ろうと思い、そのテストのために作ってみようかなと思って作りました。

使用するパッケージとその導入

Tkinterとpyautoguiを使いました。

mouseCapture.py
import tkinter as tk
import pyautogui as pgui

asでパッケージ名を省略できるみたいです。(tkinterとpyautoguiってパッケージでいいんですよね?パッケージとかモジュールとかの区別もついてないです)

現在座標の表示

現在座標の表示をするための関数とそれをまとめたクラスを書きます。クラスはFrame,関数はコンストラクタと座標を更新するupdate_capture()の2つです。
コードは上で書いたものの続きです。

mouseCapture.py
class Frame(tk.Frame):
    def __init__(self,master=None):
        tk.Frame.__init__(self,master)
        self.position=tk.Label(self,text="")
        self.position.pack()
        self.update_capture()

    def update_capture(self):
        currentPosition=pgui.position()
        self.position.configure(text=currentPosition)
        self.after(20,self.update_capture)

解説できるところは解説していこうと思います。

Frameクラスはtkinterに備え付けのクラスで、widgetをまとめてくれるそうです。widgetは部品という意味です。(この記事を書きながら調べました:smiley:)ここでいう部品というのはLabelのことですね。ほかにもボタンなどが部品に該当します。

Label()のtextに表示したい文字列を代入して表示したい文字列を決め、pack()でそれを表示します。

下の関数のcurrentPositoinという変数はいらない気がしてきました、今更ですが。

configure()でtextの中身を変えます。ほかにも文字列の色とか変えられると思います。

after(n,f)でnミリ秒後にfを呼び出します。ここでは20ミリ秒後にupdate_capture()を再帰的に呼び出しています。これでプログラムが停止するまでupdate_capture()を呼び出し続けます。

メイン部分

Frameクラスのインスタンスを生成し、それをpack()して、mainloop()をくっつければ完成です。

mouseCapture.py
if __name__ == "__main__":
    f = Frame()
    f.pack()
    f.mainloop()

mainloop()はtkinterに入っている関数です。その名の通りfを無限ループにします。(?)

この見出しをつけるのに悩みました。

main.py
if __name__ == "__main__":

このフレーズはお決まりだと思うんですけどなんという名前がついているのでしょうか。筆者、気になります。

コード全体

プログラム全体のコードは以下のようになります。

mouseCapture.py
import tkinter as tk
import pyautogui as pgui

class Frame(tk.Frame):
    def __init__(self,master=None):
        tk.Frame.__init__(self,master)
        self.position=tk.Label(self,text="")
        self.position.pack()

        self.update_capture()

    def update_capture(self):
        currentPosition=pgui.position()
        self.position.configure(text=currentPosition)
        self.after(20,self.update_capture)

if __name__ == "__main__":
    f = Frame()
    f.pack()
    f.mainloop()

とりあえずこれをコピペすれば動きます。

参考にしたサイト

pythonに初めて触れたと言っても過言ではないのでかなり多くのサイトを見ました。その中でも特に参考になったものを載せておきます。

https://qiita.com/SquidSky/items/90eb450310f1697d03e9
この記事でtkinterの存在を知ることができました。基本的なことが載っているので、tkinterを使おうかなと思っている人は読んでみるといいと思います。

https://www.lisz-works.com/entry/pyautogui-mouse
このサイトでpyautoguiの存在を知ることができました。基本的なことが載っているので、pyautoguiを使おうかなと思っている人は読んでみるといいと思います。(文章の使いまわしはオブジェクト指向に則っていると感じるのは私だけでしょうか:thinking:

https://codeday.me/jp/qa/20190215/270208.html
after()関数について書かれています。文章は翻訳された日本語のようです。英語を読むよりか断然ましです。

https://codeday.me/jp/qa/20190511/799727.html
上と同じサイトです。Labelの中身を更新する方法について書かれています。現在のコードを書くのに最も貢献してくれました。(今思ったんですけど、こちらはメイン部分でpack()しないでも動くんですよね。私のコードではpack()をしないと動かないんですが:disappointed_relieved:

https://help.qiita.com/ja/articles/qiita-article-guideline
Qiitaのガイドラインです。とりあえず取り急ぎ。

https://ja.wikipedia.org/wiki/%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0
Wikipediaのフレームの記事です。Frameは炎のことだという勘違いはここで正されました。

さいごに

pythonでやる前はC#でやっていたんですが、なにがなにやらてんでわからなかったので、情報が豊富そうなpythonに移行したらうまくいきました。pythonは神ですね。

所謂「おまじない」が散見する、というか冒頭でも述べた通り説明が足りない箇所が多い記事になってしまいました。誠にごめんなさい。いろいろおかしい部分があると思いますので訂正コメントなどをしていただけたら幸いです。

目標がマクロを作ることなのでその過程で何かまた書きたくなったら書くと思います。そのときまでに成長していることを願うばかりです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

はじめてのDjango

はじめに

未来電子テクノロジーでインターンをしているErika-Mです。

最近Djangoについて学び始め、DjangoBrothersDjango GirlsというサイトでDjangoについてチュートリアルを進めてみました。

そこで今回は、Djangoがどのようなものなのか書いていきます。

Djangoとは

Pythonをもとに作られた、Webフレームワークのことです。
Webフレームワークというのは、Webサービスでよく使われる、ユーザー登録や投稿機能などの機能を簡単に作れるようにしたものです。

また、Djangoは拡張性や柔軟性が高く、セキュリティが強いのが特徴となっています。

Djangoでブログ管理サイトをつくろう

ざっくりと流れを見ていきましょう。
詳しく知りたい方は、DjangoBrothersDjango Girlsを参照してください。

PythonとDjangoの環境構築

まずは、Pythonをインストールします。
そして仮想環境を作り、その仮想環境の中でDjangoをインストールします。
仮想環境を作ることで、異なる言語を使う複数のプロジェクトを切り替えながら作業できるのです。

プロジェクトをつくる

Djangoのプロジェクトをつくります。
プロジェクトとは、アプリケーションというWebサービスに必要な機能を実現するための部分をまとめたものです。
さらに、ユーザー情報やブログ記事などのたくさんのデータを管理するためのデータベースをつくります。

アプリケーションで機能をつくる

アプリケーションのディレクトリをつくり、Djangoプロジェクトに登録します。
さらに、アプリケーション内にテンプレート(HTMLファイル)を配置し、HTMLファイルを表示させるためにViewをつくります。
このViewというものによって、ユーザーからのリクエストをもとに、HTMLファイルを返します。
それから、ユーザーがURLを打ち込んだ時に、HTMLファイルがブラウザに表示されるようURLの設定を行います。

ブログモデルをつくる

ブログモデルという設計図をつくり、マイグレートという作業によって、データベースの設計図をデータベースに反映します。
続いて管理者のみが使えるAdminページをつくります。
あとは、トップページを表示させたり、記事の詳細をつくったりすれば完成です!

まとめ

今回は、Djangoの特徴と簡単なブログ管理サイトの作り方について書きました。
最後までつくり上げると達成感があるのでぜひ挑戦してみてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WSLでJupyter Notebookの環境を整える

jupyter notebookをインストール

pipでインストールするだけです.

$ pip install jupyter

起動は下記コマンド.

$ jupyter notebook

ここで僕がはまったのが,/mnt/c/Users/ユーザー名/直下やDocuments内でこのコマンドをたたくとサーバーエラーか何かでjupyter notebookが起動できなかったことです.Ubuntu環境とWindows環境の共有用に自分で作った/mnt/c/Users/ユーザー名/MyHome/以下でたたくと問題なく開けました.
(このあたり知識不足なので,もし原因がわかる方がいれば教えていただけると助かります.)

jupyter-themesをインストール

dunovank/jupyter-themes: Custom Jupyter Notebook Themes
公式のドキュメントにすべて載っています.ここから自分が設定したものを載せます.

$ jt -t onedork -f source -T -N -kl 

-tでテーマの変更をします.カラースキームはonedorkにしました.

-fでコード部分のフォントを変更します.Source Code Proにしています.
コード部分以外のフォントも変更できますが,僕は気にならなかったのでそのままにしています.変えたい人は下記記事が参考になると思います.

-T, -N, -klはそれぞれツールバー,ノートブックの名前,カーネルロゴを表示するオプションです.つけておいた方が便利.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonでユニットテスト書いてみよう-2

概要

Pythonのユニットテストについて前回はunittestを紹介しました。今回は「doctest」について紹介します。

docstring

doctestを理解するためには、docstringを理解することが重要です。関数やクラスなどの機能を簡単に分かるために、それらの定義の先頭に入れる文字列の事をdocstringと言います。docstringがある事で他のところから参照する時に、その説明を簡単に見ることができます。
説明だけではわかりにくいので実際にコードを書いて確認してみましょう。
先ず、sample.pyというファイルを作成し、以下の内容を保存してください。

def add(num1, num2):
    """
    引数として受けた数値を足し算し、
    その結果を返す。

    Parameters
    ----------
    num1 : int
        1つ目の足し算する数値
    num2 : int
        2つ目の足し算する数値

    Returns
    -------
    result : int
        足し算の結果
    """
    result = num1 + num2
    return result

ターミナルから「sample.py」保存したフォルダーに移動し、以下のコマンドを実行してください。

$ python -c "import sample;print(sample.add.__doc__)"

以下の様に表示されると思います。

    引数として受けた数値を足し算し、
    その結果を返す。

    Parameters
    ----------
    num1 : int
        1つ目の足し算する数値
    num2 : int
        2つ目の足し算する数値

    Returns
    -------
    result : int
        対足し算の結果

上で表示されたように関数やクラスの説明を後で簡単に確認ができます。別ファイルから関数やクラスを呼び出す時は便利ですね。
VS Codeではマウスポインタを対象の関数などに合わせるとツールチップにdocstringが表示されます。
ちなみに、以下のコマンドを実行しても同じ結果になります。

python -c "import sample;help(sample.add)"

doctest

doctestはdocstringを使ってユニットテストを実装するモジュールです。docstringの中に入出力例を書いてテストを実装します。unittestと同様にPythonをインストールした時点で利用可能になっているので、第三者のライブラリをインストールする必要がありません。Pythonの環境さえあれば、簡単に検証ができます。
docstring(コメント)にテストケースを書いて、それ以外は実際に動かすコードを書きます。

基本的にdoctestは、以下の順に書きます
1. doctestライブラリをインポートする
2. docstringの末尾にテストケースを記述
3. doctest.testmod()でテストを実行する

# 1. doctestのインポート
import doctest

# 2. docstringの末尾にテストケースを記述
def func_xxx():
    """
    関数の説明
    テストケース記述
    """
    関数の中身

if __name__ == '__main__':
    # 3. 実行
    doctest.testmod()

doctest書き方

「sample.py」の中身を上記で書いて順に沿って、以下のように変えます。

import doctest

def add(num1, num2):
    """
    引数として受けた数値を足し算し、
    その結果を返す。

    Parameters
    ----------
    num1 : int
        1つ目の足し算する数値
    num2 : int
        2つ目の足し算する数値

    Returns
    -------
    result : int
        対足し算の結果

    UnitTesting
    -------
    >>> add(2, 3)
    6
    """
    result = num1 + num2
    return result

if __name__ == '__main__':
    doctest.testmod()

お気付きだと思いますが、docstring(コメント)の最後の2行がテストケースになります。2と3足したら5になりますが、あえて6が期待値として書いております。
このようにソースコードの中にテストケースも一緒に残るので後で見てもわかりやすいですね。

python sample.pyを実行するだけでテストが開始されます。

$ python sample.py
**********************************************************************
File "sample.py", line 22, in __main__.add
Failed example:
    add(2, 3)
Expected:
    6
Got:
    5
**********************************************************************
1 items had failures:
   1 of   1 in __main__.add
***Test Failed*** 1 failures.

もしテストケースが全て正しかった場合は、上記のコマンドを実行したら何も出力されません。何も出力しないと何が起きたかわからないため、わざと結果が6と書いておき、テストを失敗させました。ちなみに、python sample.py -vを実行するとどのように検証行ったか詳細に表示されます。

補足

上記の手順の①と③をソースコードに書かずにテスト実装する事もできます。以下の内容を「sample2.py」として保存してください。

def add(num1, num2):
    '''
    足し算
    >>> add(2, 3)
    6
    >>> add(-2, 3)
    1
    '''
    return num1 + num2

ターミナルからpython -m doctest sample2.pyを実行するだけでユニットテストが開始されます。

$ python -m doctest sample2.py
**********************************************************************
File "C:\Users\dakc\sample2.py", line 4, in sample2.add
Failed example:
    add(2, 3)
Expected:
    6
Got:
    5
**********************************************************************
1 items had failures:
   1 of   2 in sample2.add
***Test Failed*** 1 failures.

出力内容通り、2つのテストケースの中で一つが失敗した事が分かります。
テストケースを色々書いてどのような結果が得られるか見てみて下さい。

最後に

前回は、unittestを紹介しました。doctstringと比べてみてください。
次回は、pytestについ書きたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Filmarksからのデータ抽出を目的としたウェブスクレイピングの勉強 #5

閑話休題として,特定の映画のレビューを全件取得し,頻出の単語の集計を行います.
ほとんど,コピペと今までのコードの流用です.
本稿は筆者の趣味の塊ですので,ご容赦ください.

レビュー取得のコード

特定の映画のレビューを1ページ分取得するコードを紹介します.申し訳ありませんが,引用元以上のコードをかける自信がなかったので9割9分コピペです.

def fetch_movie_reviews_per_page(beautiful_soup_object):
    """
    Fetch movie reviews per page. Max 10 reviews.
    :param beautiful_soup_object:
    :return list of dict:
    """
    output = []
    # 辞書型で格納
    review = dict(review_id="",
                  review_text="",
                  posted_date="",
                  rating_score=0)

    soup = beautiful_soup_object

    # id_urlsのリストを取得(ここを変更しました)
    review_idurls = soup.select(".c-media__content > .c-media__text > a")
    # レビューのリストを取得
    review_texts = soup.select(".p-mark__review")
    # 投稿日時のリストを取得
    posted_dates = soup.select(".c-media__content > time")
    # 評価(星)のリストを取得
    rating_scores = soup.select(".c-media__content > div > .c-rating__score")
    # レビューがあれば
    if review_texts:
        # id_urls,レビュー,投稿日時,評価を各々のリストから1ずつ処理
        for review_idurl,review_text, posted_date, rating_score in zip(review_idurls,review_texts,posted_dates, rating_scores):
            # id_urlsを取得(ここを変更しました)
            review_id = review_idurl.attrs['href'].split('/')[-1]
            # レビューを取得
            review_text = review_text.text.replace("\u3000", "")
            # 投稿日時を取得
            posted_date = posted_date.get("datetime")
            # 評価無しでは0
            if rating_score.text == "-":
                review['rating_score'] = 0
            # 文字列から数値へ
            else:
                rating_score = float(rating_score.text)
                review['rating_score'] = rating_score
            review['review_id']   = review_id
            review['review_text'] = review_text
            review['posted_date'] = posted_date
            output.append(review.copy()) # output.append(review)ではすべての要素が同じになるダメ
    return output
出力結果

頻出単語の集計

今回はMecabで形態素解析を行い,一般名詞に限り頻出単語を集計してみようと思います.
形態素解析は文章を名詞,形容詞等の品詞ごとに分割することで,Mecabはpythonと連携できる形態素解析のオープンソースです.他にjanome,jumanなどがありますが,Mecabが簡単でしたのでしようしました.
Mecabの名前の由来は,開発者の好物が「海藻のメカブだった」ことだそうです.
コードは以下の通りです.

import MeCab
import sys
import re
from collections import Counter
import pandas as pd

# レビューはcsvで出力したので読み込む
df = pd.read_csv('joker_reviews.csv')

# 形態素解析の準備
m = MeCab.Tagger()

items = []
# 1行ずつ解析
for i in range(len(df)):
    # 形態素解析
    parse = m.parse (df.review_text[i])
    # 形態素解析の結果を改行で区切ってリスト化
    lines = parse.split('\n')

    # 形態素解析の結果を再整理
    for line in lines:
        # リストの要素を\t・,で区切る
        mecab = re.split('[\t,]',line)
        items.append(mecab)

# 形態素解析の結果の内を名詞抽出
# 一般名詞をcount
words = [[item[0],1]
         for item in items
         # 名詞をリストに格納
         if (item[0] not in ('EOS', '', 't', 'ー','‼') and
             (item[1] == '名詞') and (item[2] == '一般'))]

word_df = pd.DataFrame(words,columns=['一般名詞','count'])

# '一般名詞'ごとに頻度計算
out_df = word_df.groupby(['一般名詞']).sum().reset_index()

頻出単語の集計が終わりましたので,上記の出力結果の頻出単語の上位30位をグラフで示します.

# グラフ描画のためmatplotlibのpyplotをインポート
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

#最大表示列数の指定(ここでは50列を指定)
pd.set_option('display.max_columns', 50)
pd.set_option('display.max_rows', 500)

# ノートブック内で拡大・縮小可能
#%matplotlib notebook
%matplotlib inline

# seabornのスタイルでプロット
sns.set(style="darkgrid", palette="muted", color_codes=True,font=['IPAGothic'])


# 降順に並べる
rank_df = out_df.sort_values('count',ascending=False).reset_index(drop=True)

# 棒グラフを出力-x軸
labels = rank_df.iloc[:30,0].tolist()
x_axis = np.arange(len(labels))  # numpyで横軸を設定

# バーの太さ
height = 0.5

# 5月の棒グラフ
# 図の大きさ
plt.figure(figsize=(15,20))
#plt.ylim(0, 900)
y = rank_df.iloc[:30,1].to_numpy()
plt.barh(x_axis,y[::-1],height=height)
plt.yticks(x_axis,labels[::-1])# ラベル

plt.show()

出力結果

ここからのグラフを見たときの筆者の浅い感想です.
タイトルにもなっている「ジョーカー」が一番多いですね.
あとは,ジョーカーのキャライメージの「悪,狂気,笑い」や「ダーク,ナイト,ヒース」のジョーカーが登場したダークナイトのキーワードがありますね.
意外だったのが「階段」ですね.おそらく,階段でのダンスシーンのことを言ってると思いますが,上位30位に入るほど印象に残ってるとは思いませんでした.

おわりに

今回は,箸休めがてらレビューの抽出・集計を行いました.
次回は年代別で高評価の映画の抽出をします.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Filmarksからのデータ抽出を目的としたウェブスクレイピングの勉強

閑話休題として,特定の映画のレビューを全件取得し,頻出の単語の集計を行います.
ほとんど,コピペと今までのコードの流用です.
本稿は筆者の趣味の塊ですので,ご容赦ください.

レビュー取得のコード

特定の映画のレビューを1ページ分取得するコードを紹介します.申し訳ありませんが,引用元以上のコードをかける自信がなかったので9割9分コピペです.

def fetch_movie_reviews_per_page(beautiful_soup_object):
    """
    Fetch movie reviews per page. Max 10 reviews.
    :param beautiful_soup_object:
    :return list of dict:
    """
    output = []
    # 辞書型で格納
    review = dict(review_id="",
                  review_text="",
                  posted_date="",
                  rating_score=0)

    soup = beautiful_soup_object

    # id_urlsのリストを取得(ここを変更しました)
    review_idurls = soup.select(".c-media__content > .c-media__text > a")
    # レビューのリストを取得
    review_texts = soup.select(".p-mark__review")
    # 投稿日時のリストを取得
    posted_dates = soup.select(".c-media__content > time")
    # 評価(星)のリストを取得
    rating_scores = soup.select(".c-media__content > div > .c-rating__score")
    # レビューがあれば
    if review_texts:
        # id_urls,レビュー,投稿日時,評価を各々のリストから1ずつ処理
        for review_idurl,review_text, posted_date, rating_score in zip(review_idurls,review_texts,posted_dates, rating_scores):
            # id_urlsを取得(ここを変更しました)
            review_id = review_idurl.attrs['href'].split('/')[-1]
            # レビューを取得
            review_text = review_text.text.replace("\u3000", "")
            # 投稿日時を取得
            posted_date = posted_date.get("datetime")
            # 評価無しでは0
            if rating_score.text == "-":
                review['rating_score'] = 0
            # 文字列から数値へ
            else:
                rating_score = float(rating_score.text)
                review['rating_score'] = rating_score
            review['review_id']   = review_id
            review['review_text'] = review_text
            review['posted_date'] = posted_date
            output.append(review.copy()) # output.append(review)ではすべての要素が同じになるダメ
    return output
出力結果

頻出単語の集計

今回はMecabで形態素解析を行い,一般名詞に限り頻出単語を集計してみようと思います.
形態素解析は文章を名詞,形容詞等の品詞ごとに分割することで,Mecabはpythonと連携できる形態素解析のオープンソースです.他にjanome,jumanなどがありますが,Mecabが簡単でしたのでしようしました.
Mecabの名前の由来は,開発者の好物が「海藻のメカブだった」ことだそうです.
コードは以下の通りです.

import MeCab
import sys
import re
from collections import Counter
import pandas as pd

# レビューはcsvで出力したので読み込む
df = pd.read_csv('joker_reviews.csv')

# 形態素解析の準備
m = MeCab.Tagger()

items = []
# 1行ずつ解析
for i in range(len(df)):
    # 形態素解析
    parse = m.parse (df.review_text[i])
    # 形態素解析の結果を改行で区切ってリスト化
    lines = parse.split('\n')

    # 形態素解析の結果を再整理
    for line in lines:
        # リストの要素を\t・,で区切る
        mecab = re.split('[\t,]',line)
        items.append(mecab)

# 形態素解析の結果の内を名詞抽出
# 一般名詞をcount
words = [[item[0],1]
         for item in items
         # 名詞をリストに格納
         if (item[0] not in ('EOS', '', 't', 'ー','‼') and
             (item[1] == '名詞') and (item[2] == '一般'))]

word_df = pd.DataFrame(words,columns=['一般名詞','count'])

# '一般名詞'ごとに頻度計算
out_df = word_df.groupby(['一般名詞']).sum().reset_index()

頻出単語の集計が終わりましたので,上記の出力結果の頻出単語の上位30位をグラフで示します.

# グラフ描画のためmatplotlibのpyplotをインポート
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

#最大表示列数の指定(ここでは50列を指定)
pd.set_option('display.max_columns', 50)
pd.set_option('display.max_rows', 500)

# ノートブック内で拡大・縮小可能
#%matplotlib notebook
%matplotlib inline

# seabornのスタイルでプロット
sns.set(style="darkgrid", palette="muted", color_codes=True,font=['IPAGothic'])


# 降順に並べる
rank_df = out_df.sort_values('count',ascending=False).reset_index(drop=True)

# 棒グラフを出力-x軸
labels = rank_df.iloc[:30,0].tolist()
x_axis = np.arange(len(labels))  # numpyで横軸を設定

# バーの太さ
height = 0.5

# 5月の棒グラフ
# 図の大きさ
plt.figure(figsize=(15,20))
#plt.ylim(0, 900)
y = rank_df.iloc[:30,1].to_numpy()
plt.barh(x_axis,y[::-1],height=height)
plt.yticks(x_axis,labels[::-1])# ラベル

plt.show()

出力結果

ここからのグラフを見たときの筆者の浅い感想です.
タイトルにもなっている「ジョーカー」が一番多いですね.
あとは,ジョーカーのキャライメージの「悪,狂気,笑い」や「ダーク,ナイト,ヒース」のジョーカーが登場したダークナイトのキーワードがありますね.
意外だったのが「階段」ですね.おそらく,階段でのダンスシーンのことを言ってると思いますが,上位30位に入るほど印象に残ってるとは思いませんでした.

おわりに

今回は,箸休めがてらレビューの抽出・集計を行いました.
次回は年代別で高評価の映画の抽出をします.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[ゼロから作るDeep Learning]ニューラルネットワークの学習について分かりやすく解説してみた

はじめに

この記事はゼロから作るディープラーニング 5章ニューラルネットワークの学習を自分なりに理解して分かりやすくアウトプットしたものです。
文系の自分でも理解することが出来たので、気持ちを楽にして読んでいただけたら幸いです。
また、本書を学習する際に参考にしていただけたらもっと嬉しいです。

ニューラルネットワークの学習とは

前章の記事でも説明した通り、多層パーセプトロンでコンピュータのような複雑な処理を行うためには数万にも及ぶ数のパラメータを設定しないといけません。そして、その設定をデータを学習することで自動的に設定してくれるのがニューラルネットワークです。
つまり、ニューラルネットワークの学習とはデータを学習して複雑な処理ができるような最適パラメータを設定することいです。

では、どのような仕組みで学習して最適なパラメータを見つけ出すのでしょう。ニューラルネットワークの学習で重要となるものは主に二つあります。それは、損失関数勾配(微分)です。

損失関数について

損失関数とはニューラルネットワークの性能の悪さを表してくれる関数で、ニューラルネットワークが自身の現段階の性能を判断するための指標として使われます。
ニューラルネットワークの学習では損失関数の最小化を目標に処理をしていきます。
詳しいことは次の記事で解説します。

勾配(微分)について

勾配とは各パラメータの最適な値への方向を表すものです。具体的なことを言うと、各パラメータの偏微分を一次元配列にまとめたものを言います。

偏微分は各変数を微小な値増加させた時の全体に及ぼす変化を表します。つまり、各パラメータの偏微分を出すことで各パラメータを微小な値増加させた時の損失関数の変化を出すことができます。
ニューラルネットワークの学習では損失関数の最小化を目指すので、偏微分が正の値ならパラメータの値を小さくすれば最適パラメータに近づき、負の値ならパラメータの値を大きくすれば最適パラメータに近づくことがわかります。
スクリーンショット 2019-10-20 20.21.22.png

こうして勾配を求めることでパラメータを進める方向を求めて最適パラメータを探索していきます。

今回は簡単な内容だけを解説しましたが、次回から詳しい解説や実装を行いたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Jetson_nano】将棋所で将棋AIで遊ぶ♬

前回、参考の通りJetson_nanoでChainerなどの環境構築まで実施した。
そこで、今回は昨年動かしてみた将棋AIを動かしてみる。
【参考】
【Jetson_nano】Opencv, Chainer, Keras,そしてScikit-learn環境構築♬
【将棋AI】「将棋AIで学ぶディープラーニング」を読む♪~対戦してみる

やったこと

・将棋所を動かす
・dlshogiを動かす

・将棋所を動かす

将棋所は「Ubuntu Linuxで動作させる場合の注意点」のまんまで動きました。
まず、Displayの解像度は1280x1024がいいと思います。

将棋所のダウンロード

将棋所のサイトからShogidokoro.zipをダウンロードし、展開してから、Shogidokoroフォルダをどこか適当な場所に置きます。

Monoのインストール

Windowsで動くshougidokoroをUbuntuで動かすにはMonoをインストールします。

$ sudo apt install mono-complete

将棋所を動かす

端末でそのディレクトリに移動してから次のコマンドを実行します
最初のコマンドはうまく将棋所を起動するためのおまじないです。

$ export TERM=xterm
$ mono Shogidokoro.exe

これでとりあえず動きますが、文字が見えない対戦エンジンが表示されないなどの問題があります。

日本語フォントをインストールする

以下のようにtakaoフォントをインストールすると表示や文字が見やすく改善する。

$ sudo apt install 'fonts-takao-*'

この時点で、人同士の対戦が出来る。

・dlshogiを動かす

将棋所に付属のLesserkaiは動かない。同じく他の将棋エンジンもWindows環境でコンパイルされているものは動かない。Ubuntu環境でコンパイルし直せば動くようであるが、今回はdlshogiを動かすこととする。まず、以下の参考からdlshogiのzipファイルをダウンロードして解凍する。
【参考】
TadaoYamaoka/python-dlshogi
解凍したら、そのディレクリに移動して、以下のコマンドを実行します。

pip3 install --no-cache-dir -e .

これで以下のsetup.pyが実行されてプログラムへimportできるようになります。

import setuptools

setuptools.setup(
    name = 'python-dlshogi',
    version = '0.0.1',
    author = '',
    packages = ['pydlshogi'],
    scripts = [],
)

次に、python-shogiをインストールします。dlshogiはpython-shogiの上に作成されています。

$pip3 install python-shogi

これでdlshogiが動くようになったと思います。
そこで、以下の参考の通り手打ちを試してみましょう。
【参考】
【将棋AI】「将棋AIで学ぶディープラーニング」を読む♪~価値ネットワークで対戦する
ただし以下の../python-dlshogi-master/model/model_policyは予めpolicy_player.pyに設定したmodelfile名です。そして、model/model_policyは実際のweightsの配置です。

$python3 -m pydlshogi.usi.usi_policy_player
usi

id name policy_player
option name modelfile type string default ../python-dlshogi-master/model/model_policy
usiok

setoption name modelfile value model/model_policy
isready

readyok

position startpos

lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1

go

info string 9g9f : 0.00834
info string 8g8f : 0.00000
info string 7g7f : 0.48470
info string 6g6f : 0.00012
info string 5g5f : 0.00153
info string 4g4f : 0.00072
info string 3g3f : 0.00013
info string 2g2f : 0.46938
info string 1g1f : 0.00088
info string 9i9h : 0.00001
info string 1i1h : 0.00000
info string 7i7h : 0.00043
info string 7i6h : 0.00139
info string 3i4h : 0.01277
info string 3i3h : 0.00525
info string 6i7h : 0.00249
info string 6i6h : 0.00000
info string 6i5h : 0.00034
info string 4i5h : 0.00606
info string 4i4h : 0.00002
info string 4i3h : 0.00001
info string 2h7h : 0.00070
info string 2h6h : 0.00078
info string 2h5h : 0.00049
info string 2h4h : 0.00001
info string 2h3h : 0.00003
info string 2h1h : 0.00000
info string 5i6h : 0.00103
info string 5i5h : 0.00111
info string 5i4h : 0.00005
bestmove 7g7f
などと出れば成功です。少なくともdlshogiが動きます。

batファイルをshにする

windowsのbatファイルをubuntuのshファイルに変更します。
第一行目の@echo offを#!bin/shと書き換え、拡張子をbatからshに変更します。

モデルのディレクトリを変更する

上記と同じですがpydlshogiplayerのpolicy_player.pyのモデルのweightsの格納ディレクトリを指定し直します。
平岡サイトのままだと以下に変更します。

self.modelfile = r'../model/model_policy'

エンジン登録

これでエンジン登録して遊べると思います。
Screenshot from 2019-10-20 19-08-19.png
なお、実は他のエンジンは登録できていません。
実行速度はWindowsのものと変わりません。サクサク動きます。

まとめ

・将棋所をjetson_nanoで動かしてみた
・将棋AIのdlshogiで遊んでみた

・棋譜を保存して強化学習をしてみようと思う

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

mypy-0.740 から object.__exit__() の返り値の判定が変化した話

つい先日(10/17)、mypy 0.740 がリリースされました。そこでハマった話を共有します。

mypy-0.740 が新たなエラーを出すようになった

新バージョンが出たことにより、mypy が以下のエラーを出すようになりました。

sphinx/util/docutils.py:186:5: error: "bool" is invalid as return type for "__exit__" that always returns False
sphinx/util/docutils.py:186:5: note: Use "typing_extensions.Literal[False]" as the return type or change it to "None"
sphinx/util/docutils.py:186:5: note: If return type of "__exit__" implies that it may return True, the context manager may swallow exceptions

0.740 から常に False を返す __exit__() は返り値の型に bool を使うべきではない、というエラーが出るようになりました。そして、ヒントとして Literal[False] を指定するか、None を返すようにコードを書き換えるべきだという Note が出ています。

しかし、 Python のドキュメント では object.__init__() の返り値は以下のように説明されており、False を返してもよいように見えます。

もし、例外が送出され、かつメソッドが例外を抑制したい場合(すなわち、例外が伝播されるのを防ぎたい場合)、このメソッドは True を返す必要があります。そうでなければ、このメソッドの終了後、例外は通常通り伝播することになります。

これは一体どういうことでしょう。

変更を追いかけてみる

ということで、mypy と typeshed の変更点を拾い読みしてみたところ、こんなイシュー (mypy#7214)こんなコミットを見つけました。
細かいやり取りは読んでもらうとして、どうやらこのイシューがきっかけで mypy に新しい(暗黙的な)ルールを追加されたようです。

新ルールでは __exit__() メソッドの返り値によって、その振る舞いを次のように推測します。

  • 返り値の型が bool の場合は例外を抑制する(可能性がある)
  • 返り値の型が None の場合は例外を伝播する

このルールに則って mypy は「常に False を返す (= 例外を伝搬する)」 __exit__() メソッドの返り値に bool を指定すべきではない、と判定するようになったようです。

どう対処するとよいか

エラーに従って

  • 返り値の型を Literal[False] にする
  • コードを書き換えて返り値を None にする (型も None にする)

と書き換えるか、伝家の宝刀 # type: ignore しかなさそうです。
ちなみに、手元では返り値を None に変更しました。

正確な返り値の型チェックのためには context manager の振る舞いを知る必要があるので致し方ないのですが、mypy 独自の(しかも暗黙の)ルールが登場するのはちょっと厳しいですね。

謝辞

@atsuoishimoto とのやりとりがきっかけでこの記事を書きました。いつもありがとうございます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PythonからRustに乗り換えたときに使える小技メモ

動機

C/C++から出発して主にPythonを使う筆者.現在は,速度と安全性を求めてRustを学習している.
そこで,本稿ではPythonの構文に対応するRustの構文をまとめておく.
また,新たに知り得た段階で随時更新していくこととする.

実行環境

Python = 3.7.4
rustc = 1.38.0

本題

厳密に「1対1対応」なのかは考慮しないものとする.

キャスト

個人的によく使うもの.

a = int('1') # str -> int
b = str(1)   # int -> str
fn main(){
  let num: i32 = "1".parse().unwrap(); // str -> int
  let s: String = num.to_string();     // int -> str
}

enumerate()

enumerate.py
lst = [1, 2, 3, 4]
for idx, v in lst.enumerate():
  print(idx, v)

# OUTPUT:
# 0 1
# 1 2
# 2 3
# 3 4
main.rs
fn main(){
  let vec = vec![1,2,3,4];
  for (idx, v) in vec.iter().enumerate(){
    println!("{} {}", idx, v);
  }
}

zip()

zip.py
vec = [1,2,3,4]
tor = [5,6,7,8]
for v, t in zip(vec, tor):
  print(v, t)

# OUTPUT:
# 1 5
# 2 6
# 3 7
# 4 8
fn main(){
  let vec = vec![1,2,3,4];
  let tor = vec![5,6,7,8];
  for (v, t) in vec.iter().zip(tor.iter()) {
    println!("{} {}", v, t);
  }
}

exit()

import sys

sys.exit(0)
std::process;

fn main() {
  process::exit(0);
}

あくまで安全にプログラムを終了させるだけならreturn;で良いらしい.

fn main() {
  //...(some codes)...
  return;
  //...(some codes)...
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PythonでPDFからテキスト抽出

はじめに

全文検索などで、PDFのデータをテキストとして抽出したい場合があります。
PyPDF2というライブラリはいけそうですが、日本語がある場合は
pdfminer.six、Apache Tikaのいずれかを使って日本語を抽出することは可能です。

抽出する関連ライブラリをメモします。

Tikaで抽出するサンプル

Tikaインストール

pip install tika

サンプル

pdf.py
from tika import parser

pdf = parser.from_file("C:/var/k_ryouyouhi_shinseisho.pdf")

print(pdf["content"])

実行すると、JARファイルをダウンロードされますね。テキストも正しく抽出されました。

python .\pdf.py
2019-10-20 18:08:52,392 [MainThread  ] [INFO ]  Retrieving http://search.maven.org/remotecontent?filepath=org/apache/tika/tika-server/1.19/tika-server-1.19.jar to C:\Users\username\AppData\Local\Temp\tika-server.jar.

pytesseract

pytesseract: https://pypi.org/project/pytesseract/

Python-tesseract is an optical character recognition (OCR) tool for python. That is, it will recognize and “read” the text embedded in images.

画像、チャートがある場合、画像、チャートに入っている文字の抽出も可能のようです。

camelot

camelot: https://camelot-py.readthedocs.io/en/master/

camelotはPDFからテーブルのデータフレームを取得できるようですが、
ローカルでGhostScriptエラーがあって、32,64ビットを両方インストールすると、インストールしていないエラーが解消されましたが、
下記のエラーがあって一旦動作確認はやめました。

File "C:\Python37\lib\site-packages\camelot\ext\ghostscript\_gsprint.py", line 169, in init_with_args 
    rc = libgs.gsapi_init_with_args(instance, len(argv), c_argv)
OSError: exception: access violation writing 0x00000080

pip install camelot-py[cv]を実行すると、関連するパッケージはclick, jdcal, et-xmlfile, openpyxl, PyPDF2, sortedcontainers, pdfminer.six, opencv-python, camelot-pyも一緒にインストールされます。

あとはghostscriptをダウンロードしてインストールできます。(Windowsの場合は32ビットが必要みたいです)
ghostscript: https://www.ghostscript.com/download/gsdnld.html

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iphoneだけでPythonistaでyoutubeから動画をダウンロード出来るようにしよう!

概要

みなさんこんにちは。
Pytohnista3を触り始めてからの僕の念願が一つ達成されました。
ずばりYoutubeの動画をiphoneからダウンロードするです!

参考にしたサイト

実際やったこと、ソース

  • pythonistaの中にフォルダを作ります。
  • 作ったフォルダの中に「DL_videos」というフォルダを作成します。ダウンロードした動画はこのフォルダに保存されます。
  • 以下のソースを作成します。ソースは少し多いですが頑張って作ってみてください。

①byte2si.py

import math
def main(byte):
    #byteの桁数を取得して、3桁刻みに単位を変えてくだけ
    if 0 <= byte:
        dig = int(math.log10(byte))
        if 0 <= dig < 3:
            bytes = byte / pow(10, 0)
            return bytes, 'B'
        elif 3 <= dig < 6:
            bytes = byte / pow(10, 3)
            return bytes, 'KB'
        elif 6 <= dig < 9:
            bytes = byte / pow(10, 6)
            return bytes, 'MB'
        elif 9 <= dig < 12:
            bytes = byte / pow(10, 9)
            return bytes, 'GB'
        elif 12 <= dig <= 15:
            bytes = byte / pow(10, 12)
            return bytes, 'TB'
    else:
        raise ValueError('Not num')


def byte2si(byte):
    num, unit = main(byte)
    #少数第2位まで丸める
    renum = round(num, 2)
    return str(renum) + unit

②main.py

from pytube import YouTube
#import add_video
import my_module
import byte2si
import url_get
import os
import glob
import time
import sys
import youtube_dl

VIDEO_DIR = os.path.join(os.getcwd(), "DL_videos")
OPTS = {
    "outtmpl": "{VIDEO_DIR}/%(title)s.mp4".format(VIDEO_DIR=VIDEO_DIR),
    'ignoreerrors': True
}


def download(url):
    """
    Download video from YouTube.

    Parameters
    ----------
    url : str
        YouTube video URL

    Returns
    ----------
    info : dict
        Downloaded video info.
    """
    print("Downloading {url} start..".format(url=url))
    with youtube_dl.YoutubeDL(OPTS) as y:
        info = y.extract_info(url, download=True)
        print("Downloading {url} finish!".format(url=url))
    return info

def rename(info):
    """
    Rename downloaded video filename as camelcase.

    Parameters
    ----------
    info : dict
        Downloaded video info.
    """
    title = info["title"]
    pattern = '{VIDEO_DIR}/{title}.mp4'.format(VIDEO_DIR=VIDEO_DIR, title=title)
    for v in glob.glob(pattern, recursive=True):
        print("{title}.mp4 found! Renaming start..".format(title=title))
        file_path = os.path.join(VIDEO_DIR, v)
        new_file_path = file_path.replace(' ', '_')
        os.rename(file_path, new_file_path)
        print("Renaming finish!".format(title))

def absfpath(path):
    # ”Download”ディレクトリ下のファイルを取得する
    flist = glob.glob(path + '/*')
    if flist:
        # “Download”ディレクトリ下の1つしかない動画ファイルを取得する
        fpath = os.path.abspath(flist[0])
         # “Download”ディレクトリ下にある動画ファイルの絶対パスを返す
        return fpath
    else:
        raise OSError('The file does not exist.')


def main():
    #プログレスバー
    print('Now executing...')

    # YouTubeアプリからurlを取得
    url = url_get.yturl()
    if not url:
        raise ('Not YouTube')

    print(url)
    info = download(url)
    rename(info)
    abspath = absfpath(VIDEO_DIR)


if __name__ == '__main__':
    # 開始時間を取得
    start = time.time()
    main()
    #終了時間を少数第3位まで取得
    finish = round(time.time() - start, 3)
    #実行所要時間を表示
    print('Execute Time:', finish)
    #終了
    print('Done')

③url_get.py

import appex
import re


def trns():
    if not appex.is_running_extension():
        raise RuntimeError(
            'This script is intended to be run from the sharing extension.')

    url = appex.get_text()
    if not url:
        raise ValueError('No input text found.')
    elif re.search(r'youtube', url):
        return url
    raise ValueError('Not YouTube')


def yturl():
    geturl = trns()
    # 取得したurlの余分なテキストを置換し、なくす
    url = geturl.replace('&feature=share', '')
    return url


if __name__ == '__main__':
    print(yturl())
  • main.pyをSafariやyoutubeの共有→「Run Pythonista Script」の中で実行できるよう登録します。

使い方

  • Youtubeを開きます。
  • 見たい動画またはプレイリストに行きます。
  • 「共有」から「Run Pythonista Script」を選び登録したスクリプトを実行します。
    • 動画の場合:動画がダウンロードされDL_Videosフォルダの中に保存されます。
    • プレイリストの場合: プレイリスト内全ての動画がダウンロードされDL_Videosフォルダの中に保存されます(プレイリストは公開のものしかダウンロードできないのでご注意!)。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

周期関数の周期を知りたかったので、がんばった。~pythonを使って~

周期的に変化する関数の周期を知りたい

 計算の結果が周期関数的なふるまいをすることがあり、その周期の具体的な値を求めたいことがあります。そのため、任意の計算結果から周期を求めるアルゴリズムを自作してみました。適切なデータ間隔であれば良い精度で正しい値を求められそうです。

周期を求めるためのアイデアの説明

 周期関数$y = f(x)$が閾値$y = h$の上下で振動しているとします。基本的には、$h$を下から上、または、上から下に横切る時から$h = f(x_h)$となる$x_h$を3つ以上記録し、それらから周期を求めることがゴールです。しかし、不連続なデータの集まりである計算結果から正確な$x_h$をどのように求めたらよいでしょうか。ここで$h
\simeq f(x)$の$f(x)$が直線的なふるまいとなると仮定して、三角形の相似を用いることとしました。(下図)

period.png

このように考えれば、$x_h$はデータ点$(x_i,y_i), (x_{i+1},y_{i+1})$から、

x_h = x_i + (x_{i+1}-x_i) \times \frac{|y_i - h|}{|y_i - h|+|y_{i+1}-h|}

と求めることができるでしょう。

プログラムの作製

 プログラムは以下のようになりました。$x_h$が3つ未満であればエラー文を返すのと、それ以上であれば各点から周期を計算して平均化し返すようにしました。使用したパッケージはnumpyだけです。

'''
周期的なふるまいの計算結果list_xとlist_yと
閾値thresholdを与えることで周期を返す関数
'''
import numpy as np

def period_checker(list_x,list_y,threshold):
    #xhを格納するためのリスト
    list_x_zero = np.array([])

    #xhを計算するためのデータ点を選別。thresholdから±0.5のyの値のみ。
    list_y_check = list_y[abs(list_y - threshold) < 0.5]-threshold
    list_x_check = list_x[abs(list_y - threshold) < 0.5]

    #選別したデータから、thresholdを横切る時タイミングを確認しxhを計算・記録。
    for i in range(list_y_check.shape[0]-1):
        if list_y_check[i]*list_y_check[i+1]<0:
            ratio = abs(list_y_check[i])/(abs(list_y_check[i])+abs(list_y_check[i+1]))
            list_x_zero = np.append(list_x_zero, list_x_check[i]+ratio*(list_x_check[i+1]-list_x_check[i]))

    #記録されたxhが3未満であればエラー文を返す。
    if list_x_zero.shape[0] < 3:
        return print('please input valid interval \n')
    #xhが3以上であれば、それらより周期を求め平均化したのち、求めたxhと周期を表示
    else:
        periodT = 0
        Num_of_period = 0
        for j in range(list_x_zero.shape[0]-2):
            periodT += list_x_zero[j+2] - list_x_zero[j]
            Num_of_period += 1
        periodT = periodT/Num_of_period
        return print('x(y = {:.4f}) \n'.format(threhold),list_x_zero,'\n period : {:.18f} \n'.format(periodT))

プログラムのテスト

 周期関数の代表であるsin関数とcos関数でプログラムをテストしてみました。これらの周期の厳密解は$2 \pi$なので比較することでプログラムの正確性が測れるでしょう。
 例えば、$x$の範囲を$0$~$17$とし、その間を$5000$分割した時のsin関数とcos関数の周期を以下のように計算してみました。ついでにnumpyの組み込み関数を用いて$2 \pi$の値も表示しました。

In:
h = 0
list_angle = np.linspace(0,17,5000)
list_sin = np.sin(list_angle)+h
list_cos = np.cos(list_angle)+h

period_checker(list_angle,list_sin,h)
period_checker(list_angle,list_cos,h)

print(2*np.pi)
out:
x(y = 0.0000) 
 [ 3.14159265  6.28318531  9.42477796 12.56637061 15.70796327] 
 period : 6.283185307836515854 

x(y = 0.0000) 
 [ 1.57079633  4.71238898  7.85398163 10.99557429 14.13716694] 
 period : 6.283185307875393200 

6.283185307179586

 $x_h$がそれぞれ5つずつ記録されており、計算された周期が出力されました。また、組み込み関数の値と周期の値を比べると、それぞれの計算結果は$10^{-9}$の桁数まで一致していることが分かります。より分割数を増やすことでより高い精度の答えを求めることもできます。正確な結果を求めるには$h
\simeq f(x)$付近での十分なデータ数が必要なようです。

まとめ

 周期関数の周期を求めるプログラムを作製してみました。十分なデータ点があればよい精度が求められそうです。よい補間法と組み合わせることで少ないデータ点から周期を求めることもできるかもしれませんね。それは、また思いついたときに……。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PyTorchでRuntimeError: error executing torch_shm_managerが出た時の対象方法

Pytorch exampleのsuper resolutionのサンプルを動かそうとしたらエラーがでた。

$ pip install torch torchvision
$  python main.py --upscale_factor 3 --batchSize 4 --testBatchSize 100 --nEpochs 30 --lr 0.001

エラー内容

Traceback (most recent call last):
  File "main.py", line 81, in <module>
    train(epoch)
  File "main.py", line 48, in train
    for iteration, batch in enumerate(training_data_loader, 1):
  File "/Users/xxx/.pyenv/versions/3.7.4/lib/python3.7/site-packages/torch/utils/data/dataloader.py", line 819, in __next__
    return self._process_data(data)
  File "/Users/xxx/.pyenv/versions/3.7.4/lib/python3.7/site-packages/torch/utils/data/dataloader.py", line 846, in _process_data
    data.reraise()
  File "/Users/xxx/.pyenv/versions/3.7.4/lib/python3.7/site-packages/torch/_utils.py", line 385, in reraise
    raise self.exc_type(msg)
RuntimeError: Caught RuntimeError in DataLoader worker process 0.
Original Traceback (most recent call last):
  File "/Users/xxx/.pyenv/versions/3.7.4/lib/python3.7/site-packages/torch/utils/data/_utils/worker.py", line 178, in _worker_loop
    data = fetcher.fetch(index)
  File "/Users/xxx/.pyenv/versions/3.7.4/lib/python3.7/site-packages/torch/utils/data/_utils/fetch.py", line 47, in fetch
    return self.collate_fn(data)
  File "/Users/xxx/.pyenv/versions/3.7.4/lib/python3.7/site-packages/torch/utils/data/_utils/collate.py", line 79, in default_collate
    return [default_collate(samples) for samples in transposed]
  File "/Users/xxx/.pyenv/versions/3.7.4/lib/python3.7/site-packages/torch/utils/data/_utils/collate.py", line 79, in <listcomp>
    return [default_collate(samples) for samples in transposed]
  File "/Users/xxx/.pyenv/versions/3.7.4/lib/python3.7/site-packages/torch/utils/data/_utils/collate.py", line 53, in default_collate
    storage = elem.storage()._new_shared(numel)
  File "/Users/xxx/.pyenv/versions/3.7.4/lib/python3.7/site-packages/torch/storage.py", line 128, in _new_shared
    return cls._new_using_filename(size)
RuntimeError: error executing torch_shm_manager at "/Users/xxx/.pyenv/versions/3.7.4/lib/python3.7/site-packages/torch/bin/torch_shm_manager" at ../torch/lib/libshm/core.cpp:99

/Users/xxx/.pyenv/versions/3.7.4/lib/python3.7/site-packages/torch/bin/torch_shm_managerに実行権限がなかったので、以下のコマンドで実行権限を付与するとよい。

$  chmod +x /Users/xxx/.pyenv/versions/3.7.4/lib/python3.7/site-packages/torch/bin/torch_shm_manager
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Anacondaの仮想環境下でSpyderを使えるようにするまで (2019/10/20)

なんだかんだSpyderでの開発が捗るので、Anacondaの仮想環境でSpyderが使えるようにするまでのメモ。

環境

  • Windows 10 Home 64bit
  • anaconda 2019.10
  • python 3.7

手順

1. Anacondaをインストール

https://www.anaconda.com/distribution/
から最新版をインストール。
自分専用のPCにはAll Users向けにインストールするので
以降はC:\ProgramData\Anaconda3にインストールしたものとします。

2. Pathを通す

別にAnaconda Promptを使うならいらないが、たまーにターミナル上でpythonやpipをしたくなるため追加しておく。
Windowsの「システム環境変数の編集」→「環境変数」→「(システム環境変数の)PATH」を開いて以下2つを追加。

  • C:\ProgramData\Anaconda3
  • C:\ProgramData\Anaconda3\Scripts

3. Anaconda Prompt上で仮想環境を作成

windowsで「Anaconda Prompt (Anaconda3)」を開いて以下のコマンドを実行し仮想環境を作成する。

AnacondaPrompt
$ conda create -n <仮想環境名> python=3.7 anaconda

仮想環境が作れられたか確認する。

AnacondaPrompt
$ conda info -e

指定した仮想環境が作成されていたら

AnacondaPrompt
$ conda activate <仮想環境名>

で切り替え。

4. Spyderの起動

起動した仮想環境下で

AnacondaPrompt
$ spyder

とすれば起動する。
一度起動すれば、タスクバーにピン留めしておいてもいいし、Spyderと検索すれば「Spyder(仮想環境名)」と出てくるのでそれをクリックするだけで起動できるので、ターミナル上から指定しなくてよい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ロードセルを使って重さを測ってDBに格納したり、slackに通知したり。

1. 概要

ロードセルという重さをはかるセンサーを使って、
重さを図り、測った値をDBに格納し、必要だったらslackに通知する。

2. どうして作ったのか

日々、個数を数えたり、残量を見て発注しているものがある業種は多いと思います。
個数や残量ではなく、対象の重さを測って 発注する/しない を
判定できると楽でいいなと想像したのでプロトタイプを作りました。

3. 準備物

  • Raspberry Pi(ここではmodelB rev2とします。古い。。。) * 1個
    python3が使えるようにRapbianがセットアップ済みとします。
  • ロードセル シングルポイント(ビーム型) 10kg * 1個
    秋月電子のサイトで買えます。 P-13042で検索すると出てきます。
  • HX711使用 ロードセル用ADコンバータ モジュール基板 * 1個
    前述のRaspberry Pi と ロードセルとを接続するために使います。
    秋月電子のサイトで買えます。
    K-12370で検索すると出てきます。
    前提として、購入後に必要なはんだ付けは終わっているものとします。
  • ジャンパワイヤー(メスtoメス) * 3本
    前述のロードセル用ADコンバータとロードセルを接続するために使います。
    ぼくはスイッチサイエンスで買いました。

4. 作っていく

4.1. ロードセル と ロードセル用ADコンバータ を接続する。

ロードセルから出ている線を下記の画像のように接続します。
image.png

4.2. ロードセル用ADコンバータ と RaspberyPi を接続する

準備していたジャンパワイヤーを使って、ロードセル用ADコンバータと
RaspberryPi の GPIOピンに接続します。

  • ロードセル側の接続
    IMG_20190921_225103.jpg

  • RaspberryPi側の接続
    よく分かる画像を撮ってなかった。。。
    以下の画像のように接続します。
    絶対にここってわけではなくて、赤の電源と黒のGRD以外は、
    GPIOのピンアサインをみて他のところに刺してもOKです。

GPIO_1.png
GPIO_2.png

4.3. ロードセルで計測した値を取得するスクリプトを git cloneする

https://github.com/bogde/HX711

4.4. スクリプトを実行する

スクリプトを実行すると値が取れる。


あとは、pythonのスクリプトなので、psycopg2 とか使ったりして、
DBに格納したり、requestsとか使ったりしてslackに通知したりすればよいよい。

5. 完成形サンプル

sensor1.jpg
sensor2.jpg

6. TIPS

  • 正確なグラム数を取得するにはキャリブレーションが必要

7. その他

しきい値を作っても通知しても、実際発注するかどうかは暗黙知が必要だったりする。
なので、発注したかどうかを記録し目的変数とし、天候や販促キャンペーン、
店舗なら近隣のイベントの有無とか、重量以外の情報も説明変数として集積して、
発注するかどうかを重回帰とかで予測するのはどうだろうか。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【CTF】SECCON2019 writeup

SECCON 2019の解答
floretとして2人で参加
WelcomeとThank you for playing!含めて3問しか解けなかった。。。
時間外だけど12分オーバーでBeeeeeeeeeerも解けたので一応載せときます。

[crypto]coffee_break

Point: 50 (384solves)

問題文

The program "encrypt.py" gets one string argument and outputs ciphertext.

Example:

$ python encrypt.py "test_text"
gYYpbhlXwuM59PtV1qctnQ==

The following text is ciphertext with "encrypt.py".
FyRyZNBO2MG6ncd3hEkC/yeYKUseI/CxYoZiIeV2fe/Jmtwx+WbWmU1gtMX9m905

Please download "encrypt.py" from the following url.

encrypt.py

import sys
from Crypto.Cipher import AES
import base64


def encrypt(key, text):
    s = ''
    for i in range(len(text)):
        s += chr((((ord(text[i]) - 0x20) + (ord(key[i % len(key)]) - 0x20)) % (0x7e - 0x20 + 1)) + 0x20)
    return s


key1 = "SECCON"
key2 = "seccon2019"
text = sys.argv[1]

enc1 = encrypt(key1, text)
cipher = AES.new(key2 + chr(0x00) * (16 - (len(key2) % 16)), AES.MODE_ECB)
p = 16 - (len(enc1) % 16)
enc2 = cipher.encrypt(enc1 + chr(p) * p)
print(base64.b64encode(enc2).decode('ascii'))

解法

AESの鍵は分かっているので暗号文を復号してenc1を求める。
それを実際にencrypt.pyのenc1と比較すると

aaaaaaa.png

一致しているのが分かるので後は一文字ずつ探索するプログラムを書けば終わり。

import sys
import base64
from Crypto.Cipher import AES
# For debugging
from IPython import embed
from IPython.terminal.embed import InteractiveShellEmbed

key1 = "SECCON"
key2 = "seccon2019"
text = sys.argv[1]

def encrypt(key, text):
    s = ''
    for i in range(len(text)):
        s += chr((((ord(text[i]) - 0x20) + (ord(key[i % len(key)]) - 0x20)) % (0x7e - 0x20 + 1)) + 0x20)
    return s

def decrypt(text):
    cipher_data = base64.b64decode(text)
    crypto = AES.new(key2 + chr(0x00) * (16 - (len(key2) % 16)), AES.MODE_ECB)
    enc2 = crypto.decrypt(cipher_data)
    enc1 = enc2.split()[0].decode('ascii')
    # Brute-force attack
    flag = ''
    for i in range(len(enc1)):
        for j in range(32,128):
            estimate_of_enc1 = encrypt(key1, flag + chr(j))
            if(enc1[:i+1] == estimate_of_enc1):
                flag += chr(j)
                break
    return flag

flag = decrypt(text)
print(flag)

これでflagゲット

$ python decrypt.py FyRyZNBO2MG6ncd3hEkC/yeYKUseI/CxYoZiIeV2fe/Jmtwx+WbWmU1gtMX9m905
SECCON{Success_Decryption_Yeah_Yeah_SECCON}

[Misc]Beeeeeeeeeer

Point: 110 (182solves)

問題文

Let's decode!

解法

fileコマンドで確認すると、でかめのASCIIファイルであることが分かる。
中身を見るとshellっぽかったので、とりあえず先頭に
#bin/bashをつけて実行するとEとFがアニメーション風に表示され結果的に以下のようになる

image.png

shellは一行で書かれていて中身が見づらいので改行を入れる

f = open("入力ファイル名")
fout = open("出力ファイル名", "w")
fout.write(f.read().replace(";", "\n"))
f.close()
fout.close()

すると中に異常に長いbase64の行があるのでその部分を抜き出して実行すると
beep音が1~10回鳴ってその回数を入力するよう求められる。
ある程度入力すると以下の画面となる

image.png

次に異常に長い行の| bash;部分を消して実行するとbashに渡される前のプログラムが表示される

プログラム冒頭の$((RANDOM % 10 +1));からbeep音が1~10回なることが予想でき

base64で書かれた部分をデコードして

ZWNobyAtbmUgJ1xhJztzbGVlcCAxO2VjaG8gLW5lICdcYSc7c2xlZXAgMTtlY2hvIC1uZSAnXGEnO3NsZWVwIDE7ZWNobyAiSG93IG1hbnkgYmVlcHM/IjsK|
↓
echo -ne 'a';sleep 1;echo -ne 'a';sleep 1;echo -ne 'a';sleep 1;echo "How many beeps?";

上記から最後のbeep音回数が3回で固定されていることが分かる。

そして以下の$nが3であることも分かるので

$(echo -n $n|md5sum |cut -c2,3,5,12) -md md5 2>/dev/null | bash;
↓
$(echo -n 3|md5sum |cut -c2,3,5,12) -md md5 2>/dev/null;

と書き換えてshellとして実行すると

__=$(. 2>&1);__=${__##*.};__=$(. 2>&1);__=${__##*.};${__:$(($[($[$$/$$]<<$[$$/$$]<<$[$$/$$]<<$[$$/$$])+$[$$/$$]]+$[($[$$/$$]<<$[$$/$$]<<$[$$/$$]<<$[$$/$$])+$[$$/$$]]+$[$$/$$])):$((___=___^___||++___))}${__:$[$[$$/$$]<<$[$$/$$]<<$[$$/$$]]:$((___=___^___||++___))}${__:$(($[($[$$/$$]<<$[$$/$$]<<$[$$/$$]<<$[$$/$$])+$[$$/$$]]+$[($[$$/$$]<<$[$$/$$]<<$[$$/$$]<<$[$$/$$])+$[$$/$$]])):$((___=___^___||++___))} -- {z..A};${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))} "${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=____^____||++____))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))-$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))} ${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))-$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))} ${@:$((____=____^____||++____))$((____=____^____||++____)):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}";${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))} _____</${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}/${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))-$((____=____^____||++____)))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))-$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))};: ${@:$((____=____^____||++____))$((____=____^____||++____)):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))} ${@:$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))} ${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))};${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))} $(${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))} -${@:$((____=____^____||++____))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))} $_____|${@:$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}|${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))-$((____=____^____||++____)))):$((____=____^____||++____))} -${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}" " -${@:$((____=(____^____||++____)+(____^____||++____)))$((____=____^____||++____)):$((____=____^____||++____))}$((____=____^____||++____)))|${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))-$((____=____^____||++____)))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=____^____||++____)):$((____=____^____||++____))} -${@:$((____=____^____||++____))$(($((____=____^____||++____))-$((____=____^____||++____)))):$((____=____^____||++____))} "${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))-$((____=____^____||++____))))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))))${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))))${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))))$(($((____=____^____||++____))-$((____=____^____||++____))))${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))))$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))-$((____=____^____||++____))))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))$((____=____^____||++____))${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))))${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))"&&${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))} "${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))} ${@:$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}!"&&printf "\n\033[?7l%1024s" " "&&echo SECCON{$S1$n$_____};echo -e '\033[?7h';

難読化されたshellがを見ることができる。

行末に

echo SECCON{$S1$n$_____};

とあり、$S1$n$_____が分かればflagが取得できると分かる

最初は愚直にプログラムを読もうとしたが調べたところ

bash -x

で難読化に関係なく最終的に評価されたコマンドが見れると分かり、
難読化されたshellを別ファイルに移し行頭に#bin/bashをつけshellを生成

bash -x hogehoge.sh

で実行すると$_____がpasswordと同じであることが分かった。
passwordが「bash」であることが分かった。(readはエンターで通過)

image.png

次は同様のコマンド実行時にread部分でbashと入力

image.png

しかしこの状態では$_____しか分かってないのでまだflagではない。

ここでプログラムが結果的に3つに分かれたのを思い出し、それぞれのファイルで定義されている部分を調査。

$nは既に3だと分かっているので残りは$S1

$_____は3つ目、$nは2つ目のファイルから分かったので

1つ目のファイルを確認すると

$'\u0053\u0031'=$(echo aG9nZWZ1Z2EK |base64 -d);という部分を発見

base64っぽい部分があったので複合して

aG9nZWZ1Z2EK
↓
hogefuga

SECCON{$S1$n$_____}に各変数の値を代入してflagゲット!

SECCON{hogefuga3bash}

感想など

4問解いたけど1問は時間オーバーだし、2問は答えそのまま。
実質1問しか解けてなくて悔しい( ;∀;)

参考にしたサイト

AES
https://qiita.com/Azunyan1111/items/01a4df7a1c162dbb6f08
bash -x
https://raintrees.net/projects/a-painter-and-a-black-cat/wiki/CTF_Writeup_HamaCTF

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GCPのサーバレスで機械学習FX売買サービスを実現

こんにちは、@THERE2 です。

学習済みの機械学習モデルを使ってFXの自動売買を行うサービスを、GCPのサーバレス環境で実現してみました。

VM(Compute Engine)を使えばローカル環境と同じように運用できるのですが、VMはずっと起動させておくとそれなりに費用がかかってしまいます。
時間起動するFX自動売買であれば、実行時間に応じて課金されるCloud Functionsの方が安く運用でき、最先端のクラウド技術にも触れられるという事で自分でやってみる事にしました。

FX自動売買の機能要件

今回のFX自動売買の機能要件と処理の流れは次のようになります。
物理ファイルや外部API連携、DBアクセスも必要となり、それなりに幅広くクラウドの機能を使う必要がありました。

Oanda APIを使った売買については、先日の記事を参考にして頂ければ幸いです。

  • 月〜金の決まった時間(まずは1日1回、13時)に起動する。
  • Oanda APIを使ってUSD_JPYの最新の価格を取得する。
  • データの前処理を行う。(PandasのDataFrame利用)
  • FXのテクニカル指標を計算する。(TA-Lib利用)
  • 機械学習の学習済みモデルをロードする。(今回はLightGBMのモデル)
  • 学習済みモデルを使って将来の価格が上がるか下がるかの予測を行う。
  • 予測結果に応じて、Oanda APIを使って買い注文または売り注文を行う。
  • 予測結果、注文実施内容をおって分析するためにDBに格納する。
  • 外部から勝手に実行されないように、外から認証無しでは実行できないようにする。

これらの事が、Compute EngineではなくサーバレスのCloud Functionsで実現できるのか心配でしたが、結果的にはGCPの各サービスを活用する事で問題なくできました。
苦労もありましたが、一回実装すれば次からは容易に対応できると思います。

GCPで利用したサービス

上記の機能要件を満たすためには、Cloud Functions以外にもいくつかのサービスを組み合わせる必要がありました。

GCPのクラウドサービス 利用用途
Cloud Storage 機械学習の学習済みモデルを置いておきます。
Cloud Firestore ドキュメントDBです。のちのちの解析用に予測結果、注文実施内容を保存しておきます。
Cloud Scheduler cronの時間起動でイベントを登録するためのサービスです。
Pub/Sub Cloud Schedulerからのイベントを元にCloud Functionsを呼び出すサービスです。
Cloud Functions イベントトリガーでPythonの関数を1回実行します。関数の中身だけ実装すればよいという事で、今回のサーバレスの主役です。
IAMと管理 DBやストレージの認証を行うためのサービスです。

今回はFX予測して売買するためのシンプルな処理ですが、クラウド関数、クラウドストレージ、クラウドDB、クラウドスケジューラー、クラウド認証と幅広くサービスを活用する事になり、大変勉強になりました。

これだけの事を物理的にサーバを立てるところから実施したらかなり大変ですが、GCPでは簡単に実現でき、本当にサービス開発が容易になってきているなと感じました。

各サービスの詳細

今回の実装概要をまとめておきたいと思います。

Cloud Storage

Cloud Functionsで機械学習の学習済みモデルを読み込むためのStorageをセットアップしました。
バケットに名前を付けて保存します。
アクセス制御については、ファイル単位の制御は必要ないので、バケットレベルとしました。
image.png

バケット作成後に、ローカルの機械学習の学習済みモデルをアップロードしておきました。
「ファイルのアップロード」ボタンからローカルのファイルをサクッとアップロードできます。

image.png

Cloud Firestore

処理結果のログをドキュメントDBに残していきたかったので、Firestore DBをセットアップしました。
利用開始には難しい事は何もなく、頭を悩ますような設定もありません。
スキーマレスなのでテーブルの定義、型定義など何もなく、コレクション名を決めるぐらいです。
これで自動でスケールされるのですから、DB管理者は不要ですね。

以下はすでにCloud Functionsからデータを投入済みの状態です。

image.png

Cloud Pub/Sub

Cloud FuncitonsをHTTPトリガーにしてしまうと、外部公開されてしまうということで、Pub/Subを挟んで呼び出す事にしました。
これを使うと一時的に大量のリクエストが発生してもこのキューに溜め込んでCloud Functionsが順に処理できるようになります。
また、Cloud FunctionsをHTTPで外部公開する必要がないため、セキュリティ的にも安心です。

作成はPub/Subの「トピックの作成」からトピックIDを入力して作成すれば完了です。

image.png

Cloud Scheduler

今回のFX自動売買は、月〜金で1日1回、13時に実行することにしました。
cronと同じ形式で実行タイミングをセットできます。
実行時間はどのタイミングで実行するかのタイムゾーンを指定し、ターゲットは上記で指定したPub/Subのfx_topicを指定しました。
ペイロードはいまいちよく分かっていませんが、固定パラメータを渡せるようです。
これで時間になるとPub/Subにトリガーが送られます。

image.png

ジョブを作成した後は、実際にその時間にならなくても、「今すぐ実行」ボタンで実行できるのが便利です。

image.png

Cloud Functions

ここまでで必要な機能が揃ってきたので、いよいよCloud Functionsを実装していきます。

  • メモリは256 MBも割り当てておけば十分かと思います。
  • トリガーにはPub/Subを、トピックにSchedulerと設定したものと同じトピックをセットします。これで、Schedulerにて登録されたトリガーをCloud Functionsが検知してトリガー毎に1回処理できるようになります。
  • ランタイムは「Python 3.7」が選択できます。

Cloud Functionsの主役はNode.jsのようで、Googleのドキュメントではjsのサンプルコードはすぐに見つかるのですがPythonはあまりなく、ところどころ苦労がありました。
ただ、機械学習のライブラリやPandasを使うためにはPythonがないと困りますね。
なお、現時点ではGolangは選択できますが、.netやjavaは選択できないようです。

image.png

ソースコードとしては、main.pyrequirements.txtのみを編集します。
main.pyでは複数の関数を定義できますが、呼び出す関数を一つ定義しておく必要があります。
デフォルトでは、hello_pubsubを呼び出すようになっており、引数にevent, contextを受けるようになっています。

image.png

requirements.txtの内容

ここに必要なpythonパッケージを記入しておくと、デプロイ時に反映してくれます。(pip installが実行されるようです)
Cloud Storageを利用する場合は、google-cloud-storage、firestoreを利用する場合はgoogle-cloud-firestoreを記入しておく必要があります。
pandasnumpyはデータサイエンスには欠かせないライブラリですね。
最強の機械学習アルゴリズムのlightgbmも問題ないくinstallしてくれました。
ta-libはバイナリがなくてimportエラーになり詰んだ、、と思ったのですが、talib-binaryの方は問題なくimportできて助かりました。

# Function dependencies, for example:
# package>=version
pandas
numpy
lightgbm
oandapyV20
talib-binary
google-cloud-storage
google-cloud-firestore
jpholiday

main.pyの内容

ライブラリのインポート、DB、Storageの利用準備

まずは利用する必要なライブラリをimportします。

import base64
import requests
import json
import datetime
import jpholiday
from pandas.tseries.holiday import USFederalHolidayCalendar as uscalendar
import talib
import pandas as pd
import numpy as np
import lightgbm as lgb

Cloud Storage、Cloud Firestoreにアクセスするための準備です。
次のようにclientを設定しておくことで簡単に扱う事ができます。

from google.cloud import storage
storage_client = storage.Client()

from google.cloud import firestore 
db = firestore.Client()

価格データの取得、前処理の実施

続いて最新の価格データを取得して前処理を行う部分ですが、今回の記事の範囲を超えるため詳細を割愛します。

#1. 最新データの取得
def get_candledata_from_oanda():
    # oandaからデータを取得してpandas DataFrameに格納する処理。
    # 今回の記事の範囲を超えるため、詳細は割愛。
    return df

# 2. データ加工
def preprocess_data(df):
    # 前処理の実施。
    # 日付を加工したり、価格を前日比にしたり、テクニカル指標を追加したり。
    # 今回の記事の範囲を超えるため、詳細は割愛。

    return df

機械学習モデルによる予測の実行

ここで、Cloud Storageに保存しておいた学習済みのモデルをblobとして取り出し、いったん/tmpに保存します。
/tmpはメモリ上のCloud Functionsの一時ファイル保存用で、実行が終わると消えてしまいますが、今回のように一度Cloud Storageから取り出したファイルを置いておくのに便利です。
LightGBMのBoosterにそのモデルファイルを読み込ませ、LightGBMで予測を行います。

#3. 予測の実行
def run_prediction(df):
    bucket = storage_client.get_bucket('there2_ml_model')
    blob = bucket.get_blob('lightgbm_model_v1.mdl')

    blob.download_to_filename("/tmp/tmpmodel.mdl")

    # lightGBMのトレーニング済みのモデルの読み込み
    clf = lgb.Booster(model_file="/tmp/tmpmodel.mdl") 

    # 予測の実行
    y_pred = clf.predict(df, num_iteration=clf.best_iteration) 

    return y_pred

予測結果を元に注文を指示し、予測結果と注文結果をドキュメントDBに格納

上記までの予測結果を元に、売買の注文を行います。
Oanda APIを使ってpythonから直接売買の指示を出すことが出来ます。

具体的なやり方は先日の記事に記載しました。
機械学習でFX:Oanda APIを使ってPythonから自動売買する

その後、ドキュメントDBにJSON形式で内容をセットすればDBに格納されます。簡単過ぎますね・・

# 4. 注文の実施
def execute_transaction(y_pred):

    # 予測結果を元に売り買いの指示を出します。
    # 今回の記事の範囲を超えるため、詳細は割愛。
    # ログをDBに出力
    doc_ref = db.collection(u'cloud_function_log').document()
    doc_ref.set({
        u'datetime' : firestore.SERVER_TIMESTAMP, 
        u'y_pred' : y_pred[0].astype(np.float),
        u'order' : {
            u'accountBalance' : 3000000,
            u'accountID' : u'dummy3',
            u'instrument' : u'USD_JPY',
            u'orderID' : u'99999',
            u'price' : 110.222,
            u'reason' : u'MARKET_ORDER',
            u'units' : 10000
        }
    })
    return "OK"

全体の実行

hello_pubsubが最初に呼ばれるようになっていますので、そこからmain_processを呼び、main_processが各関数を順に呼ぶようにしています。

def main_process():
    df = get_candledata_from_oanda()
    df = preprocess_data(df)
    y_pred = run_prediction(df)
    oanda_res = execute_transaction(y_pred)

    return "OK"

def hello_pubsub(event, context):
    """Triggered from a message on a Cloud Pub/Sub topic.
    Args:
         event (dict): Event payload.
         context (google.cloud.functions.Context): Metadata for the event.
    """
    res = main_process()
    print(res)

実装が完了したら、Pub/Subからトリガーしなくても「テスト」タブから関数を実行して試してみる事ができます。
エラーになった場合はログ表示からエラーの内容を確認できます。

image.png

IAMと管理

もしかしたら必要なかったのかもしれませんが、cloud functionsがstorageを参照できるような権限を設定しました。
まず、cloud functionsの概要から「サービスアカウント」を確認します。

image.png

cloud storageのバケットの詳細から上記の「サービスカウント」をメンバーとして追加し、ストレージの閲覧者、作成者の権限を付与しました。

image.png

まとめ

以上でGCPのサービスをフル活用して最小限のコーディングで機械学習の自動売買サービスを作成する事ができました。
費用もこれぐらいの使い方であれば月に数ドルで収まるように思います。
1年で無料枠を使い切ることもないでしょう。

いままでインフラ担当者とか基盤担当者が苦労して構築してきた事が、クラウドサービスを使えばほぼ問題なく実現できてしまうのは、凄くいい時代になりましたね。
もちろん大規模なサーバ運用ではそうもいかないもかもしれませんが、スモールサービスの構築であればワードプレスぐらいのノリで労力かけずにそれなりの物が作れてしまえる、という印象を受けました。

今後は、IT技術者がシステムの運用よりも、IT知識を活かして新しいサービスを生み出していく事が求められていくのかもしれませんね。

@THERE2

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ABC143参戦記

はじめに

ここのところサボってしまっていましたが、今回初の新ABC全完ということで気分がいいので久しぶりに参戦記を書きたいと思います。

A

問題文が微妙にわかりずらかったですが、要はA-2Bです。隙間の長さはふにならないことに注意すれば問題ないでしょう

A, B = map(int, input().split())
print(max(A-B*2, 0))

B

全探索すれば通りますが、O(N^2)です。和の二乗から各たこ焼きの美味しさの二乗を引いて二で割ることでも求められO(N)です。無駄な高速化はしないようにしていますが、今回は自明すぎたのでしてしまいました。

N = int(input())
d = list(map(int, input().split()))
s = sum(d)
ans = s**2
for i in range(N):
    ans-=d[i]**2
print(ans//2)

C

隣り合う要素が異なるような場所の数+1が答えです

N = int(input())
S = input()
ans = 1
now = S[0]
for i in range(1, N):
    if now == S[i]:
        continue
    now = S[i]
    ans += 1
print(ans)

D

こういういくつも要素が動く系の数え上げはいくつかを固定してあげるとうまくいくことがあります。今回、三角形に使う棒の長さをa<=b<=cとして、b, cを固定すると、a>c-bとなるaの数え上げに帰着します。単調性から、二分探索でlognで求められるので、全体ではO(N^2logN)です。pypyでN^2logNがN=300で通るかどうか不安でしたが、約800msで通りました

from bisect import bisect_right
N = int(input())
L = list(map(int, input().split()))
L.sort()
ans = 0
for i in range(N):
    for j in range(i):
        m = L[i]-L[j]
        k = bisect_right(L, m)
        n = max(0, j-k)
        ans+=n
print(ans)

実は、L<10^3を利用すると、二分探索をしなくても大丈夫です。
各0<l<10^3についてその長さの棒の数を管理すると、上の二分探索パートがO(1)の処理に置き換えられます。

E

まず、給油をすると、必ず燃料はLリットルになるので、移動方法として最善なのは、Lリットルで行けるところまで行って、ギリギリのところで給油することを繰り返すことです。そう考えると、ある頂点から距離L以内の頂点を見つけると良さそうです。
Q<=N(N-1)/2から、最悪の場合、あらゆる頂点の組み合わせについて考える必要があるので、ワーシャルフロイドが使えそうだなぁという気持ちになります。
そうして、Lリットルの燃料で到達できる頂点を見つけた後はその頂点に向けて長さ1の辺を張ったグラフを考え、その最短経路を求めれば答えが出ます。はじめ前半のパートが幅優先探索で十分と考えてしまい3WA出してしまいました...。BFSは辺の長さが全て1の時のみ、最短距離を正しく出してくれるので、逆に後半パートには使えます。
計算量はO(N**3)ですがN<=300なのでpypyで間に合うか不安で、実際1700msとかなりギリギリでした。ワーシャルフロイドは単純なループなのでまあまあ早かったっぽいですね。

inf = 10**10
def warshall_floyd(d):
    for i in range(N):
        for a in range(N):
            for b in range(N):
                d[a][b] = min(d[a][b], d[a][i]+ d[i][b])#頂点iを使う場合と使わない場合
    return d
N, M, L = map(int, input().split())
G = [[inf]*N for _ in range(N)]
for i in range(N):
    G[i][i] = 0
for _ in range(M):
    a, b, c = map(int, input().split())
    G[a-1][b-1] = c
    G[b-1][a-1] = c
G = warshall_floyd(G)
G2 = [[0]*N for _ in range(N)]
for i in range(N):
    for j in range(N):
        if i == j:
            G2[i][j] = 0
        elif G[i][j]<=L:
            G2[i][j] = 1
        else:
            G2[i][j] = inf
d = warshall_floyd(G2)
Q = int(input())
for _ in range(Q):
    s, t = map(int, input().split())
    ans = d[s-1][t-1]-1
    print(ans if ans < 10**9 else -1)

実は、ダイクストラを改造することでもできます。距離だけでなく、何回給油が必要かを持っておくことで正しい答えを得ることができます。

inf = 10**10
def dijkstra(s):
    d = [(inf, 1)] * N
    used = [False] * N
    d[s] = (0, -L)  
    for i in range(N):
        v = -1
        for i in range(N):
            if used[i]:
                continue
            if v==-1:
                v = i
            elif d[v]>d[i]:
                v = i
        if v == -1:
            break
        used[v] = True   
        m, r = d[v]
        r = -r
        for nv in range(N):
            c = cost[v][nv]
            if r>=c:
                d[nv] = min(d[nv],(m, -(r-c)))
            elif c<=L:
                d[nv] = min(d[nv], (m+1, -(L-c)))
    return d
N, M, L = map(int, input().split())
cost = [[inf]*N for _ in range(N)]
for _ in range(M):
    a, b, c = map(int, input().split())
    cost[a-1][b-1] = c
    cost[b-1][a-1] = c
ans = []
for i in range(N):
    ans.append(dijkstra(i))
Q = int(input())
for _ in range(Q):
    a, b = map(int, input().split())
    res = ans[a-1][b-1][0]
    print(res if res != inf else -1)

ちなみにダイクストラには何種類かあって、O(MlogN)とO(N^2)がありますが、今回、最悪でM ~ N^2なので後者を採択しないと(少なくともpythonでは)死にます。

F

N<3*10**5なので各k<NについてO(logN)くらいで求められれば間に合いそうです(pythonだと間に合わないけど)。取り除ける最大の回数を求めるのは難しそうですが、取り覗ける回数にはn回取り除けるならn-1回取り除けるという単調性があるので、二分探索が使えそうです。二分探索をするにはn回取り除けるかどうかが高速に判定できる必要があります。n回で取り除けるのは同じ数字からn個までなので、同じ数字からn個取った時に、その総数がn*kより大きければ、可能、そうでなければ不可能と判定できます。同じ数字からn個取った時の総数は前計算が可能なので、判定はO(1)で行えるので全体ではO(NlogN)です。

from collections import Counter
N = int(input())
A = list(map(int, input().split()))
C = Counter(A)
C = list(C.values())
C.sort()
n = len(C)
m = max(C)
S = [0]*(m+1)
b = 0
for i in range(n):
    c = C[i]
    if b<c:
        for j in range(b+1, c+1):
            S[j] = S[j-1]+(n-i)
        b = c
for i in range(1, N+1):
    ok = 0
    ng = N+1
    while ng-ok>1:
        mid = (ok+ng)//2
        if S[min(m, mid)]>=mid*i:#midが条件を満たすか
            ok = mid
        else:
            ng = mid
    print(ok)

時間がなさすぎたのでかなりのクソコードになっています。
解説を読むと、もう少し賢く、今回の問題とは逆のn回取り出せる最大の長さk = f(n)を求めてから元の問題を解くということをしています。やはり今回のポイントは、取り出す回数nを固定すると楽になることでした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

多次元配列のインデックスの順番

はじめに

Juliaには多次元配列が用意されているが、CやJava育ちの人間からするとインデックスの付け方がとてもわかりにくいのでまとめてみた。

Javaの場合

Javaには多次元配列はなく、配列の配列が書けるだけだ。3次元の配列(というか配列の配列の配列)は
次のように書く。

int [][][] a = new int[2][3][4];

この場合、最内周になるのは一番右のインデックス(この場合は長さ4のもの)だ。

Cの場合

Cでは多次元配列も配列の配列も書ける。宣言の仕方は全然違うのだけど、使い方はおなじなので大変紛らわしい。

まず配列の配列。宣言はポインタのポインタのポインタとして定義して、mallocで中身を個別に確保する。面倒くさい。

  char ***array2;
  int counter = 0;
  array2 = (char ***) malloc(sizeof(char**) * 2);
  for (int i = 0; i < 2; i++) {
    array2[i] = (char **)malloc(sizeof(char*) * 3);
    for (int j = 0; j < 3; j++) {
      array2[i][j] = (char *)malloc(sizeof(char) * 4);
      for (int k = 0; k < 4; k++) {
        array2[i][j][k] = counter++;
      }
    }
  }

  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
      for (int k = 0; k < 4; k++) {
        printf("%d ", array2[i][j][k]);
      }
      printf("\n");
    }
    printf("\n");
  }

mallocの返り値チェックしていないとかは気にしないでほしい。

多次元配列の場合は、もう少し楽。メモリ領域が連続しているので一括で確保できる。注目してほしいのは出力部分でのアクセスの仕方が全く同じ形array[i][j][k]になっていること。

  char array[2][3][4];

  char * v =(char*) array;
  for (int i = 0; i < 24; i++)
    *v++ = i;

  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
      for (int k = 0; k < 4; k++) {
        printf("%d ", array[i][j][k]);
      }
      printf("\n");
    }
    printf("\n");
  }

出力はいずれの場合も

0 1 2 3 
4 5 6 7 
8 9 10 11 

12 13 14 15 
16 17 18 19 
20 21 22 23 

となる。いずれにしろ、最内周に対応するのは、1番右側のインデックスだ。

Python numpyの場合

numpy の場合もCやJavaと同じ。つまり最内周のインデックスは一番右。

>>> np.arange(0, 24).reshape(2,3,4)
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])

Juliaの場合

これがJuliaだと逆になる。つまり最内周に対応するのは、一番左のインデックスだ。全く同じような感じでreshapeしても結果がnumpyの場合と全く違うのがわかる。

julia> reshape(0:23, (2, 3, 4))
2×3×4 reshape(::UnitRange{Int64}, 2, 3, 4) with eltype Int64:
[:, :, 1] =
 0  2  4
 1  3  5

[:, :, 2] =
 6  8  10
 7  9  11

[:, :, 3] =
 12  14  16
 13  15  17

[:, :, 4] =
 18  20  22
 19  21  23

また、2次元配列を書く際の順番もカラム優先になっていることがわかる。

JuliaはPyCallでPythonのライブラリが呼び出せるのだけど、その時の値の受け渡しで、いろいろ面倒なことが起きそうな気がする。。

所感

多次元配列の場合には、どのインデックスを内周にするのかは言語設計者が任意に決められそうなのだけど、配列の配列を書く場合には、必然的に後ろ(右側)のインデックスが内周にならざるを得ない。

Juliaで最初のインデックスが内周になっているのはFortranの伝統なのだろうと思うのだけど、なんでFortranはそうしたんだろう。なにか必然性があるのか、ただ偶然そうしただけなのか。偶然だとするとかなり不幸な偶然だな。。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RC直列回路の過渡現象を sympy で解析、matplotlib で描画

概要

RC直列回路の過渡現象を sympy で解析して(=回路方程式(微分方程式)を sympy で解析的に解いて)、その結果を matplotlib で描画します。

$$ E = R\cdot \frac{d}{dt}q(t) + \frac{1}{C}q(t) $$

上記の回路方程式から、以下を求めて

$$ V_R(t) = E e^{- \frac{t}{C R}} $$ $$ V_C(t) = E \left(1 - e^{- \frac{t}{C R}}\right) $$

具体的なパラメータを与えてグラフに描画するところまでの内容です。
fig.png

説明

RC直列回路の回路方程式は、次のようになります。$R$ が抵抗値、$C$ がコンデンサの静電容量、$E$ が電源電圧、$i(t)$ が電流です。

$$ E = R\cdot i(t) + \frac{1}{C}\int_{0}^{t}i(t)\,dt $$

sympy では積分項は扱いにくいので、次式のような電流 $i(t)$ と 電荷 $q(t)$ の関係を用いて微分の式に書き換えます。
$$ i(t) = \frac{d}{dt}q(t) $$

次のような微分方程式になりました。

$$ E = R\cdot \frac{d}{dt}q(t) + \frac{1}{C}\cdot q(t) $$

この方程式を $\rm{Eq.(1)}$ として sympy で定義します。シンボル $C1$ の意味については後ほど。

import sympy
import numpy as np
import matplotlib.pyplot as plt

# シンボル(変数記号)の定義
q,i = sympy.symbols('q i', cls=sympy.Function)
t,E,R,C,C1 = sympy.symbols('t E R C C1')

# 方程式 eq1 を立てる
eq1 = sympy.Eq( E, R * sympy.Derivative(q(t), t) + q(t)/C )
display(eq1)
sympy.latex(eq1) 

実行結果
$$ E = R \frac{d}{d t} q{\left (t \right )} + \frac{1}{C} q{\left (t \right )} $$

この微分方程式を $q(t)$ について解きます。sympy で微分方程式を解くときは sympy.dsolve() を利用します。

$\rm{Eq.(1)}$ を、電荷 $q(t)$ について解いたものを $\rm{Eq.(2)}$ とします。

eq2 = sympy.dsolve(eq1, q(t))
display(eq2)
print(sympy.latex(eq2)) 

実行結果
$$ q{\left (t \right )} = C E + e^{\frac{1}{R} \left(C_{1} - \frac{t}{C}\right)} $$

ここで、$C_1$ は積分定数となります。時刻 $0$ でコンデンサに蓄えられている電荷はゼロであるという初期条件 $q(0)=0$ を利用して $C_1$ を求めます。

つまり、次の方程式 $\rm{Eq.(3)}$ を $C_1$ について解きます。

$$ 0 = CE + \exp({\frac{C_1}{R}}) $$
​これは普通の方程式なので sympy.solve() で解きます。

# eq2の左辺を0、右辺にt=0を代入
eq3 = sympy.Eq(0, eq2.rhs.subs(t,0),)  
C1a = sympy.solve(eq3,C1)[0]
display(C1a)
print(sympy.latex(C1a)) 

実行結果
$$ R \log{\left (- C E \right )} $$

求められた積分定数を $\rm{Eq.(2)}$ に代入して式を整理します。これを $\rm{Eq.(4)}$ とします。

# 初期条件を与えて求めた積分定数C1aを代入
eq4 = eq2.subs(C1,C1a)
display(eq4) 
print(sympy.latex(eq4)) 

# 式を整理(展開)
eq4 = eq4.expand()
display(eq4) 
print(sympy.latex(eq4)) 

実行結果
$$ q{\left (t \right )} = C E + e^{\frac{1}{R} \left(R \log{\left (- C E \right )} - \frac{t}{C}\right)} $$

$$ q{\left (t \right )} = C E - C E e^{- \frac{t}{C R}} $$

電荷 $q(t)$ を微分して 電流 $i(t)$ を求めます。この電流 $i(t)$ についての式を $\rm{Eq.(5)}$ とします。

# eq4 の右辺を t 微分
eq5 = sympy.Eq( i(t), sympy.diff(eq4.rhs,t))
display( eq5 )
print(sympy.latex(eq5)) 

実行結果
$$ i{\left (t \right )} = \frac{E}{R} e^{- \frac{t}{C R}} $$

電流 $i(t)$ を使って、抵抗の両端電圧 $V_R$ を求めます( $V_R=R\cdot i(t)$ )。また、コンデンサの両端電圧 $V_c$ を求めます( $V_C = E - V_R$ )

i = eq5.rhs

# 抵抗両端の電圧 VR 
VR = R*i
display(VR)

# コンデンサ両端の電圧 VC
VC = E - VR
display(VC)
display(VC.collect(E)) # 電圧Eで括る 

実行結果

$$ E e^{- \frac{t}{C R}} $$ $$ E - E e^{- \frac{t}{C R}}$$ $$ E \left(1 - e^{- \frac{t}{C R}}\right) $$

抵抗の両端電圧 $V_R$、コンデンサの両端電圧 $V_c$ が次のように求まりました。

$$ V_R(t) = E e^{- \frac{t}{C R}} $$ $$ V_C(t) = E \left(1 - e^{- \frac{t}{C R}}\right) $$

具体的な電源電圧 $E$、抵抗値 $R$、静電容量 $C$ を与えて、過渡現象をグラフを描画していきます。今回は、$E=5\, (t\ge0)$、$R=50 \rm{k}\Omega$、$R=40 \mu\rm{F}$ として、時刻 $t=-2$ から $15$ までをプロットします。

lambdify を利用して、sympy オブジェクトを numpyユニバーサル関数に変換して利用します。

# パラメータ
prm = { E:sympy.Piecewise((0,t<0),(5,t>=0)), R:50e3, C:40e-6 }

# パラメータを代入
i = i.subs(prm)
VR=VR.subs(prm)
VC=VC.subs(prm)

# sympy オブジェクトを numpyユニバーサル関数に変換
f_VR = sympy.lambdify(t, VR, 'numpy')
f_VC = sympy.lambdify(t, VC, 'numpy')

tt = np.linspace(-2, 15, 200)

# 
plt.figure()
plt.plot(tt, f_VC(tt),label='VC')
plt.plot(tt, f_VR(tt),label='VR')
plt.legend() 
plt.show()

fig.png

プログラム全体

%reset -f
import sympy
import numpy as np
import matplotlib.pyplot as plt

# シンボル(変数記号)の定義
q,i = sympy.symbols('q i', cls=sympy.Function)
t,E,R,C,C1 = sympy.symbols('t E R C C1')

# 方程式 eq1 を立てる
eq1 = sympy.Eq( E, R * sympy.Derivative(q(t), t) + q(t)/C )
display(eq1)

eq2 = sympy.dsolve(eq1, q(t))
display(eq2)

# eq2の左辺を0、右辺にt=0を代入
eq3 = sympy.Eq(0, eq2.rhs.subs(t,0),)  
C1a = sympy.solve(eq3,C1)[0]
display(C1a)

# 初期条件を与えて求めた積分定数C1aを代入
eq4 = eq2.subs(C1,C1a)
display(eq4) 

# 式を整理(展開)
eq4 = eq4.expand()
display(eq4) 

# eq4 の右辺を t 微分
eq5 = sympy.Eq( i(t), sympy.diff(eq4.rhs,t))
display( eq5 )

i = eq5.rhs

# 抵抗両端の電圧 VR 
VR = R*i
display(VR)

# コンデンサ両端の電圧 VC
VC = E - VR
display(VC)
display(VC.collect(E)) # 電圧Eで括る 

# パラメータ
prm = { E:sympy.Piecewise((0,t<0),(5,t>=0)), R:50e3, C:40e-6 }

# パラメータを代入
i = i.subs(prm)
VR=VR.subs(prm)
VC=VC.subs(prm)

# sympy オブジェクトを numpyユニバーサル関数に変換
f_VR = sympy.lambdify(t, VR, 'numpy')
f_VC = sympy.lambdify(t, VC, 'numpy')

tt = np.linspace(-2, 15, 200)

# 
plt.figure(dpi=96)
plt.plot(tt, f_VC(tt),label='VC')
plt.plot(tt, f_VR(tt),label='VR')
plt.legend() 
plt.show()

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jupyterでグラフで日本語をきれいに表示する。

jupyter上のmatplotlibできれいにグラフを出す設定

%config InlineBackend.figure_format = 'retina'

でボケた印象のグラフがくっきりする

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

毎朝天気を通知しよう

毎朝天気をLINE Notifyで通知する

天気予報が見たいけど、天気予報の番組の時間じゃない!って言う時ありますよね。
そんなあなたに、毎朝LINEで天気を教えてくれるこれ、オススメです。

準備するもの

・Python3実行環境
・LineNotifyのトークン
トークン取得方法は前回の記事をご覧ください

スクレイピングをしてみる

今回はtenki.kpをスクレイピングしてみたいと思います。
今回は東京の渋谷区のリンクで行います。

tenki.py
# -*- Coding: utf-8 -*-
import requests
from bs4 import BeautifulSoup
url = "https://tenki.jp/lite/forecast/3/16/4410/13113/"
r = requests.get(url)
bsObj = BeautifulSoup(r.content, "html.parser")

サイトのHTMLを見てみると、class = "weather-telop" と言うところに天気があるのが分かりますね。
なので

tenki.py
# -*- Coding: utf-8 -*-
import requests
from bs4 import BeautifulSoup
url = "https://tenki.jp/lite/forecast/3/16/4410/13113/"
r = requests.get(url)
bsObj = BeautifulSoup(r.content, "html.parser")
#天気を取得
we = bsObj.find(class_="weather-telop")
weth = we.get_text()
#最高気温を取得
hig = bsObj.find(class_="high-temp temp")
high = hig.get_text()
#最低気温を取得
lo = bsObj.find(class_="low-temp temp")
low = lo.get_text()

今日雨が降るのか降らないのか、って気になりますよね。
強引に実装してみましょう

unb.py
we = "雨のち曇り"
if "雨" in we:
    un = "必要"
else:
    un = "不要"

かなり強引ですがこんな感じです、、、
ではこれを6時に送信するようにしましょう。
前回の記事から少しだけソースを改善しています

cen.py
from bs4 import BeautifulSoup
import datetime
import requests

mode = {
    "0" : False,
    "6" : False,
}

print("system start")
#_____token_____#
token = ""

#_____def_____#
def LineNotify(token,message):
   line_notify_token = token
   line_notify_api = 'https://notify-api.line.me/api/notify'
   payload = {'message': message}
   headers = {'Authorization': 'Bearer ' + line_notify_token} 
   requests.post(line_notify_api, data=payload, headers=headers)
def count():
  time = datetime.datetime.now()
  yea = time.year
  mont = time.month
  today = time.day
  dtA = datetime.datetime(yea,mont,today)
  dtB = datetime.datetime(2020,1,18)
  theday = dtB - dtA
  num = theday.days
  message = "\n日付が変わりました\n今日は" + str(mont) + "月" + str(today) + "日です\nセンター試験まで残り" + str(num) + "日です。"
  return message
def getWeather():
    url = "https://tenki.jp/lite/forecast/3/16/4410/13113/"
    r = requests.get(url)
    bsObj = BeautifulSoup(r.content, "html.parser")
    we = bsObj.find(class_="weather-telop")
    weth = we.get_text()
    hig = bsObj.find(class_="high-temp temp")
    high = hig.get_text()
    lo = bsObj.find(class_="low-temp temp")
    low = lo.get_text()
    if "雨" in weth:
        unb = "必要"
    else:
        unb = "不要"
    mess = "天気:" + weth + "\n最高気温:" + str(high) + "\n最低気温:" + str(low) + "\n傘:" + unb
    return mess
def main():
 try:
    time = datetime.datetime.now()
    if time.hour == 0:
     if time.minute == 0:
       if mode["0"] != True:
          mode["0"] = True
          message = count()
          LineNotify(token,message)
    if time.hour == 6:
       if mode["6"] != True:
          mode["6"] = True
          message = "\nおはようございます。\n6時になったので今日の天気をお知らせします。\n" + getWeather() + "\n今日も一日がんばりましょう"
          LineNotify(token,message)
    if time.hour == 8:
       if mode["0"] == True:
          mode["0"] = False
       if mode["6"] == True:
          mode["6"] = False
    if time.hour == 10:
       if mode["0"] == True:
          mode["0"] = False
       if mode["6"] == True:
          mode["6"] = False
 except Exception as e:
  print(str(e))
while True:
 main()

test pic
しっかりと動いていました。
では今回はこの辺で、、、

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python+RaspberryPiで2つのLEDを非同期で点滅

LED1を1秒ごと、LED2を1.2秒ごとに点滅したい。

方法1 自前でカウンタ

import time
import wiringpi as w
count = 0
w.wiringPiSetup()
w.pinMode(0,1)
w.pinMode(1,1)

while 1:
  count = count + 1
  if ( 0 == ( count % 20 ) ):
      w.digitalWrite(0,1)
  if ( 0 == ( count % 24 ) ):
      w.digitalWrite(0,0)
  if ( 0 == ( count % 20 ) ):
      w.digitalWrite(1,1)
  if ( 0 == ( count % 24 ) ):
      w.digitalWrite(1,0)
  time.sleep(0.1)

方法2 マルチスレッド

import threading
import time
import wiringpi as w
led1Stat=0
led2Stat=0
w.wiringPiSetup()
w.pinMode(0,1)
w.pinMode(1,1)

def LED1():
    global led1Stat
    if (0 == led1Stat):
        led1Stat = 1
    else :
        led1Stat = 0
    w.digitalWrite(0,led1Stat)
    t=threading.Timer(1,LED1)
    t.start()
def LED2():
    global led2Stat
    if (0 == led2Stat):
        led2Stat = 1
    else :
        led2Stat = 0
    w.digitalWrite(0,led2Stat)
    t=threading.Timer(1.2,LED2)
    t.start()

t1=threading.Thread(target=LED1)
t2=threading.Thread(target=LED2)
t1.start()
t2.start()

方法3 OSのタイマーシグナルを利用する

OSからのシグナルなので少し違うが、マイコンのタイマー割り込みみたいに考えることができ。

@miminashi さんが 「Python3で一定間隔で処理を行う」として、 signal.setitimer を使う方法を紹介している
https://qiita.com/miminashi/items/50a4f0906ab8f18b105d

しかしながら複数のシグナルをセットする方法がわからなかったので、断念!

方法4 Tornado ライブラリを使う

TornadoはPythonで書かれた非同期通信フレームワーク。Webサーバなどに使うことができる。

ここを参考に
https://stackoverflow.com/questions/14186925/tornado-on-raspberry-pi-to-use-websockets-as-well-as-monitor-serial-port-arduino

import datetime
import wiringpi as w

from tornado import gen, options
from tornado.ioloop import IOLoop
from tornado.locks import Condition

condition = Condition()
led1Stat=0
led2Stat=0
w.wiringPiSetup()
w.pinMode(0,1)
w.pinMode(1,1)

@gen.coroutine
def LED1():
  global led1Stat
  while 1:
    yield condition.wait(timeout=datetime.timedelta(seconds=1))
    if (0 == led1Stat):
        led1Stat = 1
    else :
        led1Stat = 0
    w.digitalWrite(0,led1Stat)
 #   print("1",led1Stat)

@gen.coroutine
def LED2():
  global led2Stat
  while 1:
    yield condition.wait(timeout=datetime.timedelta(seconds=1.2))
    if (0 == led2Stat):
        led2Stat = 1
    else :
        led2Stat = 0
    w.digitalWrite(0,led2Stat)
#    print("2",led2Stat)

@gen.coroutine
def runner():
    yield [LED1(), LED2()]


options.parse_command_line()
IOLoop.current().run_sync(runner)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonを用いてRDataからcsvに変換する

私用で使いたいデータセットにRDataがあったのですが、内部のデータを抽出してpythonで扱えるフォーマット(今回は.csv)に変換する必要があったため、その方法を調べて自分用にまとめました。

pyreadrをインストールする

terminalでpyreadrというモジュールをインストール

pip install pyreadr

python3で使用したいなら、

pip3 install pyreadr

コチラ(https://github.com/ofajardo/pyreadr) を仕様するとRDataをpythonで扱えるようになる。

大まかな使い方

これでRData内のデータをpythonで扱うことができる。

import pyreadr
data = pyreadr.read_r(path) #使用する.RDataのpathを入力
A B C
0 a
1 b
2 c
3 d
4 e
5 f

上記のテーブルの様な型でデータが入っていて、特定のカテゴリーを抽出したい場合、

A = data['A']

このようにするとコラムだけを抽出したデータを扱うこともできる。

pandasをインストールする

ここからは抽出したデータをcsvとして出力する為の方法。
おそらくpython使用してデータを扱う方には馴染みのあるモジュールでしょう。

pip install pandas
pip3 install pandas #さっきと同様、python3で使い方はコチラでインストール

ここから普通にデータフレームを作ってcsv形式で出力するだけです。念の為、私と同様の初学者のためにこの後の流れも記載しておきますが、pandasはpythonでデータ操作をする上で重要なパッケージなのでもっとしっかりしたサイトで学習することをお勧めします。

私が学習に使用したサイトのリンクを載せておきます。
https://tutorials.chainer.org/ja/11_Introduction_to_Pandas.html
もっとしっかり実用的な学習をしたい方にはコチラの学習本をお勧めします。
PythonユーザのためのJupyter[実践]入門

pandasでデータフレームを作ってcsvで出力する

RDataを入力して、データフレームを作ってcsvで出力するまでのコードを一気に書いていきます。

import pandas as pd
import pyreadr
PATH = 'hoge' #扱いたい.RDataのパス
w_PATH = 'hogehoge' #出力した.csvのパス

data = pyreadr.read_r(PATH) #.RDataを入力

df = pd.DataFrame({ #データフレームを作成
       "A": data['A'],
       "B": data['B'],
       "C": data['C']
)}

df.to_csv(w_PATH) #作成したデータフレームを.csvで出力

以上で終了です。データフレームの作成段階で必要ないデータを抜いて出力すればより扱いやすいデータを作成できると思います。
当方も初学者ですのでもっとスマートなやり方があったり何かアドバイスがあればコメントで助言いただけると幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AtCoder Beginner Contest 143復習

昨日のABC143の復習をします。
予定が合わず前回のABCに出られなかったので一ヶ月ぶりのABCでした。相変わらずDまでしか解けません。DからEの壁を乗り越えたいです。

(1)A問題

若干問題文が難しかったので戸惑いましたがif文を書くだけでした。

answerA.py
a,b=map(int,input().split())
if 2*b>=a:
    print(0)
else:
    print(a-2*b)
#if文の部分は以下のようにできる
#print(max([0,a-2*b])

(2)B問題

Bはループを回すだけ、いつも通りでした。
最近C++書きすぎてPythonのループの書き方を忘れそうになりました。ループはC++の方が書きやすい気がする。

answerB.py
n=int(input())
d=[int(i) for i in input().split()]

c=0
for i in range(n-1):
    for j in range(i+1,n):
        c+=d[i]*d[j]

print(c)

(3)C問題

前の記事で言及したgroupby関数を使えば前から順に要素をまとめられるので長さを計算すれば良いだけかなと思ったんですが、文字列を前から順に見ていって前の文字と異なるところだけカウントすれば良いので後者で書きました。

answeC.py
n=int(input())
s=input()

c=1
for i in range(1,n):
    if not s[i-1]==s[i]:
        c+=1

print(c)

(4)D問題

なんか簡単なプログラムが通ってしまいうーーんという感じでした。
初めはPythonで書いてTLEで、C++ならいけるのではと思って書いたらACできました。C++も多少書けるようになったことがここで活きました。
(これ通せないと茶パフォ、通ってよかったです…)
問題の解き方としては、三つの不等式をそれぞれ比較するのは遅いので逆順でソートして一つの不等式のみを比較すれば良いと考える→三辺の長さのうち短い二辺A,Bが小さいとC<(A+B)が成り立たないので、Aが小さくなりすぎたらbreak、Bが小さすぎてAの一番大きいものを考えてもC<(A+B)が成り立たない場合もbreakというのを繰り返せば良いです。

answerD.py
n=int(input())
l=sorted([int(i) for i in input().split()],reverse=True)

#print(l)
co=0
for i in range(n-2):
    c=l[i]
    x=0
    for j in range(i+1,n-1):
        b=l[j]
        for k in range(j+1,n):
            a=l[k]
            if c<(a+b):
                co+=1
            else:
                if k==j+1:
                    x=1
                break
        if x==1:
            break

print(co)

answerD.cc
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

int main(){
  int n;
  int lx;
  int a,b,c;
  vector<int> l;
  int x;
  int co=0;
  cin>>n;
  for(int i=0;i<n;i++){
    cin >> lx;
    l.push_back(lx);
  }
  sort(l.begin(),l.end());
  reverse(l.begin(),l.end());
  for(int i=0;i<n-2;i++){
    c=l[i];
    x=0;
    for(int j=i+1;j<n-1;j++){
      b=l[j];
      for(int k=j+1;k<n;k++){
        a=l[k];
        if(c<(a+b)){
          co+=1;
        }else{
          if(k==j+1){x=1;}
          break;
        }
      }
      if(x==1){break;}
    }
  }
  cout << co << endl;
}

(5)E問題

ダイクストラ法の最も簡単なものはこの前実装したのでできるかなと思ったのですが、入力の形が違って受け取るのが面倒だと思ったので諦めてしまいました。この辺が実装力が問われてくるところだと思います。実際、ダイクストラ法で解けるというツイートを見たので、ダイクストラ法に慣れたら再挑戦したいと思います。(時間があれば今にでもやりたいけど授業の課題が…)

(6)F問題

解答見たらへええってなった問題でした。(コンテスト中は問題を読み違えていたことも発覚しました。)
まだF問題には手をつけなくてもいいかなと思うので、レベルが上がったら解き直したいと思います。とりあえず保留。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RDKitで電荷の中和をやってみる

はじめに

前回のフラグメント除去に続き、今回も化合物の前処理の一つとして行われる電荷の中和をやってみようと思う。

環境

  • Python3.6
  • RDKit 2019.03.3.0

コード

コードは以下の通りである。事例も含め、参考文献からの流用である。

from rdkit import Chem
from rdkit.Chem import MolStandardize

#-------------------------------------------------------
# 電荷の中和
#-------------------------------------------------------
smis = ("c1cccc[nH+]1",
        "C[N+](C)(C)C", "c1ccccc1[NH3+]",
        "CC(=O)[O-]", "c1ccccc1[O-]",
        "CCS",
        "C[N-]S(=O)(=O)C",
        "C[N-]C=C", "C[N-]N=C",
        "c1ccc[n-]1",
        "CC[N-]C(=O)CC")

uc = MolStandardize.charge.Uncharger()
for smi in smis:
    mol = Chem.MolFromSmiles(smi)
    mol = uc.uncharge(mol)
    uncharge_smi = Chem.MolToSmiles(mol)
    print("Before: " + smi + " -> After: " + uncharge_smi)

結果は以下のとおりである。

Before: c1cccc[nH+]1 -> After: c1ccncc1
Before: C[N+](C)(C)C -> After: C[N+](C)(C)C
Before: c1ccccc1[NH3+] -> After: Nc1ccccc1
Before: CC(=O)[O-] -> After: CC(=O)O
Before: c1ccccc1[O-] -> After: Oc1ccccc1
Before: CCS -> After: CCS
Before: C[N-]S(=O)(=O)C -> After: CNS(C)(=O)=O
Before: C[N-]C=C -> After: C=CNC
Before: C[N-]N=C -> After: C=NNC
Before: c1ccc[n-]1 -> After: c1cc[nH]c1
Before: CC[N-]C(=O)CC -> After: CCNC(=O)CC

2番目と5番目は変化がなく、それ以外は処理がされている。変化があるものとないものの違いがよくわからない。
smilesだとよくわからないので画像で見てみよう。

Before After
c1cccc[nH+]1.png c1ccncc1.png
C[N+](C)(C)C.png C[N+](C)(C)C.png
c1ccccc1[NH3+].png Nc1ccccc1.png
CC(=O)[O-].png CC(=O)O.png
c1ccccc1[O-].png Oc1ccccc1.png
CCS.png CCS.png
C[N-]S(=O)(=O)C.png CNS(C)(=O)=O.png
C[N-]C=C.png C=CNC.png
C[N-]N=C.png C=NNC.png
c1ccc[n-]1.png c1cc[nH]c1.png
CC[N-]C(=O)CC.png CCNC(=O)CC.png

私の画像化処理の問題のためか、ところどころ電荷が表示されていない画像があるが、要するにプロトンの除去または追加によって電荷が中和されるものについては、処理がされているように見える。

おわりに

調べてみると前処理として「標準化」、「トートマーの正規化」といったキーワードもあるが具体的にどんな処理をしているのか、調べるのが面倒くさいからスルーした今の自分の化学力からは理解できなかった。また別の機会に調べてみたい。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【第二回】CentOSにPythonの機械学習開発環境を構築する(Jupyter Notebook編)

前書き

LinuxディストリビューションのCenOSにJupyter Notebookを使用して、Python、Pythonのライブラリであるscikit-learn、Tensorflowやkeras等を使って機械学習のプログラムを作成できる機械学習の開発環境を作成する手順を紹介したいと思います。

「【第一回】CentOSにPythonの機械学習開発環境を構築する(Anacondaインストール編)」ではAnacondaをインストールし、機械学習用環境(ml_env)を作成するところまで紹介しました。
今回はJupyter Notebookを起動し、他のPC等からブラウザでJupyter Notebookを使用できるよう設定します。

環境

  • OS:CentOS Linux release 7.7.1908

前提条件

  • CentOSAnaconda3Anaconda3-2019.07-Linux-x86_64.sh)がインストールされていること
  • 機械学習用の環境(ml_env)が作成されていること

【第一回】CentOSにPythonの機械学習開発環境を構築する(Anacondaインストール編)」で紹介)

手順

1. anacondaユーザーでログイン

CentOSにanacondaユーザーでログインまたはスイッチします。
su - anaconda

コマンド実行結果
[root@CENTOS7 ~]# su - anaconda
最終ログイン: 2019/10/19 (土) 22:05:17 JST日時 pts/0
[anaconda@CENTOS7 ~]$

2. 環境変数の読み込み

「【第一回】CentOSにPythonの機械学習開発環境を構築する(Anacondaインストール編)」で作成した環境変数.anaconda.envを読み込みます。

source ./.anaconda.env

コマンド実行結果
[anaconda@CENTOS7 ~]$ source ./.anaconda.env
[anaconda@CENTOS7 ~]$

3. 機械学習用環境(ml_env)をアクティブにする

「【第一回】CentOSにPythonの機械学習開発環境を構築する(Anacondaインストール編)」で作成した機械学習用環境(ml_env)をアクティブにします。

conda activate ml_env

コマンド実行結果
[anaconda@CENTOS7 ~]$ conda activate ml_env
(ml_env) [anaconda@CENTOS7 ~]$

4. jupyterの確認

jupyterがインストールされていることを確認します。
conda list | grep jupyter

コマンド実行結果
(ml_env) [anaconda@CENTOS7 ~]$ conda list | grep jupyter
jupyter                   1.0.0                    py37_7
jupyter_client            5.3.3                    py37_1
jupyter_console           6.0.0                    py37_0
jupyter_core              4.5.0                      py_0
jupyterlab                1.1.4              pyhf63ae98_0
jupyterlab_server         1.0.6                      py_0
(ml_env) [anaconda@CENTOS7 ~]$

5. jupyter notebookの起動確認

jupyter notebookが起動できることを確認します。
jupyter notebook

コマンド実行結果
(ml_env) [anaconda@CENTOS7 ~]$ jupyter notebook
[W 02:32:49.684 NotebookApp] WARNING: The notebook server is listening on all IP addresses and not using encryption. This is not recommended.
[I 02:32:49.710 NotebookApp] JupyterLab extension loaded from /home/anaconda/anaconda3/envs/ml_env/lib/python3.7/site-packages/jupyterlab
[I 02:32:49.710 NotebookApp] JupyterLab application directory is /home/anaconda/anaconda3/envs/ml_env/share/jupyter/lab
[I 02:32:49.712 NotebookApp] Serving notebooks from local directory: /home/anaconda
[I 02:32:49.712 NotebookApp] The Jupyter Notebook is running at:
[I 02:32:49.712 NotebookApp] http://CENTOS7:8888/?token=524a1b3ec4c1a46637c595b73d7945d5bce5f0ceef917e3a
[I 02:32:49.712 NotebookApp]  or http://127.0.0.1:8888/?token=524a1b3ec4c1a46637c595b73d7945d5bce5f0ceef917e3a
[I 02:32:49.712 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[C 02:32:49.716 NotebookApp]

    To access the notebook, open this file in a browser:
        file:///home/anaconda/.local/share/jupyter/runtime/nbserver-13921-open.html
    Or copy and paste one of these URLs:
        http://CENTOS7:8888/?token=524a1b3ec4c1a46637c595b73d7945d5bce5f0ceef917e3a
     or http://127.0.0.1:8888/?token=524a1b3ec4c1a46637c595b73d7945d5bce5f0ceef917e3a

この状態ではまだ他のPCのブラウザから接続することはできません。

6. jupyter notebookの停止

Ctrl + cでjupyter notebookを停止します。

コマンド実行結果
Shutdown this notebook server (y/[n])? y
[C 02:38:39.455 NotebookApp] Shutdown confirmed
[I 02:38:39.457 NotebookApp] Shutting down 0 kernels
(ml_env) [anaconda@CENTOS7 ~]$

7. jupyterのパスを確認

jupyterのパスを確認します。
jupyter --path

コマンド実行結果
(ml_env) [anaconda@CENTOS7 ~]$ jupyter --path
config:
    /home/anaconda/.jupyter
    /home/anaconda/anaconda3/envs/ml_env/etc/jupyter
    /usr/local/etc/jupyter
    /etc/jupyter
data:
    /home/anaconda/.local/share/jupyter
    /home/anaconda/anaconda3/envs/ml_env/share/jupyter
    /usr/local/share/jupyter
    /usr/share/jupyter
runtime:
    /home/anaconda/.local/share/jupyter/runtime
(ml_env) [anaconda@CENTOS7 ~]$

8. Jupyterの設定ファイルの雛形作成

Jupyterの設定ファイルの雛形を作成します。

jupyter notebook --generate-config

コマンド実行結果
(ml_env) [anaconda@CENTOS7 ~]$ jupyter notebook --generate-config
Overwrite /home/anaconda/.jupyter/jupyter_notebook_config.py with default config? [y/N]y
Writing default config to: /home/anaconda/.jupyter/jupyter_notebook_config.py
(ml_env) [anaconda@CENTOS7 ~]$

9. パスワードの作成

IPytonを使用してパスワードを作成します。
ipython

コマンド実行結果
(ml_env) [anaconda@CENTOS7 ~]$ ipython
Python 3.7.4 (default, Aug 13 2019, 20:35:49)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.8.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]:
  • パスワードのハッシュ値を生成するためにnotebook.authモジュールのpasswd関数を読み込みます。
    from notebook.auth import passwd

  • passwd関数を実行し、パスワードを入力します。
    passwd()

  • パスワードを入力します。

  • 確認用にもう一度パスワードを入力します。

  • パスワードのハッシュ値が出力されます。

  • IPythonを終了します。
    exit

Python実行結果
In [1]: from notebook.auth import passwd

In [2]: passwd()
Enter password:
Verify password:
Out[2]: 'sha1:4fb2f50440ea:f52805b46a917a77814c538f8708c54bde4e87ea'

In [3]: exit

パスワードのハッシュ値が出力されます。

10. デフォルトの作業ディレクトリの作成

jupyter notebookのデフォルトの作業ディレクトリを作成する。
mkdir /home/anaconda/work

コマンド実行結果
(ml_env) [anaconda@CENTOS7 ~]$ mkdir /home/anaconda/work
(ml_env) [anaconda@CENTOS7 ~]$

11. Jupyterの設定ファイル(jupyter_notebook_config.py)の編集

/home/anaconda/.jupyter/jupyter_notebook_config.pyに以下の5行を追加します。

  • c = get_config()
  • c.NotebookApp.ip = '*' (すべてのIPアドレスから接続を許可)
  • c.NotebookApp.open_browser = False (JupyterNotebookの起動時にブラウザを起動しない)
  • c.NotebookApp.port = 8888 (8888番ポートで起動)
  • c.NotebookApp.password = u'sha1:4fb2f50440ea:f52805b46a917a77814c538f8708c54bde4e87ea' (パスワードのハッシュ値の設定(9で設定したパスワードのハッシュ値))
  • c.NotebookApp.notebook_dir = u'/home/anaconda/work' (デフォルトのディレクトリパス(10で作成したディレクトリのパス))
  • c.IPKernelApp.pylab = 'inline'matplotlibで描画したものがnotebook上で表示できるようにする)
/home/anaconda/.jupyter/jupyter_notebook_config.py
c = get_config()
c.NotebookApp.ip = '*'
c.NotebookApp.open_browser = False
c.NotebookApp.port = 8888
c.NotebookApp.password = u'sha1:4fb2f50440ea:f52805b46a917a77814c538f8708c54bde4e87ea'
c.NotebookApp.notebook_dir = u'/home/anaconda/work'
c.IPKernelApp.pylab = 'inline'

12. CentOSのファイアウォールの設定

rootユーザーにスイッチします。
su -

コマンド実行結果
(ml_env) [anaconda@CENTOS7 ~]$ su -
パスワード:
最終ログイン: 2019/10/19 (土) 22:05:11 JST 192.168.0.2から開始日時 pts/0
[root@CENTOS7 ~]#

CentOSに外部から8888番ポートへのアクセスを許可するよう設定します。
firewall-cmd --add-port=8888/tcp --zone=public --permanent

コマンド実行結果
[root@CENTOS7 ~]# firewall-cmd --add-port=8888/tcp --zone=public --permanent
success
[root@CENTOS7 ~]#

firewallの設定を反映させます。
firewall-cmd --reload

コマンド実行結果
[root@CENTOS7 ~]# firewall-cmd --reload
success
[root@CENTOS7 ~]#

firewallの設定を確認します。
firewall-cmd --list-all --zone=public

コマンド実行結果
[root@CENTOS7 ~]# firewall-cmd --list-all --zone=public
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp0s3
  sources:
  services: dhcpv6-client ssh
  ports: 8888/tcp
  protocols:
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

[root@CENTOS7 ~]#

exitでrootユーザーからログアウトする。
exit

コマンド実行結果
[root@CENTOS7 ~]# exit
ログアウト
(ml_env) [anaconda@CENTOS7 ~]$

13. jupyter notebookを起動

jupyter notebookを起動します。

jupyter notebook

コマンド実行結果
(ml_env) [anaconda@CENTOS7 ~]$ jupyter notebook
[W 04:18:22.028 NotebookApp] WARNING: The notebook server is listening on all IP addresses and not using encryption. This is not recommended.
[I 04:18:22.054 NotebookApp] JupyterLab extension loaded from /home/anaconda/anaconda3/envs/ml_env/lib/python3.7/site-packages/jupyterlab
[I 04:18:22.054 NotebookApp] JupyterLab application directory is /home/anaconda/anaconda3/envs/ml_env/share/jupyter/lab
[I 04:18:22.055 NotebookApp] Serving notebooks from local directory: /home/anaconda
[I 04:18:22.055 NotebookApp] The Jupyter Notebook is running at:
[I 04:18:22.055 NotebookApp] http://CENTOS7:8888/
[I 04:18:22.055 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).

14. ブラウザから接続確認

他のPCのブラウザから接続できることを確認します。

http://[サーバのIPアドレス]:8888/
01.png

パスワードを入力し、ログインします。
02.png

jupyter notebookに接続できることが確認できました。

後書き

第二回ではブラウザからJupyter Notebookを使用できるようにするための設定を行いました。
次回は機械学習用環境(ml_env)にscikit-learn、Tensorflowやkeras等の機械学習ライブラリのインストールを行い、Jupyter Notebookでサンプルソースを実行したいと思います。

リンク

【第一回】CentOSにPythonの機械学習開発環境を構築する(Anacondaインストール編)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む