satopoooonのブログ

自分向けの備忘録

複数のグラフを一度に書く

seabornのグラフを一度に複数書く、

あるパラメータで層別して、複数のグラフを一度に描きたいです。 seabornのfacetgridを使います。 参考サイトは以下です。

Python でデータ可視化 - "Facet"で属性別グラフを一気に描く方法が便利すぎる - Qiita Python - seaborn.facetgridでの文字追加(121610)|teratail seaborn.FacetGrid — seaborn 0.8.1 documentation

以下コード

import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

bins = np.arange(0, 65, 5)
tips = sns.load_dataset("tips")

#層別のパラメータが2種類ある場合は、col、rowで指定します。
#1種類の場合は、colのみで良いと思います。
g = sns.FacetGrid(tips, col="time",  row="smoker")
g = g.map(sns.distplot, "total_bill", bins=bins, color="r")

#各gridに対する、axisを読んで、テキストを埋め込みます。平均値とかを記載したときに良いかと、
for i in range(2):
    for j in range(2):
        ax = g.facet_axis(i, j)
        ax.text(40,0.04,"hoge")

plt.show()

f:id:satopoooon:20180416173350p:plain

sns.displot,sns.kdeplot でエラー(slice indices must be integers or None or have an __index__ method)

よくわからないエラーがでました。

statsmodelsを再インストールしたら治りました。

以下、参考サイト

https://qiita.com/currypurin/items/44ce22102af4eda45622

 

deming regression

回帰直線を引くときに、横軸にも誤差がある場合はdeming regressionという方法で 回帰直線をひく必要があります。 deming regressionとタイトルにありますが、 別名で直行回帰(Orthogonal distance regression)ともいうらしいです。

scipyにOrthogonal distance regressionが実装されていたのでそいつを活用、

以下参考サイト、 ・scipyの公式サイト https://docs.scipy.org/doc/scipy/reference/odr.html ・deming regressionの説明 産総研の方による説明です。 https://staff.aist.go.jp/t.ihara/deming0.html ・deming regression

import numpy as np
from pandas import DataFrame

from sklearn.datasets import load_boston
from scipy.odr import Model, RealData, ODR
import matplotlib.pyplot as plt

from scipy.stats import linregress
#%matplotlib inline

#例によってボストンのデータを使います
boston = load_boston()

# 説明変数をDataFrameへ変換
df = DataFrame(boston.data, columns = boston.feature_names)
#目的変数をデータフレームに追加
df['MEDV'] = np.array(boston.target)

x = df["RM"].tolist()
y = df["MEDV"].tolist()

#deming regressionで使用する、関数を定義
def f(B, x):
    '''Linear function y = m*x + b'''
    # B is a vector of the parameters.
    # x is an array of the current x values.
    # x is in the same format as the x passed to Data or RealData.
    #
    # Return an array in the same format as y passed to Data or RealData.
    return B[0]*x + B[1]

linear = Model(f)

#scipyのlinregressで線形回帰、
##odrする際の、初期値を決めるためです、
linreg = linregress(x, y)
mod = Model(f)

#生データの代入、sx,syは,目的変数、説明変数のばらつきの標準偏差です。
#odrメソッドの引数に、目的変数、説明変数のばらつき、初期値を入力します。
dat = RealData(x, y,sx=1,sy=1)
od = ODR(dat, mod, beta0=linreg[0:2])
out = od.run()

fig = plt.figure()
subplot = fig.add_subplot(1,1,1)

subplot.scatter(x,y)
#linex = np.linspace(6.5,8,100)
#liney = out.beta[0]*linex + out.beta[1]
#subplot.plot(linex,liney)
subplot.plot(out.xplus,out.y,color='red')

#普通に線形回帰した場合の直線もひいてみます。
x_max = max(x)[f:id:satopoooon:20180326230958p:plain]
x_min = min(x)
linex = np.linspace(x_min,x_max,100)
liney = linreg[0]*linex + linreg[1]
subplot.plot(linex,liney,color='yellow')

plt.show()

実行すると、こんな感じになります。 f:id:satopoooon:20180326230958p:plain

ファイルを読み込む際に空行を飛ばす

ファイルを読み込む

空行があるファイルを読み込むときに、
ちょっとつまずいたのでメモります。
まずファイルの読み方ですが、以下を参考に、、、
Python: テキストファイルの読み込み – read()、readlines()、readline()メソッド

テキストファイルには、改行コードやら空白やら、タブ文字やらいろいろ混ざっているんですよね。
ファイルを読み込んで、リストに入れるときにその辺を削除したいです。

コードを書くと

f = open("読み込むファイルのパス","r")

for i in f.readlines():
        i=i.strip()#末尾の改行を除去、
        i=i.split("\t")#tab区切りでリストを作成、ちなみに改行コードは\n
   if i == [''"]:
            pass
        else:
            text.append(i)

末尾の改行コードの除去や、タブ区切りをしたせいで、
リストの中身が、「\n」だったり「[""]」で判断しようと思いましたが、
今回はi==[""]が空行としました。
というよりも空行を前のコードで空リストとしてしまったらですが、
iがリストなのか、リストの中身なのか、で微妙に空行の表現を考える必要がありそう。
splitするときも、["a,b,c,d"]のリストにはsplitはできないけど、"a,b,c,d"文字列にはできるので・・・
リストなのか、リストの中身の文字列なのか、は意識しなきゃいけなさそう。
すごい駄文になった・・・

pythonで波形処理プログラム-ガウスフィット-

ガウスフィット

pythonガウスフィットさせてその面積を求めるプログラムを作りたいです。
GUIで作りたい。

ひとまず、必要な機能は、
・matplotlibのグラフ上から、ポインタ情報を引っこ抜く
ガウスフィットさせる
あたりです。

以下のサイトを参考にしました。
python/matplotlibの図上にてクリックで座標値を取得したりキーボード入力値を取得したりする - Qiita
scipy optimizeをつかってみる (Python メモ) | OpenBook

やったことは、
1.ノイズ付きの正規分布をプロット、
2.グラフ上の2点を選択、
3.その間のデータを使ってガウスフィット、
4.フィッティング関数から面積を求める、

1.ノイズ付きの正規分布をプロット

import numpy as np
from scipy.stats import norm
# ベクトルxを [-5.0, ..., 5.0] の区間で作成
x = np.linspace(-1.0, 1.0, 100)

# 平均0, 標準偏差1の正規分布における、xの確率を求める
y = []
for i in range(len(x)):
    y.append(norm.pdf(x=x[i], loc=0, scale=1))

from scipy.optimize import curve_fit
#ノイズを作成
from numpy.random import *
y3 = normal(0.1,0.01,size = 100)

y = y + y3

f:id:satopoooon:20180129231340p:plain
こんな感じのグラフができます。
次は
2.グラフ上の2点を選択、、、
mpl_connectメソッドを使います。
第1引数が"クリック"によってイベント発生ということを指定して、
第2引数がクリックで実施することです。

#グラフ化
fig=plt.figure()
ax=fig.add_subplot(111)
ax.plot(x,y)
#グラフ上でクリックすることで、座標情報をひっぱります。
cid = fig.canvas.mpl_connect('button_press_event', onclick)
plt.show()

onclick関数は以下です。

def onclick(event):
    mask = np.ones(len(x), dtype=bool)
    global stat
    global leftind, rightind
    #stat = 1
    ind = np.searchsorted(x, event.xdata)
    plt.title("You clicked index=" + str(ind))
    print(stat,event.button)
    if event.button == 3 and stat == 1:
        leftind = ind
        ax.plot([x[ind]], [y[ind]], ".", color="red")
        stat = 2
    elif event.button == 3 and stat == 2:
        rightind = ind
        ax.plot(x[leftind:rightind], y[leftind:rightind], color="red")
        stat = 3

        gauss_return = gauss_fit(x[leftind:rightind],y[leftind:rightind])
        ax.plot(gauss_return[0],gauss_return[1], color="red")
        print(integ(x[leftind],x[rightind]))
    elif event.button == 1 and event.dblclick == 1 and stat == 3:
        plt.title("Approved")
        mask[leftind:rightind] = False
        stat = 1

    elif event.button == 2 and stat == 3:
        plt.title("Canceled")
        ax.plot(x[leftind:rightind], y[leftind:rightind], color="blue")
        ax.plot([x[leftind]], [y[leftind]], ".", color="green")
        stat = 1
    fig.canvas.draw()

ガウスフィットしたり、積分したりする関数は以下です。

def gauss_fit(x,y):
    global popt,pcov
    popt, pcov = curve_fit(gaussian,x,y)
    x_range = np.linspace(x[0], x[-1], 100)

    y_gauss = gaussian(x_range, np.ones(100) * popt[0], np.ones(100) * popt[1],
                  np.ones(100) * popt[2], np.ones(100) * popt[3])

    return x_range,y_gauss

def gaussian(x, a, mu, c, gamma):
    return a * np.exp(- gamma * (x - mu) ** 2) + c

def func(x):
    return popt[0] * np.exp(-popt[3] * (x - popt[1]) ** 2) + popt[2]

def integ(left,right):
    return integrate.quad(func,left,right)

ちなみにこんな感じでフィッティングされます。
f:id:satopoooon:20180129232617p:plain

jsonファイルと辞書型

JSONファイル

JSONファイルって何?
調べればすぐに出てきますけど(以下等参考)、
プログラムと人の両方が理解できる形式ってイメージです。
【Python入門】JSON形式データの扱い方 - Qiita
同じようなものにXMLとかあるけど、それよりも見やすい気がします。

visual studio codeで独自のスニペッツを作成しましたが、
そのルールを記載するファイルがjson形式でした。

データベースのテーブルとカラムが記載している表から、
json形式でスニペットを作成するスクリプトです。
辞書型を使うとすごい簡単にできました。

PATH = "データベースのテーブル、カラムが記載しているエクセルファイルのパス.xlsx"
dframe = pd.read_excel(PATH,sheetname="Sheet2")

list2 = {}
text = "" 
dict={}
for column in dframe.columns:
    list2[column] = dframe[column].dropna()
    for n in list2[column]:
        text =  n + "," + text 
    dict[column] = text

import json
file =open("ファイルのパス.json","w")
json_dict2 = {}

for key in dict.keys():
    json_dict= {}
    json_dict["prefix"]=key
    json_dict["body"]= key + ".${1|" +dict[key] + "none|}"
    json_dict2[key] = json_dict 

json.dump(json_dict2,file)

pythonで動的に変数を作成したい(for文でloopまわして作成)

動的に変数を作成

pythonでつまずいたことがあったのでメモ

for文でloopさせて、動的に変数作成をしたかったんですよ。
その際に、引数もfor文で作成的なやつです。

間違った例↓

for n in dataframe.colums
 "data_{}".format(n) = dateframe[n]

これはダメでしたね。
.formatは文字列に変数を代入するという方式みたいですね。

やるとしたら、
[[dateframe[1],[dateframe[2],・・・・,datafarame[n]]
みたいに、リスト内リストか辞書型が良いみたいです。

list = [] #リスト
for column in dframe.columns:
     list.append(dframe[column].dropna())

list2 = {} #辞書型
for column in dframe.columns:
    list2[column] = dframe[column].dropna()

リスト内リストだと引数が数字になるけど、
辞書型だと、引数に文字列が使えるので便利かな、

ちなみに下記を参考にすると、exec関数で解決できるっぽいですが、
変数となるタイミングの問題で推奨されていなよう。
Python - pythonでname0,name1,name2・・・のように変数を宣言したい(9130)|teratail
モジュール外から呼び出すと、想定外の動きしないとか、そういうバグ
具体例としては以下2つの違い

for i in range(3):
    code = 'name{} = {}'.format(i, 3 ** i)  # 例として3のi乗を代入
    exec(code)

print name0, name1, name2  # ==> 1 3 9
name1 = 123

def foo(num):
    exec('name{} = 456'.format(num))
    print name1  # 123

foo(1)