Kuzunoha-NEのブログ

プログラミングなどの勉強をしてます

【Python】 インスタンス変数を辞書型で出力 + JSON形式で文字列にする。

こんばんは、葛の葉です。

Pythonインスタンス変数を辞書型で出力する方法とそれをJSONに変換する方法を記載します。

環境

Python 3.6.5

こんな感じ

class MyClass(object):
    def __init__(self, name):
        self.name = name
        self.gender = 'male'
        self._address = '192-1999'
        self.interest = ['Kemono Friends', 'DoDonpachi', 'Python']
        self.age = 111

myclass = MyClass('kuzunoha')

このようにクラスとインスタンスを作成する。

print(myclass.__dict__)

myclass.__dict__インスタンス変数を辞書型として持っている。

{'name': 'kuzunoha', 'gender': 'male', '_address': '192-1999', 'interest': ['Kemono Friends', 'DoDonpachi', 'Python'], 'age': 111}

JSON形式文字列にする。

内部modulejsonjson.dumpsを使ってJSON型にする。

import json # ここ追加になっている点に注意

class MyClass(object):
    def __init__(self, name):
        self.name = name
        self.gender = 'male'
        self._address = '192-1999'
        self.interest = ['Kemono Friends', 'DoDonpachi', 'Python']
        self.age = 111

    def to_json(self):
        return json.dumps(self.__dict__, ensure_ascii=False, indent=4) 

myclass = MyClass('kuzunoha')

json.dumps(self.__dict__, ensure_ascii=False, indent=4)jsonモジュールを使って辞書型をjsonに変更しています。

オプションのensure_ascii=Falseはこれを打たないとバイト文字になってしまうので、そのようにしています。

indent=4はインデントを空白4つで自動で行ってくれます。自動ってすごい。かしこい。

print(myclass.to_json())で出力してみましょう。

{
    "name": "kuzunoha",
    "gender": "male",
    "_address": "192-1999",
    "interest": [
        "Kemono Friends",
        "DoDonpachi",
        "Python"
    ],
    "age": 111
}

上記の通り出ている。けれども、少し問題なのがアンダースコア_で定義したインスタンス変数もそのまま出力されている点かも?

それが嫌なら、一度、インスタンス変数の辞書をコピーして、そこから_addressを抜き取ってみる?

    def to_json(self):
        dicts = self.__dict__.copy()
        # 以下の要素はjsonには要らないので削除
        dicts.pop('_address')

        return json.dumps(dicts, ensure_ascii=False, indent=4)
myclass = MyClass('kuzunoha')

print(myclass.to_json())

{
    "name": "kuzunoha",
    "gender": "male",
    "interest": [
        "Kemono Friends",
        "DoDonpachi",
        "Python"
    ],
    "age": 111
}

【Docker】Windowsで「curl」コマンド使いたい件について[byrnedo/alpine-curl]

お世話になっております。葛の葉です。

WindowsPowerShellにはInvoke-WebRequestというLinuxBASHでいうところのcurlみたいなコマンドがついていて、何かHTTP系に色んなテストをしてみたいときなんかは有効です。しかし、curlに慣れていたりするとコマンドが打ちにくい、結果もなんだかわかりにくい等、curlがやっぱり使いたい、ってときがあります。

そんなときに便利なDockerImageがbyrnedo/alpine-curlです。

hub.docker.com

使い方は書いてある通り

先のURLにも書いてありますが…まぁ書いておきます。

docker pull byrnedo/alpine-curl

先ず、DockerImageをダウンロードするため、上記のコマンドを打ちます。-tオプションでタグを指定するのもよいと思いますが、まぁ気にしなくてもいいかなぁと思います。ダウンロードが終われば使用可能です。

docker run --rm byrnedo/alpine-curl (url)

このコマンドでHTTPのrequestを確認することができます。

なお、dockerの--rmオプションは、コンテナを通してコマンドを実行し、そのコマンドが完了したときにそのコンテナを消す、というオプションになります。curlコマンドは何回も使うはずなので、そのたびにコンテナのカスが溜まっていくことを防いでいるわけですね。

docker run --rm byrnedo/alpine-curl https://www.google.co.jp

Googleのサイトをcurlで受け取ればソースコードを取得できます。あとはcurlの操作そのまま使えるので調べて使ってみてみてくださいな。

なお、Win版のcurlもある模様

下記URLにあります。一番下のほうにWindows64bit用のリンクがあるので、それを落としてbinフォルダ以下に環境変数PATHを通してあげましょう。PowerShellではcurlコマンドがInvoke-WebRequestにaliasされているので、curl.exeってコマンドになるか、あれこれ設定をしてあげなきゃあいけません。詳しくはググってもらったほうがいいかな。

curl.haxx.se

参考

qiita.com

【Python】Scikitlearnの内LinearRegressionを使った値予測を行う

こんばんは、葛の葉です。機械学習ライブラリであるsklearnを使ってデータの学習と予測…と言いたいところなんだけど、なんだかいいデータがなくて、全く無造作なデータを学習させて予測するという意味のないことやってました。

環境

Anaconda 5.3.0

Python 3.7.0

numpy 1.15.1

pandas 0.23.4

jupyter 1.0.0

scikit-learn 0.19.2

適当なデータを使う。

tekito.csv

data1,data2,data3
22,62,73
22,66,61
76,58,26
35,60,43
35,30,100
61,35,97
50,62,48
87,94,86
95,33,34
35,58,32
79,84,20
82,15,92
50,66,37
62,89,74
53,51,65
74,80,75
89,27,80
100,62,78
97,77,79
88,37,54

data1,data2,data3は共に15から100の数値を適当出力する設定にしているだけなので、なんも意味を持ちません。

重回帰分析を使う

回帰分析のうち、目的変数に対して説明変数が2つ以上のものを重回帰分析といい、説明変数が1つの場合は単回帰分析と言うみたいです。

目的変数とは予測したいデータで今回はdata3です。

説明変数とは目的変数を予測するために使うデータのことdata1,data2です。

data1data2を使ってdata3を予測するというのが今回の目的です。

回帰分析については以下のWikipediaも参照。

回帰分析 - Wikipedia

説明変数のデータ

test.csv

data1,data2
35,77
60,43
45,53
64,65
81,21
90,33
100,46
50,18
30,32
29,60

このtest.csvdata1,data2を使ってdata3に相当するデータを出力する。

JupyterNoteBookを使う

以下はJupyterNoteBook上でのコマンドになります。

ライブラリのインポート

import pandas as pd
import numpy as np # 要らないかも
from sklearn.linear_model import LinearRegression as LR

上記コードをセルに挿入してからShift + Enterすると次のセルに移ります。

その時に何らかのエラーが出ればそれはライブラリのインポートが失敗していると思います。

from sklearn.linear_model import LinearRegression as LR

これが機械学習のライブラリsklearnです。

LinearRegressionは線形回帰を意味しているようです。

as LRとしてLinearRegressionLRとして略称します。

CSVのインポート

pandasでcsvをロードします。

train = pd.read_csv('tekito.csv')
test = pd.read_csv('test.csv')

pandas.read_csv('***.csv')はpandasのライブラリとしてcsvをpandasのDataFrameとして読み込みます。

Shift + Enterで、次のセルへ。

エラーを吐く場合は事前に説明していたCSVが、同じディレクトリ内にないとか、その辺りだとおもいます。

train.head()

.head()は読み込んだDataFrameの最初の5行分を出力します。

data1    data2   data3
0   22  62  73
1   22  66  61
2   76  58  26
3   35  60  43
4   35  30  100

Shift + Enterで、次のセルへ。

特定のカラムのデータを読み取る

trainX = train[['data1', 'data2']]
y = train['data3']
testX = test[['data1','data2']]

train内の2つのカラムを代入しています。

train[['data1', 'data2']]

yは目的変数としtrain['data3']を代入しています。

testXは最終的に予測するための説明変数をもっています。

trainXyを使ってdata1,data2data3で線形回帰を行い、最終的にはtestX内にあるdata1,data2を元にtestXのdata3に当たるデータを出力します。

Shift + Enterで、次のセルへ。

モデルの作成とモデルへの学習

model = LR()
model.fit(trainX, y)

model.fit(trainX, y).fit(説明変数, 目的変数)となっており、回帰モデルとなっています。

trainXdata1data2をもっているので重回帰モデルとなっています。

Shift + Enterで、次のセルへ。

学習したデータを出力する

pred = model.predict(testX)
pred

以下のデータが学習で出したデータになります。

array([55.80356749, 65.91988259, 62.42089327, 60.75553676, 72.7960059 ,70.4459384 , 67.91714972, 71.41497867, 66.58486472, 59.5950097 ])

さっぱり意味がないデータですけどね。

Shift + Enterで、次のセルへ。

ついでにCSVとして出力しましょう。

tester = pd.DataFrame()
tester[0] = pred
tester.to_csv('cho_tekitou.csv', header=None, index=None)

.to_csv関数にてcho_tekitou.csvという名前で出力します。

ここまできていて言うのもなんだが

data1data2は無造作なデータで、当然、法則性なんてないです。なので、最終的なpredの値はとても意味のないものになるはずです。

またpredが正確にデータを予言できているかどうか、というのも保証できません。

とりあえず、データを出せてよかったねってレベルで今回は終わりです。

f:id:Kuzunoha-NE:20190104222556p:plain

【Python】JupyterNotebook + matplotlibで折れ線グラフを出力する

今回はAnaconda + matplotlibを使って折れ線グラフを出力します。

プログラミングの環境

Anaconda 5.3.0

Python 3.7.0

numpy 1.15.1

pandas 0.23.4

matplotlib 2.2.3

jupyter 1.0.0

使うデータ

日本が統計を開始してから2017年までの交通事故の統計データ

政府統計の総合窓口

www.e-stat.go.jp

前準備

データ分析によく使われるpandas numpy matplotlibをライブラリとしてインストールしておく必要あります。

pandasライブラリはpythonの標準の配列機能より強力なものを使えます。

numpyライブラリは計算機能が強い。

matplotlibライブラリは図を表示するもの。

以上のライブラリが必要になります。

なお、今回はAnaconda5.3.0環境でのVersionになります。

Anacondaなら最初から3つのライブラリは入っています。

データを加工する

もともと入っているデータではちょっと表示がやりづらそうだったのでこんな感じで変更

和暦 =>(西暦に変換して) => AD
発生件数(件) =>  Incidents
負傷者数(人)=>  Injured_person
死者数(人)=>  Casualties

それ以外のものは削除しました。

これのデータをCSVとして変更しました。これらは

名前はh29_jiko.csvです。

AD,Incidents,Injured_person,Casualties
1948,21341,17609,3848
1949,25113,20242,3790
1950,33212,25450,4202
。。。。

こんな感じ

jupyter notebookコマンドにてjupyter notebookを起動しましょう。

(JupyterNotebookについてはまた今度)

最初のライン

import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
%matplotlib inline

%matplotlib inline はJupyterNote上でmatplotlibを表示するための設定です。

上記のコードを入力した状態でShift + Enterを押します。

ライブラリがうまくはいっていれば、問題なく次の行に進むはず。

2つ目のライン

jiko29 = pd.read_csv('h29_jiko.csv')

pd.read_csv()pandascsvファイルを読み込む関数です。

h29_jiko.csvという名前でCSVファイルを同じディレクトリに保管しているので、このデータ呼び出し方で呼べます。

Shift + Enterでそのコードを実行。

3つ目のライン

plt.plot(jiko29['AD'],jiko29['Incidents'])

Shift + Enterでコード

f:id:Kuzunoha-NE:20190102205248p:plain
事故件数の折れ線グラフ

と、このように簡単に折れ線グラフを出力できます。

2000年頭くらいから2010年くらいまでの事故数が増えて、それから右肩下がり?のようです。

3つ目のラインを編集

plt.plot(jiko29['AD'],jiko29['Injured_person'])をグラフ内に追加します。(負傷者数)

plt.plot(jiko29['AD'],jiko29['Incidents'])
plt.plot(jiko29['AD'],jiko29['Injured_person'])

f:id:Kuzunoha-NE:20190102210735p:plain
事故件数+負傷者数の折れ線グラフ

このように、データを重ねて表示することも可能です。

一回の事故に対して、負傷者が増えたってこと…であってるのかな?

4つ目にやりたかったけど出来なかったこと

死亡者数のデータを出力しようとすると…

plt.plot(jiko29['AD'],jiko29['Incidents'])
plt.plot(jiko29['AD'],jiko29['Injured_person'])
plt.plot(jiko29['AD'],jiko29['Casualties'])

f:id:Kuzunoha-NE:20190102220340p:plain
事故件数+負傷者数+死者数

緑色のグラフが死者数…だけど、さっぱりデータがわからない。

f:id:Kuzunoha-NE:20190102220438p:plain
死者数

このデータだけ見てみると経緯がわかりやすい。

死者数はだんだんと減っていっている傾向にある、ということ。

二つ軸グラフが使えればデータわかりやすかったんだと思うけど、ちょっと今はわからない。

また調べてやってみよう。

【Python】FlaskのJinja2と別でimportしたJinja2を使って文字の置換を二回行う

こんばんは、葛の葉です。

Flaskの文字置換を二段階にわけて行いたい場合の方法を書きます。

環境

python 3.6.5

flask 1.0.2

jinja2 2.10

やり方の説明

元のhtml -> custom_jinja -> flaskのjinja2 できれいにする。

custom_jinjaでは::で挟んだ変数を読み込む

flaskのjinja2では{{}}で挟んだ変数を読み込む

jinja2とjinja2.Environmentを使う

flask内のjinja2とはのjinja2を使用する。

その別のjinja2を便宜的にカスタムjinjaと呼ぶことにする。

jinja2.Environment()内の引数にある以下の値を変更、それをカスタムjinjaというインスタンスとして扱う。

variable_start_stringは変数を囲う最初の文字

variable_end_stringは変数を囲う最後の文字

custom_jinja = jinja2.Environment(
    loader=jinja2.FileSystemLoader('templates'),
    variable_start_string=':',
    variable_end_string=':'
)

説明が前後したけど、loader=jinja2.FileSystemLoader('templates')はtemplateファイルを読み込むディレクトリ名をtemplatesにしています。

page = custom_jinja.get_template('index.html')

と、このようにすることで、templateファイルを読み込めます。

render_template = pages.render(variable_name='value')

と、することでvariable_nameの変数をtemplateファイルに送ることができます。

こんな感じ

sandbox_flask.py
templates/
    ├index.html
    └layout.html

sandbox_flask.py

from flask import Flask, render_template, render_template_string
import jinja2

app = Flask(__name__)

custom_jinja = jinja2.Environment(
    # 元htmlの保存されているディレクトリ名。flaskと併用するので以下の値。
    loader=jinja2.FileSystemLoader('templates'),

    #デフォルトでは{%にあたるもの。flaskのjinjaと重複しない値にする。
    block_start_string='[{[{[Q',
    #デフォルトでは%}にあたるもの。flaskのjinjaと重複しない値にする。
    block_end_string='Q]}]}]', 
    
    # このvariable...と続くステータスを挟みたい文字にする。
    variable_start_string=':',
    variable_end_string=':',

    # オートエスケープは特殊文字等をエスケープします。
    autoescape=False)


# 定数のようなもの。:Key: -> Value にしてくれる。
SPECIAL_CONVERSION_CHARACTER = {
    'test': '!TesT!'
}
# SPECIAL_CONVERSION_CHARACTERが不要ならここの処理も不要
custom_jinja.globals.update(SPECIAL_CONVERSION_CHARACTER)


@app.route("/")
def hello():

    # index.htmlを元にhtmlを生成します。
    page = custom_jinja.get_template('index.html')

    template = page.render(name='サンプルタイガー')

    return render_template_string(template, name='サンプルドラゴン', title='Titleだよ!!!')

if __name__ == "__main__":
    app.run(debug=True)

layout.html

<!doctype html>
<html>

<head>
    <meta http-equiv="content-type" charset="utf-8">
    <title>{{ title }}</title>
</head>

<body>
    {% block content %}
    {% endblock %}
</body>

<footer>
</footer>

</html>

index.html

{% extends "layout.html" %}
{% block content %}

<p>
    :':name:':=:name:<br>
    {{ '{{ name }}' }}={{ name }}<br>
</p>

:test:

{% endblock %}

表示はこんな感じ

:name:=サンプルタイガー
{{ name }}=サンプルドラゴン
!TesT!

f:id:Kuzunoha-NE:20181228230746p:plain

STR型を変換する

bunsho = ':one:である :two:なのだ'

以下のような辞書を参照してAである Bなのだという文字列にしたい場合

{'one': 'A','two': 'B'}

importやcustom.jinjaなどの記載省略
...

@app.route("/")
def hello():
...省略...

@app.route("/x")
def xstring():

    string = custom_jinja.from_string(':one:である :two:なのだ ')
    strings = string.render(
        {
            'one': 'A',
            'two': 'B'
        }
    )
    return render_template('strings.html', strings=strings)
...

strings.html ( {{ strings }}と書かれているだけのhtml)

{{ strings }}

出力するとこうなる

Aである Bなのだ

出来なかった個所

key名が数字のみ場合は参照されない。

    string = custom_jinja.from_string(':1:である :2:なのだ ')
    strings = string.render(
        {
            '1': 'A',
            '2': 'B'
        }
    )
    return render_template('strings.html', strings=strings)

-> 1である 2なのだ

【Python】数字をprint()する時、整数か文字列かがわからなくて困る件について repr()

お世話になっております。

葛の葉です。

標題の件につきまして、解決方法がわかりましたので、ご連携いたします。

なんかもう挨拶が面倒なので、次からはこんなスタンスにしたいです。

Pythonの環境について

Python 3.6.5

事の発端

プログラミングをしているとprint()を使って値がちゃんと出ているかどうか確認したいなぁって思ったりしますね。

例えば遷移する変数の値を見てみたい、なんて時なんかはprint()で見ていきたいですよね。

nana = 7
shichi = '7'

print('nana={}'.format(nana))
print('shichi={}'.format(shichi))

nana7shichi'7'が返ってくると思うじゃあないですか?結果は以下の感じなんですよ。

nana=7
shichi=7

shichi='7'になってほしかったですね。これだけ見るとshichiも整数と勘違いしてしまうかも。

repr()で解決

print()で変数を囲う前にrepr()で変数を囲ってあげればよいです。以下の感じ。

nana = 7
shichi = '7'

print('nana={}'.format(repr(nana)))
print('shichi={}'.format(repr(shichi)))

出力は以下の感じ

nana=7
shichi='7'

repr()についての公式ドキュメント

以下が公式ドキュメント

2. 組み込み関数 — Python 3.6.5 ドキュメント

コマンドライン上ではどうか?

Python 3.6.5 (default, Jun 27 2018, 08:15:56)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> nana = 7
>>> shichi = '7'
>>>
>>> nana
7
>>> shichi
'7'
>>> repr(nana)
'7'
>>> repr(shichi)
"'7'"
>>> 'nana={}'.format(repr(nana))
'nana=7'
>>> 'shichi={}'.format(repr(shichi))
"shichi='7'"

ちなみにドキュメントにも書いてある通り、この子たちはstrのようです。

>>> type(repr(nana))
<class 'str'>
>>> type(repr(shichi))
<class 'str'>

【Python】かんたんassertテストをしてみる

こんにちは、葛の葉です。

assertを使って簡単なテストをしてみましょう。

Python実行環境

Python 3.6.5

assertの使い方

assert 条件式, '間違ったときに出力される文字列'

assert条件式の間は半角スペースなんですよ。Pythonでは珍しくパレンティスの括弧を使わないで引数を指定しているような気がします。

パレンティスの括弧はこの子 -> ()

こんな感じ

def arry_to_str(arry):
    '''
    引数で受け取ったarryを配列っぽい形でstrにして返します。
    arry_to_str([i1,i2,i3,.....]) -> '["i1","i2","i3",.....]'
    要素は""で囲われます。
    '''
    moji = '['
    for i in arry:
        moji += '"{}"'.format(i)
        if i != arry[-1]:
            moji += ','
    moji += ']'
    return moji


arry = ['yum', 'npm', 'apt']
test_str = '["yum","npm","apt"]'
assert test_str == arry_to_str(arry), 'まちがってるぜ'

これで実行しても何も返らない。成功しているので特に返り値がないみたい。失敗しているとエラー文が返され、実行されたプログラムはそこで終了する。

逆に言えばassertが問題なく実行されれば、処理は続行されることになる。誤った処理などを弾くのに向いているかも知れない。

assertを失敗させる

17行目を以下のように変更する。

test_str = "['yum','npm','apt']"

各要素をダブルクォーテーション""で囲っている部分をシングルクォーテーション''に変更する。そうすると…

Traceback (most recent call last):
  File "d.py", line 19, in <module>
    assert test_str == arry_to_str(arry), 'まちがってるぜ'
AssertionError: まちがってるぜ

このように期待通り失敗する(?)