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

PythonでC(++)のヘッダファイル(.h)からマクロ定数を取得

「PythonとC++で定数を共有したい,かつC++側の定数はコンパイル時に確定させたい,かつPythonでも同じ変数を使いたい」という特異な例があったのでメモ.

Pythonから.hで定義された

sample.h
#ifndef _PARAM
#define _PARAM

#define NUM 10
#define epoch 100

#endif

と言うような定数を取り出す.今回は,const intなどで定義したグローバル変数は場合分けがめんどくさいので考えません.

def read_header_and_get_macro(header_filepath):
    with open(header_filepath, mode='r') as f:
        lst = [s.strip() for s in f.readlines()]
    comment_flg = False
    for l in lst:
        items = l.split()
        if len(items) != 0 and items[0] == "/*":
            comment_flg = True
            continue
        if comment_flg == True:
            if len(items) != 0 and items[0] == "*/":
                comment_flg = False
            continue
        if len(items) < 2 or items[0] != "#define":
            continue
        if items[1] in globals():
            if items[2] == 'true':
                globals()[items[1]] = True
            elif items[2] == 'false':
                globals()[items[1]] = False
            else:
                try:
                    globals()[items[1]] = float(items[2])
                except ValueError:
                    try:
                        globals()[items[1]] = int(items[2])
                    except ValueError:
                        globals()[items[1]] = items[2]

やっていること

3行目
    lst = [s.strip() for s in f.readlines()]

ファイルを読み込んで行ごとにリストにする.今回は行ごとのリストをとってきてそれをリストで回したけど別にf.readline()をfor文回すのは自由

4行目
    comment_flg = False

C(++)では/*から始まって*/で終わるまでの間は全てコメントなので,これを覚えておくためのフラグ.

6行目
        items = l.split()

行の中身について,半角スペースなどでわけられているところを分けたリストを取得.

['#define', 'NUM', '10']

みたいなリストがほしかった.

7行目~13行目
        if len(items) != 0 and items[0] == "/*":
            comment_flg = True
            continue
        if comment_flg == True:
            if len(items) != 0 and items[0] == "*/":
                comment_flg = False
            continue

上記の通り,コメントを除く.

14行目~15行目
        if len(items) < 2 or items[0] != "#define":
            continue

#defineから始まらない行はマクロを定義していないので除く.

16行目~28行目
        if items[1] in globals():
            if items[2] == 'true':
                globals()[items[1]] = True
            elif items[2] == 'false':
                globals()[items[1]] = False
            else:
                try:
                    globals()[items[1]] = float(items[2])
                except ValueError:
                    try:
                        globals()[items[1]] = int(items[2])
                    except ValueError:
                        globals()[items[1]] = items[2]

今までのもの以外のものが定数のはずです.このとき, items[1]には定数名が入っているはずなので,これがグローバル変数として定義されているかチェックするためにitems[1] in globals()をします.もしも定義されていれば, globals()[items[1]]でその値がわかるので,変数を強制的に書き換えれば完成.ですが,C++のbool型をPythonにそろえてあげる処理をしているのと,float/int/strの変換も行っています.

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

python argparse memo

argparse_sample.py
from argparse import ArgumentParser

if __name__ == "__main__":

    parser = ArgumentParser()
    # 必須になる
    parser.add_argument("must", type=int)
    # 任意になる
    # actionにstore_treuでbool引数にあればtrueが格納される。
    parser.add_argument("-d", "--do", help="do something", action="store_true")
    # 途中のハイフンはアンダースコアにすれば使える
    parser.add_argument("-o", "--this-is-option")

    args = parser.parse_args()

    print(args.must)
    print(args.do)
    print(args.this_is_option)
$ python argparse_sample.py 1 -d -o "hello"
1
True
hello
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AirpodsProが欲しいので入荷したらLINEに通知する

ことの始まり

AirpodsProの波に乗り遅れた。
先月末発売されたAirpodsProですが、Sonyの例のノイキャンと比較レビューとか見てから買おうと思ってたが、自分が記事を読んだ頃には完売してた、やらかした。
たまに直営のAppleStoreに入荷が入るらしいのでそれを監視して、入荷が入り次第自分のLINEに通知を送る。

下調べ

どうやって入庫情報を取得するか問題
どうやら毎朝入荷があった場合ちゃんと更新されているらしい
スクリーンショット 2019-11-15 13.19.36.png

受け取れる日を確認をクリックするとこの画面が出る。
郵便番号入れると近くのショップ情報が取得できる
スクリーンショット 2019-11-15 13.25.15.png

この感じだとGETとかPOSTで取れそうなので、ネットワーク見る
68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3235363139362f30656336353930312d353739382d383336632d343739332d3334663038623530363361362e706e67.png

めっちゃこれじゃねえかみたいなのがある

デベロッパーツールでResponse見れますが
見た目わかりやすくしたいのでGETとかPOSTしてくれるアプリで情報取ってみる
スクリーンショット 2019-11-15 13.36.28.png
めっちゃ来た

partsが品番でlocationが郵便番号

レスポンスをちゃんと見る

さっき返ってきたレンスポンスは一店舗でこれだけ返ってくる。

response
{
  "head": {
    "status": "200",
    "data": {}
  },
  "body": {
    "stores": [
      {
        "storeEmail": "shinsaibashi@apple.com",
        "storeName": "心斎橋",
        "reservationUrl": "http://www.apple.com/jp/retail/shinsaibashi",
        "makeReservationUrl": "http://www.apple.com/jp/retail/shinsaibashi",
        "state": "大阪府",
        "storeImageUrl": "https://rtlimages.apple.com/cmc/dieter/store/4_3/R091.png?resize=828:*&output-format=jpg",
        "country": "JP",
        "city": "大阪市",
        "storeNumber": "R091",
        "partsAvailability": {
          "MWP22J/A": {
            "storePickEligible": true,
            "storeSearchEnabled": true,
            "storeSelectionEnabled": true,
            "storePickupQuote": "土 2019/12/07 Apple 心斎橋",
            "pickupSearchQuote": "受け取れる日:<br/>土 2019/12/07",
            "storePickupLabel": "受取日:",
            "partNumber": "MWP22J/A",
            "purchaseOption": "",
            "ctoOptions": "",
            "storePickupLinkText": "受け取れる日を確認",
            "storePickupProductTitle": "AirPods Pro",
            "pickupDisplay": "ships-to-store"
          }
        },
        "phoneNumber": "06-4963-4500",
        "address": {
          "address": "Apple 心斎橋",
          "address3": "アーバンBLD心斎橋",
          "address2": "中央区西心斎橋1-5-5",
          "postalCode": "542-0086"
        },
        "hoursUrl": "http://www.apple.com/jp/retail/shinsaibashi",
        "storeHours": {
          "storeHoursText": "営業時間",
          "bopisPickupDays": "日数",
          "bopisPickupHours": "時間",
          "hours": [
            {
              "storeTimings": "10:00~21:00",
              "storeDays": "月~日:"
            }
          ]
        },
        "storelatitude": 34.672,
        "storelongitude": 135.50007,
        "storedistance": 3.44,
        "storeDistanceWithUnit": "3.44 km",
        "storeDistanceVoText": "542-0086からの距離:3.44 km",
        "storelistnumber": 1,
        "storeListNumber": 1
      }

----------------いっぱい下に続く----------------

storePickupQuoteだけ取得できれば問題なさそう

Python で書いてく

GETするだけならrequestsだけで実装できるので適当に書く

在庫情報の取得
def get_store_status(_parts, _location):
    arrival = []
    url =  "https://www.apple.com/jp/shop/retail/pickup-message"
    param = {"parts.0": _parts, "location": _location}

    response = requests.get(url, params=param)
    json_data = json.loads(response.text)

    stores = json_data['body']['stores']
    for store in stores:
        store_pickup = store['partsAvailability'][_parts]['storePickupQuote']

        if '本日' in store_pickup:
            arrival.append({'店舗':store['address']['address'],
                            '郵便番号':store['address']['postalCode']})

    return arrival

とりあえず書いた、適当に解説すると
帰ってきたjsonを辞書に入れて
入荷が入った店舗は日付ではなく本日が入っているのでそういう判定をして
本日があれば通す

実際に在庫情報がいつ更新されるかはわからないので、
6時〜10時の間に1分に1回リクエストさせる。

def start_job():
    parts = {'AirpodsPro':'MWP22J/A', 'Airpods':'MV7N2J/A'}
    location = '530-0000'

    end_stamp = datetime.now().timestamp() + (60 * 60 * 4)

    while True:
        now = datetime.now().timestamp()
        arrival = get_store_status(parts['AirpodsPro'], location)

        if arrival or now > end_stamp:
            line_notif(arrival)
            break

        time.sleep(60)

def trigger():
    schedule.every().day.at('06:00').do(start_job)
    while True:
        schedule.run_pending()
        time.sleep(1)

在庫があればループ抜けて通知
なければ10時までオリョクル

LINEに通知する

LINEはNotifyののやつ登録して自分に送る
これを参考にさせて頂く - PythonでLINEに通知を送る

def line_notify(stores):
    line_notify_token = 'アクセストークン'
    line_notify_api = 'https://notify-api.line.me/api/notify'

    if stores:
        strings=''
        for s in stores:
            strings += f'''
            店舗: {s['店舗']}
            郵便番号: {s['郵便番号']}\n'''

        message = f'''
        AirpodsProが入荷されました。
        対象店舗:{strings}
        ショッピングカート:https://www.apple.com/jp/shop/bag
        '''

        payload = {'message': message}
        headers = {'Authorization': 'Bearer ' + line_notify_token}
        line_notify = requests.post(line_notify_api, data=payload, headers=headers)

とりあえず入荷があればこれでLINEに通知くる
ショッピングカートのURL貼っておけばそのまま購入できる。
(前もって購入する端末とブラウザでショッピングカートに入れておく必要はある)

入荷されているのに来なかったら悲しいので動くのか試しておく

airpodsはあるのでこいつで試す
さっきの辞書にAirpodsの情報入れといたのでそれを使う

arrival = get_store_status(parts['Airpods'], location)

これで実行する。

25521.jpg

通知が来たのでオッケー

最後に

LINE Notify めっちゃ便利ですね、これ今後もめっちゃ使えそう。

一番の問題は朝起きているかなんですけどね

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

多変量正規分布に従う乱数の背景にはCholesky分解がいた

1. 数学的背景

  • 確率変数の変数変換
  • 多次元正規分布の導出
  • 分散共分散行列の性質
  • Cholesky分解のアルゴリズム

を解説する。

1.1. 多次元確率分布の変数変換

確率変数

    \begin{eqnarray}
        X_1,\ X_2,\  \cdots ,\  X_n 
\end{eqnarray}

を、確率変数

\begin{eqnarray}
        Y_1,\ Y_2,\  \cdots ,\  Y_n 
    \end{eqnarray}

に変換する。
ただし、$X$と$Y$は、それぞれ

\begin{eqnarray}
        &f_X&\left(x_1,\ x_2,\ \cdots ,\ x_n\right)\\
        &f_Y&\left(y_1,\ y_2,\ \cdots ,\ y_n\right)
\end{eqnarray}

の確率密度関数をもつものとする。

各$Y$を、$X$の全単射として、

    \begin{eqnarray}
        \begin{cases}
            Y_1 = h_1\left(X_1,\ X_2,\  \cdots ,\  X_n\right)&\\
            Y_2 = h_2\left(X_1,\ X_2,\  \cdots ,\  X_n\right)&\\
            \ \vdots &\\
            Y_n = h_n\left(X_1,\ X_2,\  \cdots ,\  X_n\right)
        \end{cases}
    \end{eqnarray}

と表す。各$h_i$は全単射ゆえ逆関数が存在し、

    \begin{eqnarray}
        \begin{cases}
            X_1 = g_1\left(Y_1,\ Y_2,\  \cdots ,\  Y_n\right)&\\
            X_2 = g_2\left(Y_1,\ Y_2,\  \cdots ,\  Y_n\right)&\\
            \ \vdots &\\
            X_n = g_n\left(Y_1,\ Y_2,\  \cdots ,\  Y_n\right)
        \end{cases}
    \end{eqnarray}

と表される。$n$次元実数空間の任意の部分集合$D$に対して、積分の変数変換を施して、

    \begin{eqnarray}
        &\int\cdots\int_D& f_X\left(x_1,x_2,\cdots ,x_n\right) dx_1dx_2\cdots dx_n \nonumber \\
        = &\int\cdots\int_D& f_X\left(y_1,y_2,\cdots ,y_n\right)\left|\frac{\partial\left( x_1,x_2,\cdots x_n\right) }{\partial\left(y_1,y_2,\cdots y_n\right) }\right| dy_1dy_2\cdots dy_n 
    \end{eqnarray}

が成立する。
ただし、ヤコビアンを、

    \begin{eqnarray}
        \left|\frac{\partial\left( x_1,x_2,\cdots x_n\right) }{\partial\left(y_1,y_2,\cdots y_n\right) }\right| =
        \begin{pmatrix}
            \frac{\partial g_1}{\partial y_1}&\frac{\partial g_1}{\partial y_2}&\cdots&\frac{\partial g_1}{\partial y_n}\\
            \frac{\partial g_2}{\partial y_1}&\ddots&&\vdots\\
            \vdots &&\ddots&\vdots\\
            \frac{\partial g_n}{\partial y_1}&\cdots&\cdots&\frac{\partial g_n}{\partial y_n}
        \end{pmatrix}
    \end{eqnarray}

と表記している。

ここから、確率密度関数の変換式

    \begin{eqnarray}
        f_Y\left(y_1,y_2,\cdots ,y_n\right)
        = f_X\left(y_1,y_2,\cdots ,y_n\right)\left|\frac{\partial\left( x_1,x_2,\cdots x_n\right) }{\partial\left(y_1,y_2,\cdots y_n\right) }\right|

    \end{eqnarray}

を得る。

1.2. 多次元正規分布の導出

$1$次元標準正規分布を、$N\left(0,1\right)$と表記する。

互いに独立な$n$個の標準正規分布

    \begin{eqnarray}
        Z_1,\ Z_2,\cdots ,\ Z_n \sim N\left(0,1\right)\ \textrm{i.i.d}
    \end{eqnarray}

を定める。
これらの同時密度関数は、独立分布であるため積で表され、

    \begin{eqnarray}
        f_Z\left(z\right) = \frac{1}{\left(2\pi\right)^{n/2}} \exp\left(-\frac{1}{2}z^Tz\right)
    \end{eqnarray}

となる。

ここで、

    \begin{eqnarray}
        X &=& AZ + \mu
\end{eqnarray}

の変数変換を施す。
ただし、$Z,X$は$n$次元確率変数ベクトル、$A$は$n\times n$行列、$\mu$は$n$次元ベクトルである。

\begin{eqnarray}
        X&=&\left(X_1,\ X_2,\ \cdots\ X_n\right)^{\mathrm{T}} \nonumber \\
        Z&=&\left(Z_1,\ Z_2,\ \cdots\ Z_n\right)^{\mathrm{T}} \nonumber \\

        A&=&
        \begin{pmatrix}
            a_{1,1}&a_{1,2}&\cdots&a_{1,n}\\
            a_{2,2}&\ddots&&\vdots\\
            \vdots&&\ddots&\vdots\\
            a_{n,1}&\cdots&\cdots&a_{n,n}
        \end{pmatrix} \nonumber \\

        \mu&=&\left(\mu_1,\ \mu_2,\ \cdots\ \mu_n\right)^{\mathrm{T}} \nonumber
    \end{eqnarray}

変数変換のヤコビアンは、

    \begin{eqnarray}
        \left|\frac{\partial\left( x_1,x_2,\cdots x_n\right) }{\partial\left(z_1,z_2,\cdots z_n\right) }\right| &=& \det\left( A\right)
    \end{eqnarray}

より、

\begin{eqnarray}
        \left|\frac{\partial\left( z_1,z_2,\cdots z_n\right) }{\partial\left(x_1,x_2,\cdots x_n\right) }\right| &=& \frac{1}{\det\left( A\right)}

    \end{eqnarray}

を得る。

よって、$X$の同時密度関数は、

    \begin{eqnarray}
        f_X\left(x\right)&=&f_Z\left(A^{\mathrm{T}}\left(x-\mu\right)\right)\frac{1}{\det\left(A\right)} \nonumber \\
        &=&\frac{1}{\left(2\pi\right)^{n/2}\det\left(A\right)}\exp\left\{-\frac{1}{2}\left(x-\mu\right)^{\mathrm{T}}AA^{\mathrm{T}}\left(x-\mu\right)\right\}
    \end{eqnarray}

となる。

変換された確率変数の平均ベクトルと、分散共分散行列は、

    \begin{eqnarray}
        E\left[X\right]&=&AE\left[Z\right]+\mu \nonumber \\
        &=&\mu 
         \\
        Cov\left(X\right)&=&E\left[\left(X-\mu\right)\left(X-\mu\right)^{\mathrm{T}}\right] \nonumber \\
        &=&E\left[AZZ^{\mathrm{T}}A^{\mathrm{T}}\right] \nonumber \\
        &=&AE\left[Z\right] E\left[Z^{\mathrm{T}}\right] A^{\mathrm{T}} \nonumber \\
        &=&AA^{\mathrm{T}}
    \end{eqnarray}

分散共分散行列を、

    \begin{eqnarray}
        \Sigma = AA^{\mathrm{T}}
    \end{eqnarray}

と表記する。

これを適用して、多次元正規分布の同時密度関数

    \begin{eqnarray}
        f_X\left(x\right)
        &=&\frac{1}{\left(2\pi\right)^{n/2}\det\left(A\right)}\exp\left\{-\frac{1}{2}\left(x-\mu\right)^{\mathrm{T}}\left(AA^{\mathrm{T}}\right)^{-1}\left(x-\mu\right)\right\} \nonumber \\
        &=&\frac{1}{\left(2\pi\right)^{n/2}\det\left(\Sigma\right)^{1/2}}\exp\left\{-\frac{1}{2}\left(x-\mu\right)^{\mathrm{T}}\Sigma^{-1}\left(x-\mu\right)\right\}
    \end{eqnarray}

を得る。

1.3. 分散共分散行列の性質

分散共分散行列からCholesky分解によって乱数列を生成するにあたり、分散共分散行列が正定値であるという制約条件が必要になる。

ここでは、一般に分散共分散行列の性質を確認し、Cholesky分解可能性を確かめる。

ここで、線形代数の知識を確認する。

行列の半正定値性

以下は同値。
- $n\times n$対称行列$A$が半正定値である
- 任意のベクトル$x$に対して、$x^\mathrm{T}Ax\geq 0$
- $A$の任意の主小行列式が非負
- 全ての固有値は非負

行列の正定値性

以下は同値。
- $n\times n$対称行列$A$が正定値である
- 任意のベクトル$x$に対して、$x^\mathrm{T}Ax> 0$
- $A$の任意の主小行列式が正
- 全ての固有値は正

以下の主張を証明する。
分散共分散行列$\Sigma$は半正定値対称行列である。

分散$\sigma_i$、共分散$\rho_{i,j}$を成分とする$n\times n$分散共分散行列を$\Sigma$とする。
$\rho_{i,j}=\rho_{j,i}$より、$\Sigma$は対称行列である。
$a$を任意の$n$次元定数ベクトルとする。

\begin{eqnarray}
    a^{\mathrm{T}}\Sigma a &=& a^{\mathrm{T}}E\left[\left(X-E\left[X\right]\right)\left(X-E\left[X\right]\right)^{\mathrm{T}}\right]a \nonumber \\
    &=&E\left[a^{\mathrm{T}}\left(X-E\left[X\right]\right)\left(X-E\left[X\right]\right)^{\mathrm{T}}a\right] \nonumber \\
    &=&E\left[\langle a,X-E\left[X\right]\rangle^2\right] \nonumber \\
    &\geq&0 \nonumber
\end{eqnarray}

よって、$\Sigma$は半正定値である。

実用上、分散共分散行列は多くの場合で正定値対称となることが期待されるが、
正定値対称とならない場合は、以下の乱数生成ができないため注意。

1.4. Cholesky分解

正定値対称行列$A$に対して、下三角行列$L$を用いて分解を与えることを、Cholesky分解という。

    \begin{eqnarray}
        A&=&LL^{\mathrm{T}}
        \\
        \nonumber \\
        A&=&
        \begin{pmatrix}
            a_{1,1}&a_{1,2}&\cdots&a_{1,n}\\
            a_{2,2}&\ddots&&\vdots\\
            \vdots&&\ddots&\vdots\\
            a_{n,1}&\cdots&\cdots&a_{n,n}
        \end{pmatrix} \nonumber \\
        L&=&
        \begin{pmatrix}
            l_{1,1}&0&\cdots&\cdots&0\\
            l_{2,1}&l_{2,2}&0&&\vdots\\
            \vdots&&\ddots&\ddots&\vdots\\
            \vdots&&&l_{n-1,n-1}&0\\
            l_{n,1}&\cdots&\cdots&l_{n,n-1}&l_{n,n}
        \end{pmatrix} \nonumber
    \end{eqnarray}

Cholesky分解の構成法は、以下の通りである。

    \begin{eqnarray}
            l_{i,i}&=&\sqrt{a_{i,i}-\sum_{k=1}^{i-1}l_{i,k}^2}\ \ \ \ \ \ \left(\textrm{for}\ i=1,\cdots ,n\right) \\
            l_{j,i}&=&\left(a_{j,i}-\sum_{k=1}^{i-1}l_{j,k}\ l_{i,k}\right)/l_{i,i}\ \ \ \left(\textrm{for}\ j=i+1,\cdots ,n\right) 
    \end{eqnarray}

1.5. Cholesky分解による多変量正規分布に従う乱数列の生成

分散$\sigma_i$、共分散$\rho_{i,j}$を成分とする分散共分散行列$\Sigma$に対し、正定値の制約を設ける。

$\Sigma$に対してCholesky分解を適用して、$A=L$(下三角行列)を得る。

    \begin{eqnarray}
        x&=&Lz+\mu \\
        \nonumber \\
        L&=&
        \begin{pmatrix}
            l_{1,1}&0&\cdots&\cdots&0\\
            l_{2,1}&l_{2,2}&0&&\vdots\\
            \vdots&&\ddots&\ddots&\vdots\\
            \vdots&&&l_{n-1,n-1}&0\\
            l_{n,1}&\cdots&\cdots&l_{n,n-1}&l_{n,n}
        \end{pmatrix} \nonumber \\
        l_{i,i}&=&\sqrt{\sigma_{i}-\sum_{k=1}^{i-1}l_{i,k}^2}\ \ \ \ \ \ \left(\textrm{for}\ i=1,\cdots ,n\right) \nonumber \\
        l_{j,i}&=&\left(\rho_{j,i}-\sum_{k=1}^{i-1}l_{j,k}\ l_{i,k}\right)/l_{i,i}\ \ \ \left(\textrm{for}\ j=i+1,\cdots ,n\right) \nonumber
    \end{eqnarray}

2. 実装

multivariate_normal_distribution.py
import numpy as np

#平均
mu = np.array([[2],
               [3]])

#分散共分散行列
cov = np.array([[1,0.2],
                 0.2,1]])

L = np.linalg.cholesky(cov)
dim = len(cov)

random_list=[]

for i in range(n):
    z = np.random.randn(dim,1)
    random_list.append((np.dot(L,z)+mu).reshape(1,-1).tolist()[0])

いろいろ遊んでみた。
norm_dist1.png

norm_dist2.png

norm_dist3.png

norm_dist4.png

(数字は分散共分散行列の要素を表しています。)
plot_num.png

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

多変量正規分布に従う乱数生成の背景にはCholesky分解がいた

分散共分散行列のChoresky分解で、独立な標準正規分布に従う乱数を、多変量正規分布に従う乱数に変換できる。
どゆこと?

1. 数学的背景

  • 確率変数の変数変換
  • 多次元正規分布の導出
  • 分散共分散行列の性質
  • Cholesky分解のアルゴリズム

を解説する。

1.1. 多次元確率分布の変数変換

確率変数

    \begin{eqnarray}
        X_1,\ X_2,\  \cdots ,\  X_n 
\end{eqnarray}

を、確率変数

\begin{eqnarray}
        Y_1,\ Y_2,\  \cdots ,\  Y_n 
    \end{eqnarray}

に変換する。
ただし、$X$と$Y$は、それぞれ

\begin{eqnarray}
        &f_X&\left(x_1,\ x_2,\ \cdots ,\ x_n\right)\\
        &f_Y&\left(y_1,\ y_2,\ \cdots ,\ y_n\right)
\end{eqnarray}

の確率密度関数をもつものとする。

各$Y$を、$X$の全単射として、

    \begin{eqnarray}
        \begin{cases}
            Y_1 = h_1\left(X_1,\ X_2,\  \cdots ,\  X_n\right)&\\
            Y_2 = h_2\left(X_1,\ X_2,\  \cdots ,\  X_n\right)&\\
            \ \vdots &\\
            Y_n = h_n\left(X_1,\ X_2,\  \cdots ,\  X_n\right)
        \end{cases}
    \end{eqnarray}

と表す。各$h_i$は全単射ゆえ逆関数が存在し、

    \begin{eqnarray}
        \begin{cases}
            X_1 = g_1\left(Y_1,\ Y_2,\  \cdots ,\  Y_n\right)&\\
            X_2 = g_2\left(Y_1,\ Y_2,\  \cdots ,\  Y_n\right)&\\
            \ \vdots &\\
            X_n = g_n\left(Y_1,\ Y_2,\  \cdots ,\  Y_n\right)
        \end{cases}
    \end{eqnarray}

と表される。$n$次元実数空間の任意の部分集合$D$に対して、積分の変数変換を施して、

    \begin{eqnarray}
        &\int\cdots\int_D& f_X\left(x_1,x_2,\cdots ,x_n\right) dx_1dx_2\cdots dx_n \nonumber \\
        = &\int\cdots\int_D& f_X\left(y_1,y_2,\cdots ,y_n\right)\left|\frac{\partial\left( x_1,x_2,\cdots x_n\right) }{\partial\left(y_1,y_2,\cdots y_n\right) }\right| dy_1dy_2\cdots dy_n 
    \end{eqnarray}

が成立する。
ただし、ヤコビアンを、

    \begin{eqnarray}
        \left|\frac{\partial\left( x_1,x_2,\cdots x_n\right) }{\partial\left(y_1,y_2,\cdots y_n\right) }\right| =
        \begin{pmatrix}
            \frac{\partial g_1}{\partial y_1}&\frac{\partial g_1}{\partial y_2}&\cdots&\frac{\partial g_1}{\partial y_n}\\
            \frac{\partial g_2}{\partial y_1}&\ddots&&\vdots\\
            \vdots &&\ddots&\vdots\\
            \frac{\partial g_n}{\partial y_1}&\cdots&\cdots&\frac{\partial g_n}{\partial y_n}
        \end{pmatrix}
    \end{eqnarray}

と表記している。

ここから、確率密度関数の変換式

    \begin{eqnarray}
        f_Y\left(y_1,y_2,\cdots ,y_n\right)
        = f_X\left(y_1,y_2,\cdots ,y_n\right)\left|\frac{\partial\left( x_1,x_2,\cdots x_n\right) }{\partial\left(y_1,y_2,\cdots y_n\right) }\right|

    \end{eqnarray}

を得る。

1.2. 多次元正規分布の導出

$1$次元標準正規分布を、$N\left(0,1\right)$と表記する。

互いに独立な$n$個の標準正規分布

    \begin{eqnarray}
        Z_1,\ Z_2,\cdots ,\ Z_n \sim N\left(0,1\right)\ \textrm{i.i.d}
    \end{eqnarray}

を定める。
これらの同時密度関数は、独立分布であるため積で表され、

    \begin{eqnarray}
        f_Z\left(z\right) = \frac{1}{\left(2\pi\right)^{n/2}} \exp\left(-\frac{1}{2}z^Tz\right)
    \end{eqnarray}

となる。

ここで、

    \begin{eqnarray}
        X &=& AZ + \mu
\end{eqnarray}

の変数変換を施す。
ただし、$Z,X$は$n$次元確率変数ベクトル、$A$は$n\times n$行列、$\mu$は$n$次元ベクトルである。

\begin{eqnarray}
        X&=&\left(X_1,\ X_2,\ \cdots\ X_n\right)^{\mathrm{T}} \nonumber \\
        Z&=&\left(Z_1,\ Z_2,\ \cdots\ Z_n\right)^{\mathrm{T}} \nonumber \\

        A&=&
        \begin{pmatrix}
            a_{1,1}&a_{1,2}&\cdots&a_{1,n}\\
            a_{2,2}&\ddots&&\vdots\\
            \vdots&&\ddots&\vdots\\
            a_{n,1}&\cdots&\cdots&a_{n,n}
        \end{pmatrix} \nonumber \\

        \mu&=&\left(\mu_1,\ \mu_2,\ \cdots\ \mu_n\right)^{\mathrm{T}} \nonumber
    \end{eqnarray}

変数変換のヤコビアンは、

    \begin{eqnarray}
        \left|\frac{\partial\left( x_1,x_2,\cdots x_n\right) }{\partial\left(z_1,z_2,\cdots z_n\right) }\right| &=& \det\left( A\right)
    \end{eqnarray}

より、

\begin{eqnarray}
        \left|\frac{\partial\left( z_1,z_2,\cdots z_n\right) }{\partial\left(x_1,x_2,\cdots x_n\right) }\right| &=& \frac{1}{\det\left( A\right)}

    \end{eqnarray}

を得る。

よって、$X$の同時密度関数は、

    \begin{eqnarray}
        f_X\left(x\right)&=&f_Z\left(A^{\mathrm{T}}\left(x-\mu\right)\right)\frac{1}{\det\left(A\right)} \nonumber \\
        &=&\frac{1}{\left(2\pi\right)^{n/2}\det\left(A\right)}\exp\left\{-\frac{1}{2}\left(x-\mu\right)^{\mathrm{T}}AA^{\mathrm{T}}\left(x-\mu\right)\right\}
    \end{eqnarray}

となる。

変換された確率変数の平均ベクトルと、分散共分散行列は、

    \begin{eqnarray}
        E\left[X\right]&=&AE\left[Z\right]+\mu \nonumber \\
        &=&\mu 
         \\
        Cov\left(X\right)&=&E\left[\left(X-\mu\right)\left(X-\mu\right)^{\mathrm{T}}\right] \nonumber \\
        &=&E\left[AZZ^{\mathrm{T}}A^{\mathrm{T}}\right] \nonumber \\
        &=&AE\left[Z\right] E\left[Z^{\mathrm{T}}\right] A^{\mathrm{T}} \nonumber \\
        &=&AA^{\mathrm{T}}
    \end{eqnarray}

分散共分散行列を、

    \begin{eqnarray}
        \Sigma = AA^{\mathrm{T}}
    \end{eqnarray}

と表記する。

これを適用して、多次元正規分布の同時密度関数

    \begin{eqnarray}
        f_X\left(x\right)
        &=&\frac{1}{\left(2\pi\right)^{n/2}\det\left(A\right)}\exp\left\{-\frac{1}{2}\left(x-\mu\right)^{\mathrm{T}}\left(AA^{\mathrm{T}}\right)^{-1}\left(x-\mu\right)\right\} \nonumber \\
        &=&\frac{1}{\left(2\pi\right)^{n/2}\det\left(\Sigma\right)^{1/2}}\exp\left\{-\frac{1}{2}\left(x-\mu\right)^{\mathrm{T}}\Sigma^{-1}\left(x-\mu\right)\right\}
    \end{eqnarray}

を得る。

1.3. 分散共分散行列の性質

分散共分散行列からCholesky分解によって乱数列を生成するにあたり、分散共分散行列が正定値であるという制約条件が必要になる。

ここでは、一般に分散共分散行列の性質を確認し、Cholesky分解可能性を確かめる。

ここで、線形代数の知識を確認する。

行列の半正定値性

以下は同値。
- $n\times n$対称行列$A$が半正定値である
- 任意のベクトル$x$に対して、$x^\mathrm{T}Ax\geq 0$
- $A$の任意の主小行列式が非負
- 全ての固有値は非負

行列の正定値性

以下は同値。
- $n\times n$対称行列$A$が正定値である
- 任意のベクトル$x$に対して、$x^\mathrm{T}Ax> 0$
- $A$の任意の主小行列式が正
- 全ての固有値は正

以下の主張を証明する。
分散共分散行列$\Sigma$は半正定値対称行列である。

分散$\sigma_i$、共分散$\rho_{i,j}$を成分とする$n\times n$分散共分散行列を$\Sigma$とする。
$\rho_{i,j}=\rho_{j,i}$より、$\Sigma$は対称行列である。
$a$を任意の$n$次元定数ベクトルとする。

\begin{eqnarray}
    a^{\mathrm{T}}\Sigma a &=& a^{\mathrm{T}}E\left[\left(X-E\left[X\right]\right)\left(X-E\left[X\right]\right)^{\mathrm{T}}\right]a \nonumber \\
    &=&E\left[a^{\mathrm{T}}\left(X-E\left[X\right]\right)\left(X-E\left[X\right]\right)^{\mathrm{T}}a\right] \nonumber \\
    &=&E\left[\langle a,X-E\left[X\right]\rangle^2\right] \nonumber \\
    &\geq&0 \nonumber
\end{eqnarray}

よって、$\Sigma$は半正定値である。

実用上、分散共分散行列は多くの場合で正定値対称となることが期待されるが、
正定値対称とならない場合は、以下の乱数生成ができないため注意。

1.4. Cholesky分解

正定値対称行列$A$に対して、下三角行列$L$を用いて分解を与えることを、Cholesky分解という。

    \begin{eqnarray}
        A&=&LL^{\mathrm{T}}
        \\
        \nonumber \\
        A&=&
        \begin{pmatrix}
            a_{1,1}&a_{1,2}&\cdots&a_{1,n}\\
            a_{2,2}&\ddots&&\vdots\\
            \vdots&&\ddots&\vdots\\
            a_{n,1}&\cdots&\cdots&a_{n,n}
        \end{pmatrix} \nonumber \\
        L&=&
        \begin{pmatrix}
            l_{1,1}&0&\cdots&\cdots&0\\
            l_{2,1}&l_{2,2}&0&&\vdots\\
            \vdots&&\ddots&\ddots&\vdots\\
            \vdots&&&l_{n-1,n-1}&0\\
            l_{n,1}&\cdots&\cdots&l_{n,n-1}&l_{n,n}
        \end{pmatrix} \nonumber
    \end{eqnarray}

Cholesky分解の構成法は、以下の通りである。

    \begin{eqnarray}
            l_{i,i}&=&\sqrt{a_{i,i}-\sum_{k=1}^{i-1}l_{i,k}^2}\ \ \ \ \ \ \left(\textrm{for}\ i=1,\cdots ,n\right) \\
            l_{j,i}&=&\left(a_{j,i}-\sum_{k=1}^{i-1}l_{j,k}\ l_{i,k}\right)/l_{i,i}\ \ \ \left(\textrm{for}\ j=i+1,\cdots ,n\right) 
    \end{eqnarray}

1.5. Cholesky分解による多変量正規分布に従う乱数列の生成

分散$\sigma_i$、共分散$\rho_{i,j}$を成分とする分散共分散行列$\Sigma$に対し、正定値の制約を設ける。

$\Sigma$に対してCholesky分解を適用して、$A=L$(下三角行列)を得る。

    \begin{eqnarray}
        x&=&Lz+\mu \\
        \nonumber \\
        L&=&
        \begin{pmatrix}
            l_{1,1}&0&\cdots&\cdots&0\\
            l_{2,1}&l_{2,2}&0&&\vdots\\
            \vdots&&\ddots&\ddots&\vdots\\
            \vdots&&&l_{n-1,n-1}&0\\
            l_{n,1}&\cdots&\cdots&l_{n,n-1}&l_{n,n}
        \end{pmatrix} \nonumber \\
        l_{i,i}&=&\sqrt{\sigma_{i}-\sum_{k=1}^{i-1}l_{i,k}^2}\ \ \ \ \ \ \left(\textrm{for}\ i=1,\cdots ,n\right) \nonumber \\
        l_{j,i}&=&\left(\rho_{j,i}-\sum_{k=1}^{i-1}l_{j,k}\ l_{i,k}\right)/l_{i,i}\ \ \ \left(\textrm{for}\ j=i+1,\cdots ,n\right) \nonumber
    \end{eqnarray}

2. 実装

multivariate_normal_distribution.py
import numpy as np

#平均
mu = np.array([[2],
               [3]])

#分散共分散行列
cov = np.array([[1,0.2],
                 0.2,1]])

L = np.linalg.cholesky(cov)
dim = len(cov)

random_list=[]

for i in range(n):
    z = np.random.randn(dim,1)
    random_list.append((np.dot(L,z)+mu).reshape(1,-1).tolist()[0])

いろいろ遊んでみた。
norm_dist1.png

norm_dist2.png

norm_dist3.png

norm_dist4.png

(数字は分散共分散行列の要素を表しています。)
plot_num.png

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

Pythonで標準入力を使って生徒の成績を管理するツールを作るには?

標準入力を使って生徒の成績を管理するツールを作ってください。
“save 生徒名 点数“とすると、その生徒の点数を保存。
“save 生徒名 点数“とした場合、
 生徒名がすでに保存されている時は、点数を上書きするか警告文を出して、yes/noで上書きの選択を行う。
“get 生徒名“とするとその生徒の点数を表示。
 “get 生徒名“を行った場合、生徒が登録されていない場合はErrorと出力させて処理を継続
“quit” とするとプログラムが終了する。
“average” すでに保存されいる生徒全員の平均点
“登録されている生徒XX人の平均点はXX.XX点です”

条件としてsplit関数を使うのと辞書使うことです。

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

【pandas】文字列をDataFrameに展開

かゆいところに手が届くpandasレシピ集No.1

かゆいこと

「カテゴリ名(index)」と「値(value)」が文字列として1つの要素となってしまっている配列・DataFrameをなんとかしたい。
各カテゴリ(index)をcolumn名とする列に値を割り振ろう。

ID クソデカ文字列
0 A 0, B 1, C 2
1 B 3, D 4
2 A 5, C 6
3 C 7, D 8

↑この↑クソデカ文字列を↓こう↓したい。

ID A B C D
0 0 1 2 0
1 0 3 0 4
2 5 0 6 0
3 0 0 7 8

レシピ

各カテゴリは既知とします。
未知の場合、カテゴリのリストを作成してください。

str_to_dataframe.py
import numpy as np
import pandas as pd

def str_to_dict(x):
    dic = {'A' : 0, 'B' : 0, 'C' : 0, 'D' : 0}
    for xx in x.split(","):
        xxs = xx.split(" ")
        dic[xxs[-2]] = int(xxs[-1])
    return dic

temp = df["クソデカ文字列"].apply(lambda x : pd.Series(str_to_dataframe(x)))
df = pd.concat((df, temp), axis=1)

これは早い!

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

Python から Azure CosmosDB を操作する (クイックスタート深掘り)

今回は Python から Azure Cosmos DB を操作する方法をクイックスタートを参考に見ていきます。

準備

まずは、
最新の Python のインストール -> https://www.python.org/
Visual Studio Code にて Python のインストールを実行します。VS Code での Python インストールは任意ですが、VS Code で直接 Python をいじれたり、色々なショートカットがあるので大変便利です。
https://marketplace.visualstudio.com/items?itemName=ms-python.python#overview
image.png

Python アプリの Clone

Microsoft が公開している GitHub からコードをクローンします。
git clone https://github.com/Azure-Samples/azure-cosmos-db-python-getting-started.git

Visual Studio の Terminal を開き、Clone したローカルディレクトリまで移動しておきましょう。

Cosmos DB のデプロイ

Azure Cosmos DB をデプロイします。今回はシンプルな Cosmos DB を作成するので、Azure CLI からデプロイします。

az cosmosdb create --name <account-name> --resource-group <resource-group-name>

作成できたら、以下コマンドを Azure CLI から実行し、Endpoint と Key を取得します。

az cosmosdb keys list --name <account-name> --resource-group <resource-group-name>
az cosmosdb show --name <account-name> --resource-group <resource-group-name>

Visual Studio の Terminal からデプロイ

該当ディレクトリまで進んだ Visual Studio のターミナルから、以下コードを実行します。

python cosmos_get_started.py

すると以下が出力されます。

Read item with id Smith_d457f895-3756-49cd-a629-e7708d7ed252. Operation consumed 1 request units
Read item with id Johnson_3bd2aa16-742f-4aed-9a95-3adc548a94e3. Operation consumed 1 request units
Read item with id Wakefield_c73aa98d-7104-4b01-a3da-4946081575ff. Operation consumed 1 request units
Query returned 2 items. Operation consumed 3.09 request units

Portal ページを確認すると、Cosmos DB を操作できていることが確認できました。

深掘り

今回少しハマったのが、以下エラーが発生したときです。

Traceback (most recent call last):
File "cosmos_get_started.py", line 1, in <module>
    from azure.cosmos import exceptions, CosmosClient, PartitionKey
ImportError: cannot import name 'exceptions' from 'azure.cosmos' (...\lib\site-packages\azure\cosmos\__init__.py)

このエラーは、Azure Cosmos DB の SDK が正しくインストールされていないことで発生するエラーです。-perp のバージョンのAzure Cosmos SDK をインストールしている場合、このエラーにぶち当たります。
解決策ですが、以下の Python ドキュメントに記載の方法で回避ができます。

https://pypi.org/project/azure-cosmosdb-table/
(参照元)https://docs.microsoft.com/ja-jp/azure/cosmos-db/table-sdk-python

また、以下ページに Python SDK の正しいインストール方法が記載されているので、みてみると参考になります!

https://github.com/Azure/azure-cosmos-table-python/tree/master/azure-cosmosdb-table

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

MatrixProfileによるECGデータの異常検知

MatrixProfileにより時系列データマイニングがレボリューションだそうです。
が、日本語の文献はまだまだ少ないようなので、足しになればと思います。
本記事では、MatrixProfileの概要とPythonによる実装例を紹介します。

MatrixProfileとは

部分時系列同士の類似結合のことです。

時系列データのペア(A,B)を考えます(同じ時系列同士のペアでも構いません)。
それぞれの時系列は長さmの部分時系列に分解されます。
A内のすべての部分時系列について、それに最も近いB内の部分時系列のインデックスおよびその距離(類似度)を求めたものをMatrixProfileと呼びます。なお、もしAとBが同じ時系列だと、部分時系列も自身とのペアが最適になってしまうので、自身の部分時系列周辺は無視します。

まともに計算しようとすれば膨大な時間が必要となりますが、FFTなどを利用したアルゴリズムの工夫により現実的な時間で解くことが可能です。レボリューションですよね。

詳細はhttps://www.cs.ucr.edu/~eamonn/MatrixProfile.html
SAXやShapeletで有名なUCRのKeogh先生の研究室発の技術だそうです。
論文のタイトルがMatrix Profile 〇〇:みたいな形でシリーズ化されているようで、2019年11月現在Matrix Profile XIX:までパブリッシュされているみたいです。

MatrixProfile用ライブラリ"matrixprofile-ts"

matrixprofile-tsという超お手軽なMatrixProfile専用Pythonライブラリがすでに存在します。

pip install matrixprofile-ts

こちらでも入手可能です。RやC++のものもあるらしいです。
今回はこのmatrixprofile-tsを使ってECGデータの異常検知を試みます。

問題設定

今回は異常波形部位を含んだ一連のECGデータを用いて、教師なしで各点の異常度を計算し、その異常部位を特定するといった問題になります。
以下、実装に移ります。

ECGデータの読込

今回はqtdbsel102と呼ばれるECGデータを用います。
https://www.cs.ucr.edu/~eamonn/discords/qtdbsel102.txt
こちらもKeogh先生の論文で用いられたデータです。
データ長が非常に長いため、初めの5000点を用いることにします。
ちなみにMatrixProfileでは計算過程で正規化されるため、前処理は必要ありません。

from matrixprofile import *
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

#データの読み込み
ECG = pd.read_csv('qtdbsel102.txt', header=None, delimiter='\t')    
X = ECG.values[:5000,2]

#ECGの可視化
plt.style.use('seaborn')
plt.figure(figsize=(16, 8))
plt.xlim(0, 5000)
plt.plot(X)
plt.axvspan(4200, 4400, facecolor='r', alpha=0.1)

4200点~4400点付近異常波形が出現しています。一目瞭然ですね。
ecg_data.png

MatrixProfileの計算

優秀なライブラリのおかげでお手軽に実装出来ます。
ハイパーパラメータは部分時系列の長さだけです。今回は200に設定します。
計算手法は精度はやや落ちるものの最速かつ最新の手法であるSCRIMP++を使ってみます。

#matrixProfileの計算
window_size = 200
MP = matrixProfile.scrimp_plus_plus(X,window_size)

異常検知

ECGのような周期性を持つデータにおいて、正常な部分時系列は自身と非常に似通った波形が他の箇所にも確認されるはずです。
逆に言えば、自身と似通った波形が存在しない、つまりMatrixProfileの距離の値が大きくなる部分が異常と考えられます。

さて、先ほど計算したMP内には距離列と参照インデックス列が格納されています。
距離列を確認し、可視化してみましょう。

#MatrixProfileの可視化
plt.figure(figsize=(16, 8))

plt.subplot(2,1,1)
plt.xlim(0, 5000)
plt.title('ECG')
plt.plot(X)
x = np.arange(4200,4400)
plt.axvspan(4200, 4400, facecolor='r', alpha=0.1)

plt.subplot(2,1,2)
plt.xlim(0, 5000)
plt.title('MatrixProfile')
plt.plot(MP[0])
plt.axvspan(4200, 4400, facecolor='r', alpha=0.1)

うまく異常が検知出来ていることが確認出来ます。
ecg_and_mp.png

MatrixProfileの強み

「異常検知なんて他の手法でも出来るじゃないか。ディープなラーニングでいいじゃないか。」
そう思われる方も多いかもしれません。

しかし、MatrixProfileの魅力はそのシンプルさにあります。
MatrixProfileの計算過程にアメージングな斬新さはあれど、最終的に出力しているのは単なる部分波形同士のマッチングに過ぎません。つまり、異常が検出されたとして、その妥当性を人が容易にチェックすることが出来ます。信頼性やトラクタビリティといった観点において右に出るものはないでしょう。

さらに、今回の異常波形検出はMatrixProfileの持つ機能の1つに過ぎません。MotifやShapeletの発見をはじめとして、数多くの応用先があるらしいです。レボリューションですよね。

まとめ

MatrixProfileを紹介し、matrixprofile-tsと呼ばれるライブラリを試しました:grinning:

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

(書き直し)e-Gov法令APIとXML Pythonを用いた特定ワードが含まれる条文抽出

きっかけ

201910 Business Law Journalに,”XMLによる情報整理の薦め”という記事あり.
興あり.まず法令XMLをいじってみた.

他記事で全くコードを記載していないことに対する代償行為ではない.ないってば.

*なお,”XMLによる情報整理の薦め”の著者は,
 判例等を含めて閲覧可能としたXMLを作成しており,次で公開している.
 民法体系XML http://cyberlawschool.jp/kagayama/civ_ver1.xml
 http://cyberlawschool.jp/kagayama/

内容

  • e-Gov法令APIから指定する法令入手
  • 法令条文表示.
  • 特定のワードを含む条文のみ抽出することも可(個人的にはよく使う)

 参照 https://qiita.com/sakaia/items/c787aa6471e70000d253

問題あり.

  • 遅い.「条の~」を処理する部分が無駄.
  • 遅い.法令全体を読み込んで特定条文のみ処理する方式のほうが速いであろうな.(とりま,all指定で法文全体読み込み可能に変更)
  • KEYWORDを受ける関数が隠れてしまい明示的でない.(ダミーをおいてみたがどうも)
  • 基礎からおかしいでしょう.指摘していただけると,本当に,助かります.
  • iはデバグ用.
  • lawnumはint入力だが内部処理はstr.スッキリしない.

*官報が取れるとよいのですが,掲載されていないのですねぇ.

コード

import requests
import xml.etree.ElementTree as ET

#e-Gov 法令API 仕様書
#https://www.e-gov.go.jp/elaws/pdf/houreiapi_shiyosyo.pdf

#法令選択
def lawName2No(KEYWORD, TAG='LawName'):
    url = 'https://elaws.e-gov.go.jp/api/1/lawlists/2'
    r = requests.get(url)
    root = ET.fromstring(r.content.decode(encoding='utf-8'))
    iflag = 0
    for i,e in enumerate(root.getiterator()):
        if TAG==e.tag:  
            iflag = 0
        if KEYWORD==e.text: 
            iflag = 1
        if iflag==1 and e.tag=='LawNo':
            return  str(e.text)

#法令表示・抽出
def lawTextExtractor(KEYWORD, lawnum='all', wordn=0, words=[]):
    if lawnum=='all':
        url = 'https://elaws.e-gov.go.jp/api/1/lawdata/'+lawName2No(KEYWORD)
    else:
        url = 'https://elaws.e-gov.go.jp/api/1/articles;lawNum='+lawName2No(KEYWORD)+';article='+str(lawnum)
    r = requests.get(url)
    if r.status_code!=requests.codes.ok:
        return
    root = ET.fromstring(r.content.decode(encoding='utf-8')) 
    lawstrs = []
    ssentcount = 0
    for i,e in enumerate(root.getiterator()):
        for xtag in ['LawTitle', 'SupplProvisionLabel', 'ArticleCaption', 'ArticleTitle', 'ParagraphNum', 'ItemTitle', 'Sentence']:
            if e.tag==xtag :

                if wordn==1 and xtag=='Sentence':#ワード指定する場合
                    tmpstr = ''
                    for word in words:
                        if str(e.text).find(word)>-1:
                            if tmpstr=='':
                                tmpstr = e.text.replace(word, '●'+word)
                            else:
                                tmpstr = tmpstr.replace(word, '★'+word)
                    if tmpstr!='':
                        lawstrs.append(tmpstr)
                        ssentcount += 1

                else:
                    lawstrs.append(str(e.text))
    if lawstrs==[]:
        pass
    else:
        if lawnum=='all':
            print('●条項号数', ssentcount)
        print('\n'.join(lawstrs))
#法令指定
KEYWORD = '著作権法'

#ワード抽出する場合 1
wordn = 1 
#抽出ワードリスト
words = ['映画'] 

#条文指定 今回は14条から29条
for lawnum in range(14,30): 
    lawTextExtractor(KEYWORD, lawnum, wordn, words)
    #「条の~」を無理やり処理.後で対策を考える.
    [lawTextExtractor(KEYWORD, str(lawnum)+'.'+str(i), wordn, words) for i in range(15)]

#法令全体指定する場合(3秒ほどで出力される)
lawTextExtractor(KEYWORD, 'all', wordn, words)

#並行して,
#lawTextExtractor(KEYWORD, 18, 0, [])
#など,特定条文の非表示部分を確認するのも良い

#for i in [134,134.2,134.3,153,164.2,134.2]:
#    lawTextExtractor(KEYWORD, i, 0, [])
#など,「第百三十四条第一項若しくは第二項、第百三十四条の二第五項、
#第百三十四条の三、第百五十三条第二項又は第百六十四条の二第二項の規定により
#指定された期間内に限り」など引用された条文をまとめて確認するのも良い

出力結果

  • ワード抽出すると,たとえば,映画では譲渡権等が頒布権に変換されているのだな,などよく分かる.
  • 条項号はワード抽出有無に関わらずそのまま残し,全体構造がわかるようにした.
  • Noneは法令を読み慣れている人にはこのほうが読みやすいと考え,そのまま.
  • インデントは見にくくなったため,行わず.

(著作者の推定)
第十四条
None
(職務上作成する著作物の著作者)
第十五条
None

(映画の著作物の著作者)
第十六条
None
●映画の著作物の著作者は、その●映画の著作物において翻案され、又は複製された小説、脚本、音楽その他の著作物の著作者を除き、制作、監督、演出、撮影、美術等を担当してその●映画の著作物の全体的形成に創作的に寄与した者とする。
(著作者の権利)
第十七条
None

(公表権)
第十八条
None




第二十九条の規定によりその●映画の著作物の著作権が●映画製作者に帰属した場合















(氏名表示権)
第十九条
None






(同一性保持権)
第二十条
None





(複製権)
第二十一条
None
(上演権及び演奏権)
第二十二条
None
(上映権)
第二十二条の二
None
(公衆送信権等)
第二十三条
None

(口述権)
第二十四条
None
(展示権)
第二十五条
None
(頒布権)
第二十六条
None
著作者は、その●映画の著作物をその複製物により頒布する権利を専有する。

著作者は、●映画の著作物において複製されているその著作物を当該●映画の著作物の複製物により頒布する権利を専有する。
(譲渡権)
第二十六条の二
None
著作者は、その著作物(●映画の著作物を除く。以下この条において同じ。)をその原作品又は複製物(●映画の著作物において複製されている著作物にあつては、当該●映画の著作物の複製物を除く。以下この条において同じ。)の譲渡により公衆に提供する権利を専有する。






(貸与権)
第二十六条の三
None
著作者は、その著作物(●映画の著作物を除く。)をその複製物(●映画の著作物において複製されている著作物にあつては、当該●映画の著作物の複製物を除く。)の貸与により公衆に提供する権利を専有する。
(翻訳権、翻案権等)
第二十七条
None
著作者は、その著作物を翻訳し、編曲し、若しくは変形し、又は脚色し、●映画化し、その他翻案する権利を専有する。
(二次的著作物の利用に関する原著作者の権利)
第二十八条
None
第二十九条
None
●映画の著作物(第十五条第一項、次項又は第三項の規定の適用を受けるものを除く。)の著作権は、その著作者が●映画製作者に対し当該●映画の著作物の製作に参加することを約束しているときは、当該●映画製作者に帰属する。

専ら放送事業者が放送のための技術的手段として製作する●映画の著作物(第十五条第一項の規定の適用を受けるものを除く。)の著作権のうち次に掲げる権利は、●映画製作者としての当該放送事業者に帰属する。



専ら有線放送事業者が有線放送のための技術的手段として製作する●映画の著作物(第十五条第一項の規定の適用を受けるものを除く。)の著作権のうち次に掲げる権利は、●映画製作者としての当該有線放送事業者に帰属する。

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

計算ドリル python

今回はテキストファイルを利用して、一桁自然数の二項演算の計算ドリルを作っていきます。環境はanaconda。書いたコードは以下の通りです。

コードと出力結果

計算ドリル
import random

with open("keisan.txt","w") as f:
    answer = []
    for cnt in range(10):
        num1 = random.randint(1,9)
        num2 = random.randint(1,9)
        question = str(num1) + " + " + str(num2) + " = "
        answer.append(num1+num2)
        f.write(question+"\n")
    f.write("\nA. ")
    for ans in answer:
        f.write(str(ans) + " ")
出力結果
5 + 1 = 
5 + 2 = 
3 + 3 = 
2 + 6 = 
1 + 5 = 
4 + 8 = 
6 + 1 = 
8 + 4 = 
6 + 7 = 
6 + 4 = 

A. 6 7 6 8 6 12 7 12 13 10 

フロー

プログラムのフローを説明してきます。

テキストファイルのオープン
with open("keisan.txt","w") as f:

まず、最初に一桁の自然数を取得するためにrandomモジュールをインポートします。そして、テキストファイルに出力するためにwithステートメントを使い、ファイル名を"keisan.txt"として、書き込みモードにして開きます。

式の書き込み
answer = []
for cnt in range(10):
        num1 = random.randint(1,9)
        num2 = random.randint(1,9)
        question = str(num1) + " + " + str(num2) + " = "
        answer.append(num1+num2)
        f.write(question+"\n")
    f.write("\nA. ")

ここでは実際の足し算を書き込んでいます。答えの出力もするので、リストを用意します。randomモジュールの整数を生成するrandint関数を使い、1~9の自然数を生成しています。questionには問題文を格納するのですが、演算子を使うと勝手に計算されてしまいますので文字列に変換して格納しています。答えの方はappendメソッドを使い、リストに加えていきます。そして、questionに格納された計算式をwriteメソッドで書き込んでいます。見やすいように改行しつつ出力します。

答えの書き込み
 for ans in answer:
        f.write(str(ans) + " ")

最後に、answerに格納された答えのリストをfor文で順番に出力していきます。

振り返り

過去にC言語を使った、テキストファイルの書き読み込みをしたことがありますが、pythonだとwith文を使うことでファイルのクローズを意識しなくなるのでとても楽ですね。
このコードは足し算の二項演算だけなので、今後はもっとこの計算ドリルを演算を自由に選べたり桁数を指定したりと機能を拡張していきたいと思います。なにかご意見、質問などありましたら気軽にコメントして下さい。

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

SonyのPaSoRi(RCS370)でSesameを動かす

背景

第2回「研究室のみんなそれぞれ1日使って何か作ろう!」の日。
他のメンバーの記事はこちら
第1回の僕の記事はこちら

研究室の鍵にはSesameがついているんですが、開閉するたびに携帯でアプリ開いて操作するのが非常にめんどくさい。SuicaとかApple payのFelicaで開けられたらいいのに、と思った。

イメージ図はこんな感じ。
image.png

画像はこちらの記事のものです。ありがとうございます。

事前準備

https://www.otwo.jp/blog/raspi_sesame_1/
http://www.otwo.jp/blog/raspi_sesame_2/
この辺を参照。

  • Raspberry Pi:研究室に転がってたやつ(多分Raspberry Pi3)。
  • Sesame:研究室の入り口に設置済み。
  • NFCリーダ:RCS370。研究室の先生の私物を拝借(なぜ持っているのか…)

Raspberry Piでやってみた(失敗)

nfcpy: 1.0.3
python2: 2.7.16
python3: 3.7.3

nfcpyをpipでインストール。python3にも対応しているとのことだったのでpython3を使う方針。

$ pip3 install nfcpy

同時にgithubからソースをクローン。examples/tagtool.pyを動かしてみる。

$ python3 tagtool.py
[nfc.clf] searching for reader on path usb
[nfc.clf] no reader available on path usb
[main] no contactless reader found on usb
[main] no contactless reader available

エラーが出る。

公式ドキュメントとか、Qiitaの記事とかを調べてみると、デバイスIDを使ってUSBのパスを指定する必要があるみたい。
lsusbで調べてみると、

$ lsusb
(省略)
Bus 001 Device 025: ID 054c:02e1 Sony Corp. FeliCa S330 [PaSoRi]
(省略)

デバイスIDが表示されている。370ではなく330になっているのが気になるが、用いるドライバは同じみたいなのでよしとする。

USBパスを指定して実行してみる。

$ python3 tagtool.py
[nfc.clf] searching for reader on path usb:054c:02e1
[nfc.clf.rcs956] input/output error while waiting for ack
[main] no contactless reader available

あれれ?色々と調べてみると、指定したパスのデバイスとうまくコミュニケーションができていないみたい…。

色々試す

ピザ休憩を挟んで作業再開。
そもそも、RCS370 & Raspberry Pi & nfcpyでうまくいっている例がない。
この記事を始め、370を380に変えたら動きました!系がちらほら…。

テンション下がりつつも、まだ諦めたくなかったのでMonsterを飲んで調査を続ける。

この記事ではDebianとRCS370を使っている。python -m nfcでそもそもnfcpyがちゃんとインストールできているかを確認できるそう。とりあえずやってみる。

This is the 1.0.3 version of nfcpy run in Python 3.7.3
on Linux-4.19.57-v7+-armv7l-with-debian-10.0
I'm now searching your system for contactless devices
ERROR:nfc.clf.rcs956:input/output error while waiting for ack
I'm not trying serial devices because you haven't told me
-- add the option '--search-tty' to have me looking
-- but beware that this may break other serial devs
Sorry, but I couldn't find any contactless device

input/output errorと出ている。python, nfcpyのバージョンを参考記事と合わせても変わらなかった。

エラーの内容としてはこの記事と同じ。うーん…。

nfcpy, pythonのバージョンを色々試しましたが、やっぱりできない…。これはお手上げ…。

僕なりの結論

RCS370 & Raspberry Pi & nfcpyはうまくいかない!

USBポートにRCS370が挿さったことをラズパイは認識するんだけどなあ…。まあRCS380使えということか。

MacBook Airでやってみた(成功)

nfcpy: 1.0.3
python3: 3.6.5

それでも諦めたくなかったので、自分のパソコン(MacBook Air)でやってみることにした。

同様にnfcpyをクローンし、examples/tagtool.pyを走らせる。

[nfc.clf] searching for reader on path usb
[nfc.clf] using Sony RC-S370/P RCS956v1.30 at usb:020:018
** waiting for a tag **
Type3Tag 'FeliCa Standard (RC-S???)' ID=カードのID (省略)

行けた。

Sesameとの接続

公式の記事を参照。Python3を使いたかったのでところどころいじる。

nfc_card.py
# -*- coding: utf-8 -*-
import requests
import json
import binascii
import nfc
import time
from threading import Thread, Timer

# login CANDY HOUSE account and get token
url = "https://api.candyhouse.co/v1/accounts/login"
head = {"Content-type": "application/json"}
payload = {"email":"アカウントのメールアドレス", "password":"アカウントのパスワード"}
response = requests.post(url, headers=head, data=json.dumps(payload))
token = response.json()["authorization"]

# Suica待ち受けの1サイクル秒
TIME_cycle = 1.0
# Suica待ち受けの反応インターバル秒
TIME_interval = 0.2
# タッチされてから次の待ち受けを開始するまで無効化する秒
TIME_wait = 3

# NFC接続リクエストのための準備
# 212F(FeliCa)で設定
target_req_suica = nfc.clf.RemoteTarget("212F")
# 0003(Suica)
target_req_suica.sensf_req = bytearray.fromhex("0000030000")

print('Waiting for SUICA...')
while True:
    # USBに接続されたNFCリーダに接続してインスタンス化
    clf = nfc.ContactlessFrontend('usb')
    # Suica待ち受け開始
    # clf.sense( [リモートターゲット], [検索回数], [検索の間隔] )
    target_res = clf.sense(target_req_suica, iterations=int(TIME_cycle//TIME_interval)+1 , interval=TIME_interval)

    if target_res is not None: # != -> is not に変更

        #tag = nfc.tag.tt3.Type3Tag(clf, target_res)
        #なんか仕様変わったっぽい?↓なら動いた
        tag = nfc.tag.activate_tt3(clf, target_res)
        tag.sys = 3

        #IDmを取り出す
        idm = binascii.hexlify(tag.idm)
        print('Suica detected. idm = ' + str(idm))

        if (idm == b'ICのID'):
            headers = {'X-Authorization':token,}
            response = requests.get('https://api.candyhouse.co/v1/sesames/セサミのシリアルナンバー', headers=headers)

            status = response.json()["is_unlocked"]

            url_control = "https://api.candyhouse.co/v1/sesames/セサミのシリアルナンバー/control"
            head_control = {"X-Authorization": token, "Content-type": "application/json"}

            # 開いてたら閉まる、閉まってたら開く
            if status:
                payload_control = {"type":"lock"}
            else:
                payload_control = {"type":"unlock"}

            response_control = requests.post(url_control, headers=head_control, data=json.dumps(payload_control))

            print(response_control.text)

        print('sleep ' + str(TIME_wait) + ' seconds')
        time.sleep(TIME_wait)
    #end if

    clf.close()

#end while

シリアルナンバーで、SESAME ID(これ)ではないことに注意。ここをSESAME IDにしていたせいでAPIが叩けず、30分ぐらい無駄な時間を過ごした…。

使ってみる

  • ドアの内側 inside


まあ、今回はしょうがない。

  • ドアの外側 outside

NFCリーダを内側からセロテープではり(先生ごめんなさい)、外からICカードで開けられるか確認します。

…開く!けどタッチしてから3~5秒ほどの間がある。それでも
「携帯取り出す→ロック解除→セサミのアプリ開く→接続されるのを待つ→タップする」
よりは早いし楽!良さげ。

iPhone XにいれているモバイルSuicaもちゃんと反応。ただICカードと比べて若干反応が悪いかな?

感想と今後

さらっといけると思っていたけど、やっぱりさらっといかなかった。通信・ハードが絡むとちょっと難易度あがる。でも研究室がさらに便利になりそうで満足。

そしてRCS380を発注した。笑
すぐに届くみたいなので実装して扉につけます。380でうまくいったらまた記事にします。

月1回ぐらいでこの会やりたいなあ。次は何を作ろう。

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

機械学習入門者が一日でシェルティー判定AIを作成してみた

謝辞

まず初めに参考にさせていただきました。
大変感謝申し上げます。

https://qiita.com/yottyann1221/items/a08300b572206075ee9f
https://qiita.com/tomo_20180402/items/e8c55bdca648f4877188
https://qiita.com/mainvoidllll/items/db991dc30d3ddced6250
https://newtechnologylifestyle.net/keras_imagedatagenerator/

掲載している画像はフリー画像を使用させていただきました。
https://pixabay.com/ja/

機械学習に入門しました。
ざっとぐぐったところこんな感じで進めていくようです。

  1. 大量の画像を用意する
  2. 教師/テストデータを作成する
  3. モデルを作成する
  4. モデルで評価する

今回は、画像認識でシェルティー判定プログラムを作成することにしました。

環境構築

Windows 10 に chocolatey で python3 を入れました。
venv で仮想環境を構築し、必要なライブラリを入れます。

> mkdir -p e:\python\ml
> cd e:\python\ml
> python -m venv ml
> .\ml\Scripts\activate
(ml)> pip install requests
(ml)> pip install beautifulsoup4
# lxml を入れないと下のエラーが出る
# bs4.FeatureNotFound: Couldn't find a tree builder with the features you requested: lxml. Do you need to install a parser library?
(ml)> pip install lxml
(ml)> pip install pillow
# 最新の numpy 入れて np.load() すると下のエラーが出るので古いバージョンを指定する
# ValueError: Object arrays cannot be loaded when allow_pickle=False
(ml)> pip install numpy==1.16.2
(ml)> pip install sklearn
(ml)> pip install tensorflow
(ml)> pip install keras
(ml)> pip install matplotlib

構成

こんな構成にしました。

e:\python\ml
├─ml
└─src
   ├─data
   │ ├─img
   │ │ ├─original              : スクレイピングして取得した元画像
   │ │ │ ├─シェルティー 
   │ │ │ ├─コーギー
   │ │ │ └─ボーダーコリー
   │ │ ├─trimmed               : 不要な画像を取り除いて残った画像
   │ │ │ ├─シェルティー 
   │ │ │ ├─コーギー
   │ │ │ └─ボーダーコリー
   │ │ ├─excluded              : 不要な画像
   │ │ │ ├─シェルティー 
   │ │ │ ├─コーギー
   │ │ │ └─ボーダーコリー
   │ │ ├─extended              : 残った画像を加工して水増しした画像
   │ │ │ ├─シェルティー 
   │ │ │ ├─コーギー
   │ │ │ └─ボーダーコリー
   │ │ └─test                  : AI の動作確認用の画像
   │ ├─train_test_data
   │ │ └─data.npy              : extended の画像を使って作成した教師/テストデータ
   │ ├─model                   : 作成したモデル
   │ │ ├─Training_and_validation_accuracy.png
   │ │ ├─Training_and_validation_loss.png
   │ │ ├─model_predict.hdf5
   │ │ └─model_predict.json
   ├─img_scraper               : 画像スクレイピング
   │ ├─google_search.py
   │ └─main.py
   ├─img_trimmer               : 画像除去
   │ └─main.py
   ├─img_duplicator            : 画像水増し
   │ └─main.py
   ├─train_test_data_generator : 教師/テストデータ作成
   │ └─main.py
   ├─model_generator           : モデル生成
   │ └─main.py
   └─ai                        : シェルティー判定
     └─main.py

画像スクレイピング

まずは画像を用意します。
今回は 300 枚ずつスクレイピングしました。

img_scraper\google_search.py
# -- coding: utf-8 --

import json
from urllib import parse
import requests
from bs4 import BeautifulSoup

class Google:
    def __init__(self):
        self.GOOGLE_SEARCH_URL = 'https://www.google.co.jp/search'
        self.session = requests.session()
        self.session.headers.update({
            'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0'})

    def Search(self, keyword, type='text', maximum=1000):
        '''Google検索'''
        print('Google', type.capitalize(), 'Search :', keyword)
        result, total = [], 0
        query = self.query_gen(keyword, type)
        while True:
            # 検索
            html = self.session.get(next(query)).text
            links = self.get_links(html, type)

            # 検索結果の追加
            if not len(links):
                print('-> No more links')
                break
            elif len(links) > maximum - total:
                result += links[:maximum - total]
                break
            else:
                result += links
                total += len(links)

        print('-> 結果', str(len(result)), 'のlinksを取得しました')
        return result

    def query_gen(self, keyword, type):
        '''検索クエリジェネレータ'''
        page = 0
        while True:
            if type == 'text':
                params = parse.urlencode({
                    'q': keyword,
                    'num': '100',
                    'filter': '0',
                    'start': str(page * 100)})
            elif type == 'image':
                params = parse.urlencode({
                    'q': keyword,
                    'tbm': 'isch',
                    'filter': '0',
                    'ijn': str(page)})

            yield self.GOOGLE_SEARCH_URL + '?' + params
            page += 1

    def get_links(self, html, type):
        '''リンク取得'''
        soup = BeautifulSoup(html, 'lxml')
        if type == 'text':
            elements = soup.select('.rc > .r > a')
            links = [e['href'] for e in elements]
        elif type == 'image':
            elements = soup.select('.rg_meta.notranslate')
            jsons = [json.loads(e.get_text()) for e in elements]
            links = [js['ou'] for js in jsons]
        return links

img_scraper\main.py
# -- coding: utf-8 --

import os
import requests
import google_search

# 検索キーワード
KEYWORDS = [u'シェルティー', u'コーギー', u'ボーダーコリー']
# 取得する画像数
IMG_CNT = 300

g = google_search.Google()
for keyword in KEYWORDS:
    # 保存先作成
    img_dir = os.path.join('./../data/img/original', keyword)
    os.makedirs(img_dir, exist_ok=True)
    print(u'保存先: {}'.format(img_dir))
    # 画像検索
    img_urls = g.Search('{} filetype:jpg -柴犬 -フェルト -商品 -おもちゃ -ぬいぐるみ -置物 -アイテム -マスコット -本 -表紙 -映画 -漫画 -チャーム -jpg -楽天市場 -イラスト -ステッカー -通販 -LINE -スタンプ -シルエット -デザイン -マグカップ -ブリーダー -ジモティー -集団 -群れ -ネイル -クレーン -景品 -製品'.format(keyword), type='image', maximum=IMG_CNT)   
    # 画像保存
    total_cnt = 0
    for i,img_url in enumerate(img_urls):
        try:
            # 画像パス
            img_full_path = os.path.join(img_dir, (str(i) + '.jpg'))
            print('{}: {} -> {}'.format(str(i), img_url, img_full_path))
            re = requests.get(img_url, allow_redirects=False)
            if len(re.content) <= 0:
                print(u'{}: レスポンスの中身が空のためスキップします。'.format(str(i)))
                continue
            with open(img_full_path, 'wb') as f:
                f.write(re.content)
                total_cnt = total_cnt + 1
        except requests.exceptions.ConnectionError:
            continue
        except UnicodeEncodeError:
            continue
        except UnicodeError:
            continue
        except IsADirectoryError:
            continue
    print(u'{}: {} 件の画像を取得しました。'.format(keyword, total_cnt))

print(u'保存完了しました。')

> cd e:\python\ml\src\img_scraper
> python main.py
シェルティー: 270 件の画像を取得しました。
コーギー: 286 件の画像を取得しました。
ボーダーコリー: 281 件の画像を取得しました。

数件はうまく画像を保存できなかったようです。

画像精査

教師データとして使えない画像を取り除きます。
例えば他の犬種が一緒に写っていたりテキストが大量に入っていたり…しているのでそういった画像の番号をメモします。
あとは Python で教師データに使用するデータと除外対象とを仕分けします。

img_trimmer\main.py
# -- coding: utf-8 --

import os, glob
import shutil

# 検索キーワード
KEYWORDS = [u'シェルティー', u'コーギー', u'ボーダーコリー']

targets = [
    [19, 25, 34, 40, 41, 49, 54, 74, 77, 81, 86, 89, 91, 93, 102, 104, 108, 111, 118, 119, 124, 127, 130, 131, 132, 136, 152, 154, 158, 159, 161, 168, 169, 173, 178, 181, 183, 184, 192, 193, 198, 201, 202, 213, 218, 233, 235, 236, 238, 240, 243, 245, 246, 248, 249, 252, 255, 264, 265, 269, 271, 272, 280, 283, 284, 287, 294, 295],
    [19, 29, 30, 40, 45, 67, 69, 73, 76, 78, 80, 100, 101, 105, 117, 120, 132, 136, 141, 143, 149, 151, 154, 158, 167, 170, 179, 180, 183, 186, 200, 201, 208, 213, 220, 224, 225, 228, 234, 235, 241, 243, 247, 248, 250, 253, 259, 262, 264, 266, 273, 277, 278, 279, 281, 282, 283, 285, 290, 293, 294, 298, 299],
    [9, 21, 25, 37, 38, 40, 41, 42, 49, 52, 55, 61, 65, 66, 71, 72, 78, 80, 89, 93, 96, 103, 108, 110, 113, 114, 118, 122, 126, 127, 128, 145, 146, 152, 158, 160, 161, 164, 166, 167, 171, 174, 175, 176, 182, 183, 186, 187, 193, 194, 196, 197, 200, 202, 203, 206, 207, 222, 223, 224, 226, 228, 230, 232, 233, 234, 237, 238, 241, 243, 244, 257, 259, 260, 262, 263, 264, 265, 267, 268, 270, 273, 275, 276, 277, 278, 281, 282, 283, 287, 289, 292, 293, 295]
    ]

for idx, keyword in enumerate(KEYWORDS):
    total_count = 0
    # コピー元
    img_dir = os.path.join('./../data/img/original', keyword)
    # コピー先
    os.makedirs(os.path.join('./../data/img/trimmed', keyword), exist_ok=True)
    os.makedirs(os.path.join('./../data/img/excluded', keyword), exist_ok=True)
    files = glob.glob(os.path.join(img_dir, '*.jpg'))
    for f in files:
        if int(os.path.splitext(os.path.basename(f))[0]) in targets[idx]:
            shutil.copy2(f, os.path.join('./../data/img/excluded', keyword))
            print(u'{} : 除外対象です。'.format(f))
        else:
            shutil.copy2(f, os.path.join('./../data/img/trimmed', keyword))
            print(u'{} : 教師データに使用します。'.format(f))
            total_count = total_count + 1
    print(u'{} : 使用できるデータ数は {} 件です。'.format(keyword, total_count))


print(u'保存完了しました。')

> cd e:\python\ml\src\img_trimmer
> python main.py
シェルティー : 使用できるデータ数は 202 件です。
コーギー : 使用できるデータ数は 223 件です。
ボーダーコリー : 使用できるデータ数は 187 件です。

教師データとして使用できるのは 6 ~ 7 割となりました。

画像複製

Keras の ImageDataGenerator で画像を複製、加工することでデータ数を水増しします。

img_duplicator\main.py
# -- coding: utf-8 --

import os
import glob
import numpy as np
from keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array, array_to_img


CATEGORIES = [u'シェルティー', u'コーギー', u'ボーダーコリー']
# 画像サイズ
IMG_SIZE = 150

# ImageDataGeneratorを定義
DATA_GENERATOR = ImageDataGenerator(horizontal_flip=0.3, zoom_range=0.1)

for idx, category in enumerate(CATEGORIES):
    # コピー元
    img_dir = os.path.join('./../data/img/trimmed', category)
    # コピー先
    out_dir = os.path.join('./../data/img/extended', category)
    os.makedirs(out_dir, exist_ok=True)

    files = glob.glob(os.path.join(img_dir, '*.jpg'))
    for i, file in enumerate(files):
        img = load_img(file)
        img = img.resize((IMG_SIZE, IMG_SIZE))
        x = img_to_array(img)
        x = np.expand_dims(x, axis=0)
        g = DATA_GENERATOR.flow(x, batch_size=1, save_to_dir=out_dir, save_prefix='img', save_format='jpg')
        for i in range(10):
            batch = g.next()
    print(u'{} : ファイル数は {} 件です。'.format(category, len(os.listdir(out_dir))))

> cd e:\python\ml\src\img_duplicator
> python main.py
Using TensorFlow backend.
シェルティー : ファイル数は 1817 件です。
コーギー : ファイル数は 1983 件です。
ボーダーコリー : ファイル数は 1708 件です。

教師データ作成

大量の画像を用意したので教師データを作成します。
画像をラベリングしていきます。
全データの 2 割ほどをテストデータにまわします。
作成した教師/テストデータは保存しておきます。

train_test_data_generator\main.py
# -*- coding: utf-8 -*-

from PIL import Image
import os, glob
import numpy as np
import random, math
from sklearn.model_selection import train_test_split
from keras.utils import np_utils

# 画像が保存されているルートディレクトリのパス
IMG_ROOT_DIR = './../data/img/extended'
# カテゴリ
CATEGORIES = [u'シェルティー', u'コーギー', u'ボーダーコリー']
# 密度
DENSE_SIZE = len(CATEGORIES)
# 画像サイズ
IMG_SIZE = 150
# 画像データ
X = []
# カテゴリデータ
Y = []
# 教師データ
X_TRAIN = []
Y_TRAIN = []
# テストデータ
X_TEST = []
Y_TEST = []
# データ保存先
TRAIN_TEST_DATA = './../data/train_test_data/data.npy'


# カテゴリごとに処理する
for idx, category in enumerate(CATEGORIES):
    # 各ラベルの画像ディレクトリ
    image_dir = os.path.join(IMG_ROOT_DIR, category)
    files = glob.glob(os.path.join(image_dir, '*.jpg'))
    for f in files:
        # 各画像をリサイズしてデータに変換する
        img = Image.open(f)
        img = img.convert('RGB')
        img = img.resize((IMG_SIZE, IMG_SIZE))
        data = np.asarray(img)
        X.append(data)
        Y.append(idx)

X = np.array(X)
Y = np.array(Y)

# 正規化
X = X.astype('float32') /255
Y = np_utils.to_categorical(Y, DENSE_SIZE)

# 教師データとテストデータを分ける
X_TRAIN, X_TEST, Y_TRAIN, Y_TEST = train_test_split(X, Y, test_size=0.20)

# 教師/テストデータを保存する
np.save(TRAIN_TEST_DATA, (X_TRAIN, X_TEST, Y_TRAIN, Y_TEST))
print(u'教師/テストデータの作成が完了しました。: {}'.format(TRAIN_TEST_DATA))

> cd e:\python\ml\src\train_test_data_generator
> python main.py
Using TensorFlow backend.
教師/テストデータの作成が完了しました。: ./../data/train_test_data/data.npy

モデル構築

教師/テストデータを用意できたのでいよいよモデルを構築します。
構築したモデルは保存しておきます。

model_generator\main.py
# -*- coding: utf-8 -*-

#モデルの構築

from keras import layers, models
from keras import optimizers
import numpy as np
import matplotlib.pyplot as plt
import os

# カテゴリ
CATEGORIES = [u'シェルティー', u'コーギー', u'ボーダーコリー']
# 密度
DENSE_SIZE = len(CATEGORIES)
# 画像サイズ
IMG_SIZE = 150
INPUT_SHAPE = (IMG_SIZE, IMG_SIZE,3)
# 教師データ
X_TRAIN = []
Y_TRAIN = []
# テストデータ
X_TEST = []
Y_TEST = []
# データ保存先
TRAIN_TEST_DATA = './../data/train_test_data/data.npy'
# モデル保存先
MODEL_ROOT_DIR = './../data/model/'


# ----- モデル構築 ----- #
model = models.Sequential()
model.add(layers.Conv2D(32,(3,3),activation="relu",input_shape=INPUT_SHAPE))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(64,(3,3),activation="relu"))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128,(3,3),activation="relu"))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128,(3,3),activation="relu"))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Flatten())
model.add(layers.Dropout(0.5))
model.add(layers.Dense(512,activation="relu"))
model.add(layers.Dense(DENSE_SIZE,activation="sigmoid"))

#モデル構成の確認
model.summary()
# ----- /モデル構築 ----- #

# ----- モデルコンパイル ----- #
model.compile(loss="binary_crossentropy",
              optimizer=optimizers.RMSprop(lr=1e-4),
              metrics=["acc"])
# ----- /モデル構築 ----- #

# ----- モデル学習 ----- #
# 教師データとテストデータを読み込む
X_TRAIN, X_TEST, Y_TRAIN, Y_TEST = np.load(TRAIN_TEST_DATA)
model = model.fit(X_TRAIN,
                  Y_TRAIN,
                  epochs=10,
                  batch_size=6,
                  validation_data=(X_TEST, Y_TEST))
# ----- /モデル学習 ----- #

# ----- 学習結果プロット ----- #
acc = model.history['acc']
val_acc = model.history['val_acc']
loss = model.history['loss']
val_loss = model.history['val_loss']

epochs = range(len(acc))

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.savefig(os.path.join(MODEL_ROOT_DIR, 'Training_and_validation_accuracy.png'))

plt.figure()

plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.savefig(os.path.join(MODEL_ROOT_DIR, 'Training_and_validation_loss.png'))
# ----- /学習結果プロット ----- #

# ----- モデル保存 ----- #
# モデル保存
json_string = model.model.to_json()
open(os.path.join(MODEL_ROOT_DIR, 'model_predict.json'), 'w').write(json_string)

#重み保存
model.model.save_weights(os.path.join(MODEL_ROOT_DIR, 'model_predict.hdf5'))
# ----- /モデル保存 ----- #

> cd e:\python\ml\src\model_generator
> python main.py
Using TensorFlow backend.
2019-11-15 17:02:03.400229: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
conv2d_1 (Conv2D)            (None, 148, 148, 32)      896
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 74, 74, 32)        0
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 72, 72, 64)        18496
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 36, 36, 64)        0
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 34, 34, 128)       73856
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 17, 17, 128)       0
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 15, 15, 128)       147584
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 7, 7, 128)         0
_________________________________________________________________
flatten_1 (Flatten)          (None, 6272)              0
_________________________________________________________________
dropout_1 (Dropout)          (None, 6272)              0
_________________________________________________________________
dense_1 (Dense)              (None, 512)               3211776
_________________________________________________________________
dense_2 (Dense)              (None, 3)                 1539
=================================================================
Total params: 3,454,147
Trainable params: 3,454,147
Non-trainable params: 0
_________________________________________________________________
Train on 4396 samples, validate on 1100 samples
Epoch 1/10
4396/4396 [==============================] - 103s 23ms/step - loss: 0.5434 - acc: 0.7298 - val_loss: 0.5780 - val_acc: 0.7067
Epoch 2/10
4396/4396 [==============================] - 109s 25ms/step - loss: 0.4457 - acc: 0.7989 - val_loss: 0.4288 - val_acc: 0.8024
Epoch 3/10
4396/4396 [==============================] - 103s 23ms/step - loss: 0.3874 - acc: 0.8318 - val_loss: 0.3992 - val_acc: 0.8170
Epoch 4/10
4396/4396 [==============================] - 106s 24ms/step - loss: 0.3483 - acc: 0.8469 - val_loss: 0.3476 - val_acc: 0.8476
Epoch 5/10
4396/4396 [==============================] - 107s 24ms/step - loss: 0.3029 - acc: 0.8717 - val_loss: 0.3085 - val_acc: 0.8603
Epoch 6/10
4396/4396 [==============================] - 109s 25ms/step - loss: 0.2580 - acc: 0.8947 - val_loss: 0.2918 - val_acc: 0.8736
Epoch 7/10
4396/4396 [==============================] - 107s 24ms/step - loss: 0.2182 - acc: 0.9084 - val_loss: 0.2481 - val_acc: 0.8970
Epoch 8/10
4396/4396 [==============================] - 113s 26ms/step - loss: 0.1855 - acc: 0.9217 - val_loss: 0.1920 - val_acc: 0.9209
Epoch 9/10
4396/4396 [==============================] - 120s 27ms/step - loss: 0.1548 - acc: 0.9394 - val_loss: 0.1775 - val_acc: 0.9345
Epoch 10/10
4396/4396 [==============================] - 114s 26ms/step - loss: 0.1243 - acc: 0.9530 - val_loss: 0.1738 - val_acc: 0.9412

00.png

01.png

シェルティー判定プログラム作成

いよいよシェルティー判定プログラムを用意するときがきました。
確認用の画像を用意していよいよ実行です。

ai\main.py
# -*- coding: utf-8 -*-

from keras import models
from keras.models import model_from_json
from keras.preprocessing import image
import numpy as np
import sys
import os
from keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array, array_to_img

# モデル保存先
MODEL_ROOT_DIR = './../data/model/'
MODEL_PATH = os.path.join(MODEL_ROOT_DIR, 'model_predict.json')
WEIGHT_PATH = os.path.join(MODEL_ROOT_DIR, 'model_predict.hdf5')
# カテゴリ
CATEGORIES = [u'シェルティー', u'コーギー', u'ボーダーコリー']
# 画像サイズ
IMG_SIZE = 150
INPUT_SHAPE = (IMG_SIZE, IMG_SIZE,3)

# モデルを読み込む
model = model_from_json(open(MODEL_PATH).read())
model.load_weights(WEIGHT_PATH)

# 入力引数から画像を読み込む
args = sys.argv
img = image.load_img(args[1], target_size=INPUT_SHAPE)
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)

# モデルで予測する
features = model.predict(x)

print(features)

if features[0, 0] == 1:
    print(u'シェルティーです。')
else:
    for i in range(0, len(CATEGORIES)):
        if features[0, i] == 1:
            print(u'シェルティーではないようです。{}です。'.format(CATEGORIES[i]))

(ml)> cd e:\python\ml\src\ai
(ml)> python .\main.py '..\data\img\test\sheltie_00.jpg'
Using TensorFlow backend.
2019-11-15 17:58:44.863437: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
[[1. 0. 0.]]
シェルティーです。
(ml)> python .\main.py '..\data\img\test\corgi_00.jpg'
Using TensorFlow backend.
2019-11-15 17:58:55.519838: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
[[0. 1. 0.]]
シェルティーではないようです。コーギーです。
(ml)> python .\main.py '..\data\img\test\bordercollie_00.jpg'
Using TensorFlow backend.
2019-11-15 17:59:06.457517: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
[[0. 0. 1.]]
シェルティーではないようです。ボーダーコリーです。

sheltie_00.jpg
シェルティーです。

02.jpg

cogy_00.jpg
シェルティーではないようです。コーギーです。

03.jpg

bordercolli_00.png
シェルティーではないようです。ボーダーコリーです。

04.jpg

いい感じに判定できました。

フランちゃんはシェルティー?

アイコンにも使用しているフランちゃんはどう判定されるでしょう。

(ml)> python .\main.py '..\data\img\test\fran.jpg'
Using TensorFlow backend.
2019-11-15 17:59:28.118592: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
[[0. 0. 1.]]
シェルティーではないようです。ボーダーコリーです。

fran.png
シェルティーではないようです。ボーダーコリーです。

05.jpg

残念。。。
うちのフランちゃんはシェルティーなのに「シェルティーではないようです。ボーダーコリーです。」と判定されました。
理由はおそらく教師データに黒いシェルティーが少なかったからだと考えられます。
教師データの重要性を痛感しました。

追記

画像を変えてリベンジしました。

(ml)> python .\main.py '..\data\img\test\fran_01.jpg'
Using TensorFlow backend.
2019-11-18 17:21:07.929836: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
[[1. 0. 0.]]
シェルティーです。

fran_01.jpg

うちのフランちゃんがシェルティーとして判定されました。

展望

  • 教師データに黒いシェルティーを追加してリベンジする
  • Web アプリに AI を組み込む
  • CNN について理解する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP→Pythonの呼び出し時に'ascii' codec can't encode characters in position xx-xx: ordinal not in range(128)

Pythonでスクリプト書いて、PHPから呼び出したい。
Python側をコンソール上で動作確認して、
PHPでフロント作って呼び出そうとしたらこれ

'ascii' codec can't encode characters in position 28-32: ordinal not in range(128)

ascii?コンソール上で動くけど?
locale変更してもダメ

試しにpython実行コマンドで

LC_ALL=\"ja_JP.utf8\" python test.py

のように定義したら動いた。

分からんわこんなん。

P.S. 上司ありがとう。

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

DjangoのModelsはDB製品が変わっても大丈夫なのか?

目的

とある知り合いが、「なんでDjangoの書籍とか研修はsqlite3が前提なんじゃ!既存のDBをそのまま使ってDjangoでWebアプリ作りたいんじゃ!とりあえずsqliteで練習して後から本番DBに差し替えられないのか!?」とかぬかしおったので検証。結論を言ってしまうと、とりあえず実現できました。

本記事では検証用に簡単なWebアプリを作っていますが、Djangoの詳しい使い方についてはちゃんと触れていませんのでご了承ください。

検証環境

Djangoで簡単なWebアプリを開発する

今回は、とりあえず商品一覧を表示するだけの簡単なアプリケーションを作成してみます。
特に検索機能等はつけず、純粋に全件表示するだけです。

Djangoプロジェクトの作成

プロジェクト用のファイルを展開するために適当なフォルダを検討し、コンソール等で以下のコマンドを叩きます。

#Djangoプロジェクトを作成
> django-admin startproject my_project

#プロジェクトフォルダに移動
> cd my_project

#テストサーバを起動してみる
> python manage.py runserver

ブラウザを立ち上げて、http://127.0.0.1:8000にアクセスしてみます。
テストページが表示されれば、プロジェクトの作成は完了です。
image.png

サーバを停止させたい時は、Ctrl+Cを押します。

アプリケーションを作成する

Djangoではプロジェクト内に複数の「アプリケーション」を登録することができ、それら一つ一つが単体のWebアプリを構成します。つまり、ここからさらに「アプリケーション」とやらを作る必要があります。

以下コマンドで、アプリケーションを作ることができます。今回はwebshoppingという名前で登録します。

> python manage.py startapp webshopping

自動的にアプリケーションと同名のフォルダが出来上がり、様々なファイルが展開されます。

my_project
  |-- manage.py
  |-- db.sqlite3
  |-- my_project
  |     |-- __pycache__
  |     |-- settings.py
  |     |-- urls.py
  |    (略)
  |-- webshopping
        |-- migrations
        |-- apps.py
        |-- models.py
        |-- views.py
       (略)

次に、アプリケーションの登録を行います。
my_project/settings.pyに既に記述があるので、追記するイメージです。

my_project/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'webshopping.apps.WebshoppingConfig' #追記
]

Webページの準備

Webページは、以下の場所にtemplatesフォルダを作り、その中に配置することになっているので
templatesフォルダとhtmlを一枚作ります。

my_project
  |-- manage.py
  |-- db.sqlite3
  |-- my_project
  |     |-- __pycache__
  |     |-- settings.py
  |     |-- urls.py
  |    (略)
  |-- webshopping
        |-- migrations
        |-- apps.py
        |-- models.py
        |-- views.py
        |-- templates  <--作成
               |-- itemlist.html  <--作成
       (略)

htmlファイルの中身はこんな感じにします。

webshopping/templates/itemlist.html
<!DOCTYPE html>
<html>
    <body>
        <h1>商品一覧</h1>
        <table border=1>
            <tr>
               <th>商品名</th>
               <th>価格</th>
            </tr>
        </table>
    </body>
</html>

Python関数(View)とWebページのマッピング

このWebページと、Python関数(View)とマッピングします。
このマッピングは、webshopping/views.pyで定義します。

webshopping/views.py
from django.shortcuts import render

# 以下の関数を追加
def itemlist(request):
    return render(request, 'itemlist.html')

URLとViewのマッピング

次に、特定のURLへリクエストが来たときにこの関数が動くようにマッピングを調整します。
このマッピングはmy_project/urls.pyで定義します。

my_project/urls.py
from django.contrib import admin
from django.urls import path
from webshopping import views #追加

urlpatterns = [
    path('admin/', admin.site.urls),
    path('itemlist/', views.itemlist) #追加(itemlist/にリクエストが来たらviews.itemlist関数を動かす)
]

確認

とりあえず色々そろったので、確認してみます。
サーバを起動してから、http://127.0.0.1:8000/itemlistへアクセスしてみます。

image.png
大丈夫そうです。

Modelクラスを準備する

DjangoではSQLをほぼ書かずにDBを構築することができます。
「顧客」「商品」「社員」等ひとかたまりのデータ群をModelクラスとして定義し、
クラスに書かれている内容を元に勝手に構築やデータの取得、書き込みを行ってくれます。

ではModelを書いていきます。Modelはwebshopping/models.pyに追記していきます。

webshopping/models.py
from django.db import models

#以下のクラスを追記
class Item(models.Model):
    #クラスが持つ(あるいはDBのテーブルが持つ)べきデータを定義する
    item_name = models.CharField(primary_key=True, max_length=20) #商品名(文字データなのでCharField)
    price = models.IntegerField() #価格(数字データなのでIntegerField)

データベースと同期する

コンソールを立上げ、以下のコマンドを実行することで簡単にデータベースのテーブルを構築できます。

> python manage.py makemigrations webshopping
> python manage.py migrate webshopping

適当なデータを突っ込む

これでテーブルはできていますが、中身が何もないのであまりおもしろくありません。
コンソールを使って、データを突っ込んでいきましょう。

> python manage.py shell

IN [1]: from webshopping import models
IN [2]: models.Item('重すぎるノートPC', 80000).save()
IN [3]: models.Item('軽すぎるノートPC', 120000).save()
IN [4]; exit()

これで2件の商品が登録された状態です。

View関数でDBから値を取得するように調整する

webshopping/views.pyを調整していきます。

webshopping/views.py
from django.shortcuts import render
from webshopping import models #追加

def itemlist(request):
    #追加
    context = {
        'item_list': models.Item.objects.all() #DBから情報を取得する
    }
    #変更
    return render(request, 'itemlist.html', context=context)

View関数から渡された値を利用するようにHTMLを調整する

webshopping/templates/itemlist.htmlを調整していきます。

webshopping/templates/itemlist.html
<!DOCTYPE html>
<html>
    <body>
        <h1>商品一覧</h1>
        <table border=1>
            <tr>
               <th>商品名</th>
               <th>価格</th>
            </tr>

            <!--以下を追加-->
            {% for item in item_list %}
            <tr>
                <td>{{ item.item_name }}</td>
                <td>{{ item.price }}</td>
             </tr>
            {% endfor %}
        </table>
    </body>
</html>

確認

サーバを起動して確認してみます。
image.png
大丈夫そうです!!

PostgreSQLのデータベースを用意する

いよいよPostgreSQLに切り替えて行きます。

PostgreSQLのインストールとデータベースの作成

PostgreSQLをインストールし、適当なユーザ名とパスワードを設定しておきます。
この時点では、データベースのみ作りテーブルは作りません。

> psql -U postgres -W
ユーザpostgresのパスワード: ***********

postgres=# create database webshopping;
CREATE DATABASE

Django側の設定をいじる

実は参照先DBの設定は、my_project/settings.pyにあります。
DATABASESと書いてあるところを探し、以下のように書き換えます。
(元の設定はコメントアウトしておくことをオススメします。

my_project/settings.py
#(略)
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'webshopping', #1つ上の項目で作成したデータベースの名前
        'USER': 'postgres', #DBにログインするユーザ名
        'PASSWORD': 'P@ssw0rd', #DBにログインするパスワード
        'HOST': 'localhost',
        'PORT': '',
    }
}
#(略)

設定をDBと同期する

再度設定をDBと同期させます。

> python manage.py migrate webshopping

データを流し込む

再度データを流し込んでいきます。実際に移行する場合は、ここで一旦CSVやSQLファイルに吐き出しておいてからインポートする流れになるでしょう。

> python manage.py shell

IN [1]: from webshopping import models
IN [2]: models.Item('重すぎるノートPC', 80000).save()
IN [3]: models.Item('軽すぎるノートPC', 120000).save()
IN [4]; exit()

PostgreSQL側のデータを確認

一応、PostgreSQLにログインしてデータが入っているか確認します。

> psql -U postgres -W
ユーザpostgresのパスワード: ***********

postgres=# \c webshopping
webshopping=# \d
                     リレーションの一覧
 スキーマ |           名前           |     型     |  所有者
----------+--------------------------+------------+----------
 public   | django_migrations        | テーブル   | postgres
 public   | django_migrations_id_seq | シーケンス | postgres
 public   | webshopping_item         | テーブル   | postgres
(3 行)

webshopping=# select * from webshopping_item;
    item_name     | price
------------------+--------
 重すぎるノートPC |  80000
 軽すぎるノートPC | 120000
(2 行)

うまくいっているようです。

Webページで確認

最後に、サーバを立上げ直してWebで確認してみます。

image.png

問題なさそうです!!

結論

  • ModelsはDB製品が変わったとしても特に大きな影響は無し
  • 別件で日付も検証したが、sqlite3のdatetime型とpsqlのdate型ならpythonのdatetime.date型で特に問題なさそう
  • 製品が変わってもほぼコードいじらなくていい、なんならSQLもほぼ書かなくていいDjangoバンザイ!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonによる二値化画像処理の基本

画像処理

画像の加工、解析を行うためによくされる画像処理である。おぼえておくと、画像ファイルの減少などもできるので覚えておくとよい。

二値化画像

グレースケール画像ともいう。白黒することである。画像処理の基本となる。

環境

・jupyternotebook
・python version == 3.7.4
・sample.jpg(from http://free-photo.net/archive/entry10252.html)
sample1.jpg

ソースコード

#opencvとnumpyのimport
import cv2
import numpy as np

#画像の読み込み
img = cv2.imread("sample1.jpg")

#グレースケール変換
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

#閾値の設定
threshold_value = 150

#配列の作成(output用)
threshold_img = gray.copy()

#実装(numpy)
threshold_img[gray < threshold_value] = 0
threshold_img[gray >= threshold_value] = 255

#Output
cv2.imwrite("C:\\Users\\fgdao\\python\\sample1-2.jpg",threshold_img)

出力画像結果

sample1-2.jpg

閾値の設定のところで、"150"ではなく、ほかの数字で試してみると白黒の位置変化がみられる。
imageJ(URL:"https://imagej.nih.gov/ij/")
などのソフトを使うとそういった変化もリアルタイムで見ることができるので、使ってみるとよい。

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

「Kaggleで勝つ」を読んでみた。?

本全体の感想

実際に上位のkagglerが使用した手法が記載されていて面白い。また数学的知識や数学に嫌悪感のある人でもかなりわかりやすく説明してくれている。また機械学習をしない人でも知見として軽く読むには適していると感じた

本の構成

  1. はじめに
  2. 分析コンペとは
  3. タスクと評価指標
  4. 特徴量の作成
  5. モデルの作成
  6. モデルの評価
  7. モデルのチューニング
  8. アンサンブル

1 はじめに

目次を読んで好奇心をあげるために読んだ。あらかじめ機械学習をちょろっと知ってる人なら好奇心あげあげ

2 分析コンペとは

この辺は分析コンペのモラルとかルールとかが記載されている。コンペに参加する場合は読んでみて損はない

3 タスクと評価指標

kaggle側がどのような評価指標を使っているか知れる。もちろんこの辺は実務的なことに使えてくるのでここから読み始めるのがおすすめ。評価指標を知ることは自分の見解だと機械学習を行う上で二番目に大事なことだと思います。

4 特徴量の作成

この本は後半モデル(学習器)の話が多いけどここが一番大事。ここだけでかなりの知見を得ることができた。この本は機械学習の本じゃなくてコンペ専用の本なのでここが詳しくなるのは必然かな。。すなわち機械学習を行うので一番大事なのは前処理ってことです。(個人的な意見です)

5 モデルの作成

数学的でなく直感的にモデルどんなものか知れる。使うだけなら非常に便利。特にGBDTに詳しく書いてある。ここ読むだけで機械学習の話ができるようになるかも

6 モデルの評価

三番目に大事なのがここの章かもしれない。なぜかというと後述のアンサンブルは実務的ではなくコンペで気持ちよくなるのが目的と言った印象
簡単にいうとバリデーションがでぇーじ、時系列データのバリデーションの注意点も教えてくれる

7 モデルのチューニング

ここの読んだ時の印象は「え、もっと早く教えてくんね?」って感じです。今まで読んだ本の中でもかなり効率よく見やすく教えてくれる。(そんな読んだことないけど)ありがとうございました

8 アンサンブル

みんな気になるアンサンブル。スタッキングについて全体像を理解するのにかなり助かった。正直ここ読むまでスタッキングが何かわかった気になっていた。数学的知識も不要。
また実際にkaggleで使われた例を何個もあげてくれているので想像がとてもしやすい

かなり雑に書いたので誤字脱字がたくさんあると思います。

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

Jupyter Notebookの拡張機能、nbextensionsの設定について 自分用

タイトル通り、nbextensionsの個人的の設定について。

導入

参考にしたサイト
https://www.pynote.info/entry/jupyter-notebook-nbextensions

#インストール
pip install jupyter-contrib-nbextensions
(pip install jupyter-nbextensions-configurator) #上記の時点であるっぽい?

#有効化
jupyter contrib nbextension install --user
jupyter nbextensions_configurator enable --user

アンインストール、無効化したい場合は以下の通り。
参考にしたサイト
https://github.com/ipython-contrib/jupyter_contrib_nbextensions/issues/1012

#無効化
jupyter contrib nbextension uninstall --user
jupyter nbextensions_configurator disable --user

#アンインストール
pip uninstall jupyter-contrib-nbextensions
pip uninstall jupyter-nbextensions-configurator

nbextensionsの設定

参考にしたサイト
https://qiita.com/simonritchie/items/88161c806197a0b84174

nbextensionsの設定.PNG

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

Python (第一章:初めて~実行まで)

はじめに

初心者(インストールしたこともない)人のためのページ
windows上での説明になります。macの人は違うページへ。

インストール

まずはpythonをインストールしよう。
https://www.python.org/

1)このページに"Downloads"にカーソルを当てて"Windows"を選択。
2)その中から、"Python 3.8.0 - Oct. 14, 2019"の下にある"Download Windows x86-64 executable installer"を探してクリック!
3)そのあと、Pythonのinstallerが勝手にダウンロードが始まります。あとは、全部、次へっぽいボタンをどんどん押していく!
4)最後に、installとあると思うので、それをクリックすればPythonそのもののインストールが始まります!

これで、Python本体のインストールは終了です!

Hello Worldの表示

Hello Wolrd!」を表示させてみましょう。
"メモ帳"というアプリを開いて、

print('Hello World!')

と入力して、保存してください。保存するとき、"Ctrl+S"の2つのキーを押すと保存画面に行きます。
保存画面へと言ったら、
ファイル名を

"test.py"

とおき、下の方にある、文字コードを

"UTF-8"

と設定してから、保存しましょう!場所は取り合えず、デスクトップ(Desktop)へ。

保存が終わったら、コマンドプロンプトの起動をしてください。
画面の左下にある「ここに入力して検索」のところに

"cmd"

と打ち、Enterを押せば、黒い画面が起動します。

ここから注意。'[ ]'となっている場所は、パソコン本体に入っている名前か、任意の場所です。

起動後、

C:¥Users¥[username]>

と表示される。
'>'の後に続けて、

cd Desktop

を入力。その後に、

C:¥Users¥[username]¥Desktop>

と表示されます。
またまたその'>'に続けて

python test.py

と入力してください。
そうすると、

Hello World!

と出てきます。以上!

ちなみに、print('')はもうすでに、Python言語です。
Pythonが打てて実行できる状態になりました。これが環境設定のベースとなる部分です。

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

【Python中級者への道】クラスに__getattr__関数を定義する

まとめへのリンク

https://qiita.com/ganariya/items/fb3f38c2f4a35d1ee2e8

はじめに

Pythonの勉強をするために、acopyという群知能ライブラリを写経していました。

acopyでは、多くのPythonの面白い文法・イディオムが使われており、その中でも便利だなぁというのをまとめています。

今回は、クラスに__getattr__特殊メソッドを定義してみます。

インスタンス属性

Pythonでインスタンス内部で使用される属性を取り出す方法として
__dict__を参照する方法があります。

インスタンス内部で使用される名前空間の名前が__dict__に辞書として定義されています。

例えば、以下のような例を見てみます。

class A:

    def __init__(self, x, y):
        self.x = x
        self.y = y


# {'x': 10, 'y': 20}
a = A(10, 20)
print(a.__dict__)

aというインスタンスには、$x, y$の名前が存在し
それらを辞書として取り出すことができます。

__getattr__

Pythonのクラスには、__getattr__という特殊メソッドを定義することができます。

Pythonのインスタンスの名前呼び出しにおいて
もし定義されていない名前で呼ばれればAttributeErrorを返します。

しかし、もし__getattr__が定義されている場合は、
「名前が定義されていない呼び出し時に」AttributeErrorを返す代わりに、__getattr__が実行されます。

例を見てみます。

class A:

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __getattr__(self, name):
        print(name, 'getattr')
        if name == 'hello':
            return self.world
        raise AttributeError

    def world(self, x):
        return x


'''
{'x': 10, 'y': 20}
hello getattr
<bound method A.world of <__main__.A object at 0x10e602150>>
hello getattr
20
'''
a = A(10, 20)
print(a.__dict__)
print(a.hello)
print(a.hello(20))

上のAクラスには__getattr__メソッドが定義されています。

a.helloを呼び出そうとすると、helloという属性がaに定義されていません。
そのため、__getattr__メソッドが呼び出され、代わりにworldメソッドを返しています。

a.hello(20)では、まずworldメソッドが返されて、worldメソッドに20という値を与えて実行しています。
よって、helloという名前が確定した時点で実行されています。

よって、以上の性質を使うと、以下のような無駄コードをかけます。

class A:

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __getattr__(self, name):
        print(name, 'getattr')
        if name == 'hello':
            return self.world
        if name == 'world':
            return self.great
        if name == 'great':
            return self.bad
        if name == 'bad':
            return 0
        raise AttributeError


'''
{'x': 10, 'y': 20}
hello getattr
world getattr
great getattr
bad getattr
0
'''
a = A(10, 20)
print(a.__dict__)
print(a.hello)

応用例

あまり応用例は思いつきませんが、
もし属性がない場合に代わりの規程処理をやらせる、などがあるのかなと思います。
また、分岐などを呼び出し名によって変えるなどがありそうです。

class A:

    def __getattr__(self, name):
        self.nothing(name)

    def exist(self):
        print('exist!')

    def nothing(self, name):
        print(f'nothing {name}')


'''
exist!
nothing unnti
'''
a = A()
a.exist()
a.unnti

また、後日別記事として各内容として
lambda式で名前がないとき例外コードを投げるなどでも使えそうです。

参考文献

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

Winsows10でゼロから機械学習環境を作ってみる

はじめに

皆さん、やってますか?

機械学習

ここ数年、第3次AIブームなんて言われて、もてはやされているアイツです。
人工知能、機械学習、ニューラルネットワーク、ディープラーニング
最近、黙々と勉強してはいますが、 ん~…なんだか難しいですね…。

今回は、その辺はいったん置いておいて、機械学習の環境をサクッと作っちゃいましょうというお話です。
意外と簡単に作れちゃいます。
(参考記事はたくさんあると思いますが、自分用メモも兼ねてます)

Google Colaboratoryでいいじゃん~って方は
こちら

PC環境

OS:Windows10 Pro
GPU:GeForce 940MX
CPU:Core i5-7200U
メモリ:16GB

nVIDIA CUDA ToolkitとcuDNNのインストール

まずは こちらの記事 を参考にCUDA ToolkitcuDNNをインストールしちゃいましょう。
TensorFlow(GPU版)を使う場合に必要になるためです。

注意点としては、記事中のリンクでCUDAをダウンロードしてしまうと、最新の10.1になってしまい、TensorFlowで使う場合シンボリックリンクを設定しなければならないため、こちら から10.0のアーカイブをダンロードしてインストールした方が楽です。

Anacondaのインストール

Pythonの環境はAnacondaを使います。

実のところ一度、素のPython3.7環境でも機械学習の環境を作りました。
そして「Windows10でAnacondaなしで機械学習環境を作る」みたいな記事を書こうと思いました。

ですが、調べているうちにcondaでインストールした環境の方が速度が速いという記事をいくつか見つけたため、
「え~?じゃあAnaconndaで環境作り直す~?」
という気持ちで改めて、AnacondaでPython環境を構築しました。

ということでまずはAnacondaの公式からインストーラをダウンロードしてインストールします。

途中の
Add Anaconda to my PATH environment variavle
はチェックを入れておけば、環境変数にAnaconda系のパスが勝手に入ると思います。

インストール後は、コマンドプロンプトやPowerShell等で

conda python pip
の3つを実行しパスが通っているか確認しましょう。

仮想環境作成

AnacondaはPythonの環境をお手軽に作れてしまします。
どういう利点があるのかとか、詳しく知りたい人は今回はGoogle先生にお任せします。

仮想環境を作成するコマンドは以下

> conda create --name myenv python=3.7

myenvの部分は仮想環境の名前なのでお好きな名前にしちゃってください。
これだけで環境がバンバン作れちゃいます。

環境の切り替えは

> conda activate myenv

で切り替えられます。

※ 注意として、よく見かけるのが、condaがついていない

> activate myenv

ですが、古いバージョンの書き方のようで、
一見何もエラーがなく切り替わったように見えますが、
後述のinfoで調べても環境が切り替わっていません。

自分が今どの環境をアクティブにしているか確認するには

> conda info -e

で以下のように確認できます。

# conda environments:
#
base                     C:\Users\<ユーザー名>\Anaconda3
myenv                 *  C:\Users\<ユーザー名>\Anaconda3\envs\myenv

「*」がついているのが現在の環境です。

必要そうなパッケージのインストール

必要そうなと書いているように、もしかしたらいらないかもしれません。
いろんな機械学習を試してみたい!って場合は必要かも。

conda install pandas
conda install scikit-learn
conda install scikit-image

OpenAI Gym環境インストール

pip install gym
conda install -c conda-forge jsanimation
conda install pyglet
conda install -c conda-forge ffmpeg
pip install --no-index -f https://github.com/Kojoley/atari-py/releases atari_py

conda installpip installを混在させると環境が壊れるという話もあるので、
できればpip installは使わないようにしたいのですが、Anacondaにパッケージがない場合はやむなし…。

KerasとTensorFlow環境インストール

TensorFlowあればKeras内包されてるし、いらないんじゃない?
という意見もあるかと思いますが、サンプルとかで混在している場合もあったりするので、念の為。

conda install -c conda-forge keras
conda install pydotplus
conda install tensorflow
conda install tensorflow-gpu

完成!

以上で、ある程度の機械学習の環境が整いました。

最終的なconda list
# Name                    Version                   Build  Channel
_tflow_select             2.1.0                       gpu
absl-py                   0.8.1                    py37_0
astor                     0.8.0                    py37_0
astroid                   2.3.2                    py37_0
atari-py                  1.2.1                    pypi_0    pypi
blas                      1.0                         mkl
ca-certificates           2019.10.16                    0
certifi                   2019.9.11                py37_0
cloudpickle               1.2.2                      py_0
colorama                  0.4.1                    py37_0
cudatoolkit               10.0.130                      0
cudnn                     7.6.4                cuda10.0_0
cycler                    0.10.0                   py37_0
cytoolz                   0.10.0           py37he774522_0
dask-core                 2.6.0                      py_0
decorator                 4.4.1                      py_0
ffmpeg                    4.2                  h6538335_0    conda-forge
freetype                  2.9.1                ha9979f8_1
future                    0.18.2                   pypi_0    pypi
gast                      0.2.2                    py37_0
google-pasta              0.1.7                      py_0
grpcio                    1.16.1           py37h351948d_1
gym                       0.15.4                   pypi_0    pypi
h5py                      2.9.0            py37h5e291fa_0
hdf5                      1.10.4               h7ebc959_0
icc_rt                    2019.0.0             h0cc432a_1
icu                       58.2                 ha66f8fd_1
imageio                   2.6.1                    py37_0
intel-openmp              2019.4                      245
isort                     4.3.21                   py37_0
joblib                    0.14.0                     py_0
jpeg                      9b                   hb83a4c4_2
jsanimation               0.1                        py_1    conda-forge
keras                     2.3.1            py37h21ff451_0    conda-forge
keras-applications        1.0.8                      py_0
keras-preprocessing       1.1.0                      py_1
kiwisolver                1.1.0            py37ha925a31_0
lazy-object-proxy         1.4.3            py37he774522_0
libgpuarray               0.7.6             hfa6e2cd_1003    conda-forge
libpng                    1.6.37               h2a8f88b_0
libprotobuf               3.9.2                h7bd577a_0
libtiff                   4.1.0                h56a325e_0
mako                      1.1.0                      py_0    conda-forge
markdown                  3.1.1                    py37_0
markupsafe                1.1.1            py37hfa6e2cd_0    conda-forge
matplotlib                3.1.1            py37hc8f65d3_0
mccabe                    0.6.1                    py37_1
mkl                       2019.4                      245
mkl-service               2.3.0            py37hb782905_0
mkl_fft                   1.0.15           py37h14836fe_0
mkl_random                1.1.0            py37h675688f_0
networkx                  2.4                        py_0
numpy                     1.17.3           py37h4ceb530_0
numpy-base                1.17.3           py37hc3f5095_0
olefile                   0.46                     py37_0
opencv-python             4.1.1.26                 pypi_0    pypi
openssl                   1.1.1d               he774522_3
opt_einsum                3.1.0                      py_0
pandas                    0.25.2           py37ha925a31_0
pillow                    6.2.1            py37hdc69c19_0
pip                       19.3.1                   py37_0
protobuf                  3.9.2            py37h33f27b4_0
pydotplus                 2.0.2                    py37_1
pyglet                    1.3.2                    pypi_0    pypi
pygpu                     0.7.6           py37hc8d92b1_1000    conda-forge
pylint                    2.4.3                    py37_0
pyparsing                 2.4.2                      py_0
pyqt                      5.9.2            py37h6538335_2
pyreadline                2.1                      py37_1
python                    3.7.5                h8c8aaf0_0
python-dateutil           2.8.1                      py_0
pytz                      2019.3                     py_0
pywavelets                1.1.1            py37he774522_0
pyyaml                    5.1.2            py37hfa6e2cd_0    conda-forge
qt                        5.9.7            vc14h73c81de_0
scikit-image              0.15.0           py37ha925a31_0
scikit-learn              0.21.3           py37h6288b17_0
scipy                     1.3.1            py37h29ff71c_0
setuptools                41.6.0                   py37_0
sip                       4.19.8           py37h6538335_0
six                       1.12.0                   py37_0
sqlite                    3.30.1               he774522_0
tensorboard               2.0.0              pyhb230dea_0
tensorflow                2.0.0           gpu_py37h57d29ca_0
tensorflow-base           2.0.0           gpu_py37h390e234_0
tensorflow-estimator      2.0.0              pyh2649769_0
tensorflow-gpu            2.0.0                h0d30ee6_0
termcolor                 1.1.0                    py37_1
theano                    1.0.4           py37h6538335_1000    conda-forge
tk                        8.6.8                hfa6e2cd_0
toolz                     0.10.0                     py_0
tornado                   6.0.3            py37he774522_0
tqdm                      4.36.1                     py_0
vc                        14.1                 h0510ff6_4
vs2015_runtime            14.16.27012          hf0eaf9b_0
vs2015_win-64             14.0.25420          h55c1224_11
webencodings              0.5.1                    py37_1
werkzeug                  0.16.0                     py_0
wheel                     0.33.6                   py37_0
wincertstore              0.2                      py37_0
wrapt                     1.11.2           py37he774522_0
xz                        5.2.4                h2fa13f4_4
yaml                      0.1.7             hfa6e2cd_1001    conda-forge
zlib                      1.2.11               h62dcd97_3
zstd                      1.3.7                h508b16e_0

まとめ

つらつらとインストールして、今は1つの仮想環境にいろいろまとめて入っていますが。
用途によって別途仮想環境を作って分けてもいいと思います。

あとは、サンプルを探して動かしてみるなり、
自分で試行錯誤してみたり、いろいろ試せるようになると思います!




と言いたいところですが、TensorFlow2.0.0のサンプルがまだまだ転がってない状況なんですよね…。
過去バージョンのサンプルはたくさんあるのですが、2.0.0で変わった箇所が多く、そのままでは動かないこと多数です。

TensorFlowの公式GoogleColaboratoryでチュートリアルを体験できるようになっています。
(Googleが開発したライブラリなので当然っちゃ当然ですが)

なので、旧バージョンのサンプルを自分なりに理解して、改変できるくらいの知識はやはり必要そうですね…_(┐「ε:)_

まだまだ機械学習についてはで勉強中ですが、今後何かおもしろいことができるようになってきたら、
自分の脳のメモリが追いつく限り、発信していければなーと思っています!
(既にパンパン)

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

RandomForest 使ってみた

今回の目的

 SIGNATEのPractice,ワインの品種の予測を行う.

本題

用いた学習アルゴリズム

  • SVC
  • LogisticRegression(ロジスティック回帰)
  • RandomForest (ランダムフォレスト)

3つのアルゴリズムを試した結果, ランダムフォレストが一番精度が高くなったため, 最終的な分類器として採用した.

コード

データの読み込み

wine-learning.py
wine_data = pd.read_csv('train.tsv',sep='\t')
wine_test = pd.read_csv('test.tsv',sep='\t')

前回はread_table を用いたが, せっかくなのでread_csv も使ってみた. tableのほうが楽な気がします.
ちなみに, どちらも呼び出すメソッドは同じっぽいのでどちらが正しいとかはない.

特徴量データと教師データの分離

wine-learning.py
X = wine_data.loc[:,['Alcohol','Malic acid','Ash','Alcalinity of ash','Magnesium','Total phenols','Flavanoids','Nonflavanoid ohenols','Proanthocyanins','Color intensity','Hue','OD280/OD315 of diluted wines','Proline']].values
y = wine_data.loc[:,'Y'].values

変数が多いと長くなりがちだからこれをどうにかしたい. 次の課題をやるときに改善できる方法があるか検討してみる.
ちなみにここでテストデータもやっとく

wine-learning.py
Xt = wine_test.loc[:,['Alcohol','Malic acid','Ash','Alcalinity of ash','Magnesium','Total phenols','Flavanoids','Nonflavanoid ohenols','Proanthocyanins','Color intensity','Hue','OD280/OD315 of diluted wines','Proline']].values

学習データとテストデータに分割

wine-learning.py
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.2)

今回もデータを8:2の割合で分割して行った.

欠損値の削除

wine-learning.py
X_train = X_train[:, ~np.isnan(X_train).any(axis=0)]
X_test = X_test[:, ~np.isnan(X_test).any(axis=0)]
Xt = Xt[:, ~np.isnan(Xt).any(axis=0)]

分割前まではなかった欠損値が突然現れた. 原因がわからなかったので,そのうち検証してみようと思う.
今回は欠損値を削除する方針にした.

モデルの学習

SVC
wine-learning.py
clf = svm.SVC()
clf.fit(X_train, y_train)
ロジスティック回帰
wine-learning.py
clf = LogisticRegression()
clf.fit(X_train, y_train)
ランダムフォレスト
wine-learning.py
clf = RandomForestClassifier(n_estimators=500, random_state=0)
clf.fit(X_train, y_train)

random_stateは0,n_estimators(決定木の個数)は多めに500を設定した.

モデルの評価

wine-learning.py
y_pred = clf.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print('正解率 = ' , accuracy)

例のごとく, accuracy関数を用いて正解率を出した。

SVCの正解率
正解率 =  0.6111111111111112
ロジスティック回帰の正解率
正解率 =  0.8888888888888888
ランダムフォレストの正解率
正解率 =  1.0

分類

wine-learning.py
X_pred = np.array(Xt)
y_pred = clf.predict(X_pred)
print(y_pred)

結果

image.png

やりました(パチパチ)

検討事項

  • 変数の量によって学習アルゴリズムを変える.
  • 変数の数を落とす(次元削除)作業を行ってから学習アルゴリズムを適用する.
  • そもそものアルゴリズムの特性を理解する.
  • データ分割の時点で発生した欠損値の原因追及
  • 混合行列の採用
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonで問題解いてみた Vol.1

はじめに

今回からAtCoderににある問題を解いて、定期的にその問題の内容や
解説を投稿していこうと思います:relaxed:

何回続くかも分からないし需要があるかも分からないですが、誰かが
問題に詰まった時にお役に立てたら幸いです:kissing_smiling_eyes:

では、今回からゆるーく続けていきたいと思うのでよろしくお願いします:grin:

早速いきます!

今回は3問解いてみました!
それでは早速問題の説明・解説していきます:stuck_out_tongue_winking_eye:

「9x9」

問題文

高橋君は九九を覚えたので、1以上9以下の2つの整数の積を計算することが
できます。それ以外の計算はできません。2 つの整数A,Bが与えられます。
高橋君がA×Bを計算できるならその結果を、できないなら代わりに-1を
出力してください。

制約

1 ≤ A ≤ 20
1 ≤ B ≤ 20
入力中のすべての値は整数である。

入力

入力は以下の形式で標準入力から与えられる。
A,B

出力

高橋君が A × Bを計算できるならその結果を、できないなら-1を出力せよ。

という問題でした。私の答えは ↓ の通りです。

9x9.py
A , B = map(int, input().split())

if 1 <= A and A <= 9 and 1 <= B and B <= 9:
  print(A * B)
else:
  print(-1)

カンタンに説明しますね:wink:

まずは"変数 , 変数 = map(型,input().split())"の形で二つの変数に
標準入力から入力させます。".split()"を使うと、スペースで区切って
入力された文字列を分割して入力できます。ちなみに".sprit("と")"
という風に入力すると"と"という文字で区切ってくれます:grin:

次に"if"と"and"を使って複数の条件を作り、それに当てはまった場合に
"print"でAとBの掛け算の結果を出力させます。
条件に合わなかった場合は"-1"を出力します。

以上です!簡単ですね:stuck_out_tongue_winking_eye:
次行きましょう:laughing:

「Curtain」

問題文

高橋君の家の窓の横方向の長さは A であり、横方向の長さが B の
カーテンが 2 枚取り付けられています。(カーテンは縦方向には
窓を覆うのに十分な長さがあります。)

窓のうちカーテンで隠されていない部分の横方向の長さが最小に
なるようにカーテンを閉めます。 このとき、窓のカーテンで隠されて
いない部分の合計の横方向の長さを求めてください。

制約

1 ≤ A ≤ 100
1 ≤ B ≤ 100
A , B は整数である。

入力

入力は以下の形式で標準入力から与えられる。
A , B

出力

窓のうちカーテンで隠されていない部分の合計の横方向の長さを出力せよ。
窓がカーテンで完全に隠される場合は 0 と出力してください。
各カーテンが窓の横幅より長い場合があります。

という問題でした。私の答えは ↓ です。

Curtain.py
A , B = map( int, input().split())

if(A - (B * 2) <= 0):
  print(0)
else:
  print(A - (B * 2))

これはまず、"変数 , 変数 = map(型,input().split())"の形で二つの変数に
標準入力から入力させます。"split()"なので空白区切り文字として入力です:point_up:
次に"if(条件)"の文でカーテンで窓が隠れるか隠れないかの判断をし、
隠れた場合は"0"、隠れなかった場合は隠れていない横の長さ"A - (B * 2)"
を"print()"文で表示します。

以上です。これまた簡単ですね:grin:
次で今回のラストです!

「Tenki」

問題文

ある 3 日間の天気予報が、長さ 3 の文字列 S として与えられます。

S の i(1 ≤ i ≤ 3) 文字目が S のとき、i 日目の天気予報が晴れだったことを、
C のときは曇りだったことを、R のときは雨だったことを意味します。
また 3 日間の実際の天気が、長さ 3 の文字列 T として与えられます。
T の i(1 ≤ i ≤ 3) 文字目が S のとき、i 日目の実際の天気が晴れだったことを、
C のときは曇りだったことを、R のときは雨だったことを意味します。
3 日間で天気予報が的中した日が何日あるかを出力してください。

制約

S および T は長さ 3 の文字列である。
S および T は S, C, R のみからなる。

入力

入力は以下の形式で標準入力から与えられる。
S , T

出力

3 日間で天気予報が的中した日が何日あるかを出力せよ。

という問題でした。私の答えは ↓ です。

Tenki.py
S = list(input())
T = list(input())

i = 0
x = 0

while(i < 3):
    if(S[i] == T[i]):
        x = x + 1
    i = i + 1

print(x)

この問題では、まずは"list(input())"で S と T に文字列をリストへ標準入力から入力
させました。
次に文字を3回比較するために変数"i"を、比較して一致した数を数えるための変数"x"を
用意しました。
あとは"while(条件)"の文で条件を"i < 1"にして処理が終わるたびに"i = i + 1"を
することで処理を3回繰り返させ、 S[] と T[] の配列の中身を順に比較して一致した場合
"x = x + 1"の処理をします。
whileから抜けたら x を"print()"で表示させることで一致した回数を表示させることが
できました:relaxed:

終わり

今回は3つの簡単な問題を解いてみました。
正直ちょっとプログラミングが分かる人なら簡単すぎる問題だったと思います:joy:
こんな感じでゆるーく書いていこうと思うので分かりにくいところや無駄な書き方が
あったらコメントで教えていただけると私の勉強にもなってうれしいです:blush:
今回はここまで!:blush::joy::kissing:

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

python csvデータを使用した計算問題

pythonでcsvデータを使用した計算を考えております。

具体的な内容としては、

1.csvファイルのgoalに着目し、異なる数字を5行抽出します。
df = pd.read_csv("start_goal.cost.csv")
df[0:15]

2.やりたいこととしては、同じgoalの値を持つ3つのstart(A,B,C)の中からcostが最小(>0)となるものを抽出したいです。
これを5回(*)続け、costの総和が最小となる時のstart-goalの組み合わせを新たなcsvファイルに出力したいです。
((*)Aを選択できる回数は2回、Bを選択できる回数は2回、Cを選択できる回数は1回とします。⇒計5回)

3.最初にcsvファイルから抽出した5行で5つの組み合わせが見つかればそこで終了としますが、5つの組み合わせが見つからない場合には再度csvファイルから1.で抽出した5行の次の5行を抽出し、残りの組み合わせを探します。(今回示したcsvデータでは、最初に抽出した5行で組み合わせが4つ決定する為、次の5行を抽出し、新たに抽出したデータを上から見ていき、組み合わせを決定することとします。(goalが66493,65661の時にcostが全て"-"になる為、少なくともgoalが88197,条件(*)によっては、72235まで検討を行うことになります。)

start_goal.csv
start,goal,detime,time,cost
A,84603,0.185663,0.155261921,0.160665765
B,84603,0.185663,0.306146838,0.009780848
C,84603,0.185663,0.222079925,0.093847761
A,69731,0.205922,0.290933359,0.157529338
B,69731,0.205922,0.153464999,0.294997698
C,69731,0.205922,0.234028039,0.214434658
A,66493,0.439999,0.232340368,-0.146627625
B,66493,0.439999,0.344083618,-0.258370875
C,66493,0.439999,0.233360601,-0.147647858
A,69083,0.770402,0.319535738,0.008896064
B,69083,0.770402,0.132349783,0.196082019
C,69083,0.770402,0.239396713,0.089035089
A,68044,1.96948,0.341085585,0.004103105
B,68044,1.96948,0.150513608,0.194675082
C,68044,1.96948,0.250685835,0.094502855
A,65661,2.43745,0.241541613,-0.252471114
B,65661,2.43745,0.338684945,-0.349614446
C,65661,2.43745,0.212515961,-0.223445462
A,88197,2.46186,0.182284001,0.001912936
B,88197,2.46186,0.287367027,-0.10317009
C,88197,2.46186,0.191792194,-0.007595257
A,54999,3.70759,0.325967326,-0.093275414
B,54999,3.70759,0.159753126,0.072938786
C,54999,3.70759,0.287357272,-0.05466536
A,72235,4.28023,0.289104207,0.025599918
B,72235,4.28023,0.167769969,0.146934156
C,72235,4.28023,0.250494153,0.064209972
A,95669,4.86535,0.223288997,0.114644782
B,95669,4.86535,0.299277266,0.038656513
C,95669,4.86535,0.183266565,0.154667214
A,67547,8.44938,0.223364342,-1.628245716
B,67547,8.44938,0.253998032,-1.658879406
C,67547,8.44938,0.152691651,-1.557573025
A,89495,10.035,0.261715505,-1.086858336
B,89495,10.035,0.265321578,-1.090464409
C,89495,10.035,0.132722503,-0.957865334

2.3.で示した手順をどのように表現すればよいかがわからず悩んでおります。
アイデア等ある方がいらっしゃいましたら、ご教授いただけましたら幸いです。

どうぞよろしくお願いいたします。

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

matplotlibでヒストグラムの左側の縦軸を度数、右側の縦軸を相対度数にする(邪道なやり方かも)

この記事で行うこと

この記事の続編です→「matplotlibでヒストグラムの縦軸を相対度数(棒の高さの合計=1)や相対度数密度(ヒストグラム全体の面積=1)にする」

matplotlibで、ヒストグラムを

  • 左側の縦軸を度数(matplotlibのデフォルト)
  • 右側の縦軸を相対度数(棒の高さの合計=1)

にして描画します。ただし、邪道なやり方かもしれません。

参考ページ(感謝します)

[python]matplotlibで左右に2つの軸があるグラフを書く方法

ソースコード(Jupyter Notebook)と描画結果

データ件数は7500件で、平均値50、標準偏差10の正規乱数にしました。
まず以下のPythonコードを実行してみます。

#%%

import numpy as np
import matplotlib.pyplot as plt


#%%

# データ作成
μ = 50
σ = 10
data = [ np.random.normal(μ, σ) for i in range(7500) ]


#%%

# 階級数
num_bins = 20

# グラフ描画
fig = plt.figure(figsize=(12, 8))
plt.legend()
plt.xlabel('x', fontsize=18)
plt.title('null', fontsize=18)

# (1) 縦軸を度数にしたヒストグラム
ax1 = fig.add_subplot(111)
ax1.set_ylabel('frequency', fontsize=18)
ax1.grid(True, color="dimgray")
ax1.set_axisbelow(True)    # gridを最背面に移動
ax1.hist(data, bins=num_bins, rwidth=0.5, align="mid")

# (2) 縦軸を相対度数にしたヒストグラム
ax2 = ax1.twinx()
ax2.set_ylabel('relative frequency', fontsize=18)
ax2.grid(True, color="lightgrey", linestyle="--")
ax2.set_axisbelow(True)    # gridを最背面に移動
weights = np.ones_like(data) / len(data)
ax2.hist(data, bins=num_bins, weights=weights, rwidth=0.5, align="right", color="red")

縦軸=度数の棒をデフォルトの色で、縦軸=相対度数の棒を赤で、左右に並べて描画しました。
この両者の高さが同じに見えることを人が確認します(邪道たる所以..)

1.png

度数と相対度数の棒の高さが同じに見えたら、棒の色を一致させます。
「color="red"」を指定せずに、ax2.hist()を呼び出します。

2.png

ax2のGrid線が、ax1の棒より手前に表示されてしまいました。
この横線を見えなくするために、以下のコードを追加してax1と同じグラフを上書き描画することにしました。

# (3) (1)の見栄えを調整
ax3 = ax1.twinx()
ax3.set_yticklabels([]) 
ax3.hist(data, bins=num_bins, rwidth=0.5, align="mid")

3.png

完成版のコード全体は以下の通りです。

#%%

import numpy as np
import matplotlib.pyplot as plt


#%%

# データ作成
μ = 50
σ = 10
data = [ np.random.normal(μ, σ) for i in range(7500) ]


#%%

# 階級数
num_bins = 20

# グラフ描画
fig = plt.figure(figsize=(12, 8))
plt.legend()
plt.xlabel('x', fontsize=18)
plt.title('null', fontsize=18)

# (1) 縦軸を度数にしたヒストグラム
ax1 = fig.add_subplot(111)
ax1.set_ylabel('frequency', fontsize=18)
ax1.grid(True, color="dimgray")
ax1.set_axisbelow(True)    # gridを最背面に移動
ax1.hist(data, bins=num_bins, rwidth=0.5, align="mid")

# (2) 縦軸を相対度数にしたヒストグラム
ax2 = ax1.twinx()
ax2.set_ylabel('relative frequency', fontsize=18)
ax2.grid(True, color="lightgrey", linestyle="--")
ax2.set_axisbelow(True)    # gridを最背面に移動
weights = np.ones_like(data) / len(data)
ax2.hist(data, bins=num_bins, weights=weights, rwidth=0.5, align="right")

# (3) (1)の見栄えを調整
ax3 = ax1.twinx()
ax3.set_yticklabels([]) 
ax3.hist(data, bins=num_bins, rwidth=0.5, align="mid")

邪道な感じなので、正攻法があれば教えてほしいです。

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

matplotlibでヒストグラムの縦軸の左側を度数、右側を相対度数にする(邪道なやり方かも)

この記事で行うこと

この記事の続編です→「matplotlibでヒストグラムの縦軸を相対度数(棒の高さの合計=1)や相対度数密度(ヒストグラム全体の面積=1)にする」

matplotlibで、ヒストグラムを

  • 縦軸の左側を度数(matplotlibのデフォルト)
  • 縦軸の右側を相対度数(棒の高さの合計=1)

にして描画します。ただし、邪道なやり方かもしれません。

参考ページ(感謝します)

[python]matplotlibで左右に2つの軸があるグラフを書く方法

ソースコード(Jupyter Notebook)と描画結果

データ件数は7500件で、平均値50、標準偏差10の正規乱数にしました。
まず以下のPythonコードを実行してみます。

#%%

import numpy as np
import matplotlib.pyplot as plt


#%%

# データ作成
μ = 50
σ = 10
data = [ np.random.normal(μ, σ) for i in range(7500) ]


#%%

# 階級数
num_bins = 20

# グラフ描画
fig = plt.figure(figsize=(12, 8))
plt.legend()
plt.xlabel('x', fontsize=18)
plt.title('null', fontsize=18)

# (1) 縦軸を度数にしたヒストグラム
ax1 = fig.add_subplot(111)
ax1.set_ylabel('frequency', fontsize=18)
ax1.grid(True, color="dimgray")
ax1.set_axisbelow(True)    # gridを最背面に移動
ax1.hist(data, bins=num_bins, rwidth=0.5, align="mid")

# (2) 縦軸を相対度数にしたヒストグラム
ax2 = ax1.twinx()
ax2.set_ylabel('relative frequency', fontsize=18)
ax2.grid(True, color="lightgrey", linestyle="--")
ax2.set_axisbelow(True)    # gridを最背面に移動
weights = np.ones_like(data) / len(data)
ax2.hist(data, bins=num_bins, weights=weights, rwidth=0.5, align="right", color="red")

縦軸=度数の棒をデフォルトの色で、縦軸=相対度数の棒を赤で、左右に並べて描画しました。
この両者の高さが同じに見えることを人が確認します(邪道たる所以..)

1.png

度数と相対度数の棒の高さが同じに見えたら、棒の色を一致させます。
「color="red"」を指定せずに、ax2.hist()を呼び出します。

2.png

ax2のGrid線が、ax1の棒より手前に表示されてしまいました。
この横線を見えなくするために、以下のコードを追加してax1と同じグラフを上書き描画することにしました。

# (3) (1)の見栄えを調整
ax3 = ax1.twinx()
ax3.set_yticklabels([]) 
ax3.hist(data, bins=num_bins, rwidth=0.5, align="mid")

3.png

完成版のコード全体は以下の通りです。

#%%

import numpy as np
import matplotlib.pyplot as plt


#%%

# データ作成
μ = 50
σ = 10
data = [ np.random.normal(μ, σ) for i in range(7500) ]


#%%

# 階級数
num_bins = 20

# グラフ描画
fig = plt.figure(figsize=(12, 8))
plt.legend()
plt.xlabel('x', fontsize=18)
plt.title('null', fontsize=18)

# (1) 縦軸を度数にしたヒストグラム
ax1 = fig.add_subplot(111)
ax1.set_ylabel('frequency', fontsize=18)
ax1.grid(True, color="dimgray")
ax1.set_axisbelow(True)    # gridを最背面に移動
ax1.hist(data, bins=num_bins, rwidth=0.5, align="mid")

# (2) 縦軸を相対度数にしたヒストグラム
ax2 = ax1.twinx()
ax2.set_ylabel('relative frequency', fontsize=18)
ax2.grid(True, color="lightgrey", linestyle="--")
ax2.set_axisbelow(True)    # gridを最背面に移動
weights = np.ones_like(data) / len(data)
ax2.hist(data, bins=num_bins, weights=weights, rwidth=0.5, align="right")

# (3) (1)の見栄えを調整
ax3 = ax1.twinx()
ax3.set_yticklabels([]) 
ax3.hist(data, bins=num_bins, rwidth=0.5, align="mid")

邪道な感じなので、正攻法があれば教えてほしいです。

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

PythonでX線回折シミュレーション

It’s !
どうもこんにちは。二回目の投稿です。今回はPythonでX線回折のシミュレーションをしてみました。コード全文(Jupyter Notebookを使っております)はGitHub1
に載せています。間違っている点などがあれば、ご指摘いただけると幸いです。

X線回折(X-Ray Diffraction, XRD)

X線回折とは、X線が結晶中で回折する現象のことです。X線とは、波長が一定範囲内(1 nm ~ 1 pm程度)の電磁波のことで、回折とは、波の性質を持つものが、障害物を廻り込んで伝播することです。波は回折すると、互いに干渉することで強め合うところと相殺するところができ、縞状に分布するようになります。X線回折による分析では、回折によって生じた模様を解析することで対象の結晶構造を特定します。
ちなみに、回折現象はX線だけでなく、電子線や中性子線でも起き、それらを用いた結晶構造解析法も広く使われています。

X線回折による結晶構造解析の原理

X線回折によってできた模様では、回折による干渉によって強めあったところ(回折条件を満たしたところ)がスポットとして得られます。回折条件と結晶構造の関係をブラッグ条件といい、
$$2dsin{\theta} = n\lambda$$
で表されます。dは結晶中の原子がつくる面(結晶面)同士の間隔、$\theta$は結晶面と入射X線のなす角度、$\lambda$はX線の波長です。

原子散乱因子と結晶構造因子

先に説明したブラッグ条件では、X線を散乱する原子を点としていますが、実際にX線を散乱するのは、原子のまわりに分布している電子雲であり、散乱したX線の振幅は、(弾性散乱であるとすれば)散乱された位置での電子密度に比例します。そのため、原子によって散乱したX線の振幅fは、
$$f = \int{\rho(\vec{r})e^{2π\vec{k}\vec{r}}} dV$$
となります。この式は、fは電子密度$\rho(\vec{r})$を散乱ベクトル$\vec{k}$でフーリエ変換することに相当します。散乱ベクトルとは、逆空間中に成分(h, k, l)で定義されるベクトルのことです。このfを原子散乱因子と言います。原子散乱因子を求めるには、電子密度が必要ですが、原子のまわりの電子密度は単純な場合を除いて解析的に求めることができないため、ハートリー-フォック法などで近似計算されます。各原子の原子散乱因子はすでに求められ、データベース化されているため、今回の計算ではそれらを用います。

さらに、結晶全体においても、同様の考え方を使うことができます。結晶中の電子密度をフーリエ変換することで結晶構造因子Fが得られます。
$$F(\vec{k}) = \int{\rho(\vec{r})e^{2π\vec{k}\vec{r}}} dV$$
式が原子散乱因子と一緒ですね。しかし、結晶中においては、原子位置の周辺にしか電子は分布していないので、これを原子の座標(x, y, z)と原子散乱因子fで書き換えることができ、
$$F(hkl) = \sum_{j}{f_jT_je^{2πi(hx_j + ky_j + lz_j)}}$$
となります。h, k, lは散乱ベクトルkの成分で、逆空間での格子点(逆格子点)の座標でもあります。
$T_j$は後ほど説明しますが、デバイ-ワラー因子というものです。

プログラム

ここからが実際の実装方法になります。
まず、逆格子点hklを列挙します。本来であれば、エワルト球上の点のみを挙げればいいのですが、X線の波長ではかなり幅広い範囲がエワルト球に載ってしまうので、なるだけ多く列挙します。GitHubにあるhkl.csvは列挙されたhklのファイルです。
結晶の単位格子の原子座標も必要になります。こちらは、単位格子内の原子だけで充分です。今回は、例として面心立方格子(fcc)の座標を用いています。GitHubではpos.csvという名前のファイルになっています。

次に、格子定数a, b, c, $\alpha$, $\beta$, $\gamma$から(これらはパラメータとしてあらかじめ持っておく)、実空間での格子テンソル$G$を組み立てます。
$$
G = \left(
\begin{matrix}
a^{2} & ab\cos\gamma & ac\cos\beta\\
ba\cos\gamma & b^{2} & ba\cos\alpha\\
ca\cos\beta & cb\cos\alpha & c^{2}
\end{matrix}
\right)
$$
次に、
$$G^{\ast} = G^{-1}$$
から、この格子テンソルの逆行列をとれば、逆空間での格子テンソル$G^{\ast}$が求まります。

G = [[a**2, a*b*np.cos(gamma), a*c*np.cos(beta)], [b*a*np.cos(gamma), b**2, b*a*np.cos(alpha)], [c*a*np.cos(beta), c*b*np.cos(alpha), c**2]]
invG = np.linalg.inv(G)

この逆格子テンソル$G^{\ast}$を使って、逆格子点hklでの反射Kに対する逆格子ベクトルの長さ
$$|\vec{r}| = |ha^{\ast} + kb^{\ast} + lc^{\ast}|$$
を求めます。
$$
|\vec{r}| = |ha^{\ast} + kb^{\ast} + lc^{\ast}| = h^{\mathrm{T}}G^{\ast}h$$

hkl = [h[i], k[i], l[i]]
thkl = np.transpose(hkl)
dk = 1/((np.matmul(np.matmul(invG, thkl), hkl))**(1/2))

|r|とブラッグ条件から、反射Kでの面間隔dkとブラッグ角θkが求まります。
$$d_k = \frac{1}{|\vec{r}|^2}$$
$$\theta_k = \arcsin(\frac{λ}{2d_k})$$

sinthetak = lamb/(2*dk)
thetak = np.rad2deg(np.arcsin(sinthetak))

原子散乱因子にはデータベースの値を使いますが、原子散乱因子はsinθ/λに依存する関数なので、データベースに載っているのはその係数だけです。係数から組み立てるには、
$$f(\frac{\sin\theta}{\lambda}) = \sum_{j=1}^{4}{a_j e^{-b_j(\frac{\sin\theta}{λ})^2}} + c$$
とします。$a_j$, $b_j$, cが係数です。プログラムでは、Na原子を想定してデータを用いました。

ここで、デバイ-ワラー因子について説明しなければなりません。
デバイ-ワラー因子とは、熱振動の影響を補正する因子です。等方的な熱振動のデバイ-ワラー因子は
$$T = e^{-B(\frac{\sin\theta}{\lambda})^2}$$
で表されます。Bは等方性原子変位パラメータというものです。
熱振動が等方的でない場合(異方性と言いますが)、より複雑で、$3\times3$対称行列$\beta$を用いて
$$T = e^{-h^{\mathrm{T}} β h}$$
となります。
今回は等方的な熱振動を考え、B = 2.0にしています。原子変位パラメータの値を探したのですが、見つからず、適当な値です。すいません。

原子散乱因子と結晶の単位格子の原子座標をもとに、結晶構造因子を計算します。
$$F_i(x, y, z, h_i, k_i, l_i, f_i) = f_i\sum_{j}{e^{2πi(hx_j + ky_j + lz_j)}}$$
見ての通り、結晶構造因子は複素数になります。

求めた結晶構造因子の絶対値の二乗(散乱振幅の絶対値の二乗なので、散乱強度になる)を縦軸、$2\theta$を横軸としてプロットすれば、X線回折で得られるようなプロットが得られます。X線回折を測定する機械では、試料と測定部とが互いに逆回りに$\theta$づつ回転し測定するため、実際の移動角度は$2\theta$となり、プロットでも横軸を$2\theta$で取ることが習慣となっています。

結果

プロットすると以下のようになります。
xrd_sim1.png

ブラッグ角は小数点2桁までで丸めています。
このプロットでは、ブラッグ条件を満たす反射の角度でのみデルタ関数的に散乱強度が出ていますが、実際のX線回折では、散乱強度のピークは幅を持っています。そのピークの広がりを再現してみましょう。

デルタ関数に広がりを持たせたいときに、まず思いつくのは、ガウス関数(ガウシアン)による畳み込みでしょう。いわゆる、ガウシアンコンボリューションというやつです。
さっきのプロットに、ガウス関数を畳み込んだものが以下になります。分散は適当にそれっぽく見える値を入れています。
xrd_sim2.png
裾野に少し幅ができたのがわかると思います。X線回折の計測データを見たことがある人ならわかると思いますが、実際の物に比べて、このプロットは少し裾野が狭いように見えます。実はガウス関数は、粒子の熱振動などの挙動を表現するには適していますが、このようなスペクトルのピークの表現には適していません。
そこで、スペクトルのピークの表現に使われるローレンツ関数で畳み込みをしてみます。ローレンツ関数は以下のような関数です。
$$L = \frac{1}{1 + (\frac{x}{γ})^2}$$
$\gamma$は半値幅といい、ピーク高さの1/2でのピーク幅です。このパラメータで関数の広がり方が変わります。

def lorentzian(theta, gamma):
    lorentz = 1/(1 + (theta/gamma)**2)
    return lorentz

実際に畳み込んだものが以下です。またも半値幅の値はそれっぽく適当な値です。
xrd_sim3.png
先ほどのガウス関数よりも裾野が広くなりました。X線回折のピークっぽくなりましたね。
と言っても、ローレンツ関数だけでは正確ではありません。結晶内の原子が温度によって振動するために(たとえ絶対零度であっても零点振動があります)、ガウス関数的な挙動の熱振動もピークの広がりに影響を与えます。よってローレンツ関数とガウス関数を任意の重さで畳み込んだもので、より正確にピークを表現できます。

ローレンツ関数とガウス関数を畳み込んだものをフォークト関数と言います。フォークト関数は愚直にローレンツ関数とガウス関数を畳み込んで作るよりも、複素誤差関数を用いて作る方が早いです。筆者は勉強不足でよくわかっていませんが、なんか複素誤差関数の実部がフォークト関数っぽくなっているみたいです。2
ちょうどscipyに複素誤差関数を生成してくれる関数があるらしいので、今回はそれを使いました。

def voigt(theta, Hg, Hl):
    z = (theta + 1j*Hl)/(Hg * np.sqrt(2.0))
    w = scipy.special.wofz(z)
    v = (w.real)/(Hg * np.sqrt(2.0*np.pi))   
    return v

フォークト関数でピークの広がりを表現したものが以下です。
xrd_sim4.png
やってみたはいいものの、広がりをローレンツ関数のみで表現したものの方がそれっぽいですね。フォークト関数はパラメータとしてガウス関数部、ローレンツ関数部それぞれの半値幅が必要ですが、今回は適当に設定しています。半値幅を理論的に近づけることで、実際のピークの広がりに近づくかもしれません。

回折像

X線回折においては、上記のようなプロットの他に、回折像と言われる、X線の回折をそのまま写しとった像が得られます。回折像では逆格子が見て取れるので、今回はおまけとして作ってみようと思います。

実装としては、任意の大きさでnumpy.zeros()の配列を生成しておいて、反射の条件を満たすところのみ|F|^2の値を入れ、スポットっぽく見えるようにガウス関数を畳み込みます。

def gaussian(theta, lamb):
    gauss = (1/(2*np.pi*sigma))*np.exp(-(theta**2 + lamb**2)/(2*sigma))
    return gauss

回折像は入射X線の方向に対して垂直な逆格子点のみがスポットとして出てきます。よって、今までの条件に加えて、入射X線方向についても考慮しなければなりません。入射X線方向を[001]として、内積をとって0になる反射のみを取り出します。この処理は[001]だと楽ですが、入射方向が[110]や[111]などでは複雑な処理が必要でしょう。
Pythonでは、配列をそのままmatplotlib.pyplot.imshow()で画像にできますので、白黒のコントラストで画像にします。

t, l = np.meshgrid(np.arange(-1, 1, 0.1), np.arange(-1, 1, 0.1))
g = gaussian(t, l)
xrdimage = scipy.signal.convolve2d(I, g, boundary='wrap', mode='same')
plt.imshow(xrdimage, interpolation="nearest", cmap=plt.cm.gray)

便利ですね。fccでの像は下のようになりました。
xrd_sim5.png

それっぽいですね。さっきからそれっぽいしか言っていませんね。体心立方格子(bcc)でもやってみました。
xrd_sim6.png
fccより楽しい感じになりましたね。

まとめ

X線回折のシミュレーションをしてみました。回折理論が複雑ですが、コンピュータでする処理自体は簡単ですので、X線回折を理解するにはちょうどいい教材かもしれません。
実を言うと、精密なシミュレーションをするには、今回実装したものよりも多くの因子を考えなければなりません(ローレンツ・偏光因子や選択配向関数など)。加えて、今回適当に設定したパラメータ(原子変位パラメータ、フォークト関数の半値幅)も本来は正確な値を使うべきです。では、これらのパラメータは理論的に求めることができるのでしょうか。これらのパラメータは様々な要因が複雑に影響し合ってできているため、支配的な要因から類推はできても、解析的に求めるのは難しいでしょう。
上記のようなパラメータの正確な値を得るためには、実験値とシミュレーションをすり合わせ、誤差を減らしていく、という手法を用います。そこで、前回の記事3
で紹介した、非線形最小二乗法が活躍するわけです。

参考文献

今回の記事は、以下の記事や書籍を元に作成しています。勝手ながら紹介させていただきます。

コード

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

Conda 仮想環境の操作 メモ

Condaを使ったPythonの仮想環境「作成・アクティベート・削除・コピー(複製)」の簡易メモです。

仮想環境の作成

$ conda create -n 環境名
$ conda create -n 環境名 python=3.6
SSLErrorが出た場合、パスがすべて通っているか確かめてみてください。
Windowsでは「Anaconda3\Library\bin」を設定していないとエラーになります。

アクティベート

$ activate 環境名

環境から出る

$ deactivate

環境の削除

$conda env remove -n 環境名

環境のコピー

conda create -n 新規環境 --clone 複製元環境名

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

配列の要素内で最長の文字長を取得する

これより短く書く方法が思いつかなかったので
あったら教えてください?

li = ["あ", "いうえお", "かき", "くけこ"]
print(len(max(li, key=len)))
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

matplotlibで作図入門: 簡単な関数を書く

この記事を読むとできるようになること
matplotlibで超基本的な図が描ける

gnuplot で図が作れるようになってしばらくすると,「もっと簡単に綺麗な図が描きたい」と思うようになると思います(※個人差があります).
研究発表の図を作るのに無料で使えるツールとして有名なものの1つがmatlotlibです.かなり綺麗な図が描けます(※個人の意見).
この記事では,すぐに使えるmatplotlibでの図の描き方の基礎中の基礎だけをまとめました.

より詳細な使い方については,
pythonのmatplotlibの使い方をまとめてみた
が大変わかりやすく,参考になります.

特に,オブジェクト指向インターフェースは便利ですので使えるようになると良いです.

  • 環境
    • macOS mojave 10.14.6
    • Python 3.7.5

ここではmatplotlibを用いて簡単な関数を描いてみましょう
$y = f(x)$ を描く時にコンピューター上で何が行われているかというと
(わかりやすさのために用語の正確さは無視すると),
$x$の組(配列)の要素$x_i$ のそれぞれに対し,
$y$の組(配列)の要素$y_i$ が計算されて,その組がプロットされています.
gnuplot でも同じです.
(なので,特に対数プロットにおいて,変化が急な部分ではプロット点を細かくとってあげないと正しい図にならないことがあります)

Python では次のようにして組$x$を作ることができます.
これは等差数列の配列を作っていると思ってください.

x = numpy.arange(-10, 10, 0.01)
引数は,
x = numpy.arange(xの最小値,xの最大値,公差)
です.
この等差数列(みたいなもの)を使って$y = f(x)$ を描画することができます.
では,試しに放物線を描いてみましょう.

sample.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# これはコード中に全角文字(日本語)を書く時に必要

#以下はおまじないみたいなもの
#numpy に np という名前をつける
import numpy as np
#同様に,matplotlib.pyplot に plt という名前をつける
import matplotlib.pyplot as plt

#x = np.arange(xの最小値,xの最大値,刻み)
x = np.arange(-10, 10, 0.1)
#描画する関数
y = x*x

#横軸にx,縦軸にyをとって描く
plt.plot(x, y)

#plot表示
plt.show()

sample1.png

では,次によく使うオプションをつけていろいろ描いてみましょう.

sample2.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt

x = np.arange(-10, 10, 0.1)
y = x*x

#描画範囲を指定する場合(x軸でも同様)
plt.ylim([0, 50])

#タイトルと軸ラベル
plt.title("TITLE")
plt.xlabel("Xlabel")
plt.ylabel("Ylabel")

#grid表示のon/off (デフォルトはFalse)
plt.grid(True)

#ラベルや線色,太さなどのオプション
plt.plot(x,y, label="legend", color="red", lw=3, ls="--")

#凡例を表示
plt.legend()

#plot表示
plt.show()

sample2.png

図を保存するには,最後に
plt.savefig("sample_figure.eps")
などと書けばOKです.

ここで紹介したものは一例で,他のオプション(線色とか線種とか凡例の位置とかその他いろいろ)はググって必要なもの/お気に入りものを見つけてみてください.

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