【Python】Classの定義(イニシャライザ、継承も含めて)

クラスを試してみる。

クラス、インスタンス、インヘリタンスの定義


・クラス(class):
設計図と言われる。このクラスから実体(インスタンス)を生成する。
クラス自体に実体はない(データを持たない)。

インスタンス(instance):
日本語で実体。
クラスにデータを与えて生成されるもの。
実体を生成することをインスタンス化と言う。

・インヘリタンス(inheritance):
日本語で継承。
親から子へクラスの内容を引き継がせる機能。

Pythonでの実装(クラス定義、インスタンス化)


Pythonでのクラス定義は class クラス名 : と書き、インデントを下げて関数を定義していく。
クラス内の関数はメゾッドと呼ばれ、第一引数で自分自身を定義する必要がある。第一引数の名称は self とする慣習がある。

また、次のコード例で示す def __init__(self, val1...) は特別な関数でイニシャライザと呼ばれる(初期値を与える処理)。

#-----------------------------
#-- クラス定義
#-----------------------------
class MyClass1: 
    def __init__(self, val1, val2):  #-- 初期値を与える
        self.val1 = val1
        self.val2 = val2
    
    def 足し算(self):  #-- メゾッド
        x = self.val1 + self.val2
        print ("足し算:{0}+{1}={2}".format(self.val1,self.val2,x))
 
    def 引き算(self):  #-- メゾッド
        x = self.val1 - self.val2
        print ("引き算:{0}-{1}={2}".format(self.val1,self.val2,x))
 
    def 掛け算(self):  #-- メゾッド
        x = self.val1 * self.val2
        print ("掛け算:{0}×{1}={2}".format(self.val1,self.val2,x))


次に、インスタンス名 = クラス名 (引数) とすることで、定義したクラスに値を与えてインスタンス化する。
そして、インスタンス名.メゾッド名() とすることで、クラス内に定義したメゾッドを実行する。

#-----------------------------
# インスタンス化
#-----------------------------
res1 = MyClass1(1, 2)  #-- インスタンス化(MyClass1に値を与えて実体化する)
  
#-----------------------------
# クラス定義内のメゾッドを実行
#-----------------------------
res1.足し算()
res1.引き算()
res1.掛け算()
 
--- out ---
足し算:1+2=3
引き算:1-2=-1
掛け算:1×2=2


上のコード例の通り、クラス定義により様々な機能を1つの括りでまとめることができる。
大規模な開発になるほど可読性やメンテナンス性などが向上する可能性がある。


Pythonでの実装(継承)


クラスでは親クラス→子クラスに値を引き継がせる継承機能を扱うことができる。

子クラスは class クラス名 (親クラス名) : とする。
この時、子クラス内で super().親クラスのメゾッド(引数名) とすれば、親クラスのメゾッドを呼び出すことができ、同一処理をわざわざ再定義する必要がない。

せっかくなので、これまでの自身の記事を参考にして、
スチューデントのT検定Wilcoxonの順位和検定 を実行できる簡単なパッケージを作ってみる。(※検定名のところに記事のリンク貼っている)

class MyStat1:
    def __init__(self, group1, group2):
        self.group1 = group1
        self.group2 = group2
        self.n1 = len(group1)  #-- グループ1の例数
        self.n2 = len(group2)  #-- グループ2の例数
        self.N  = self.n1 + self.n2  #-- 全体の例数
 
class StudentTtest(MyStat1):
    def __init__(self, group1, group2):
        super().__init__(group1, group2)  #-- 親クラスのイニシャライザを継承
        print("継承しました")  #-- 継承したか見たいだけのプリント
        
    def Diffmean(self):
        import numpy as np
        m1 = np.mean(group1)
        m2 = np.mean(group2)
        return m1 - m2
        
    def TestResult(self):
        import numpy as np
        from scipy import stats
        s1 = np.sum((group1 - np.mean(group1))**2)
        s2 = np.sum((group2 - np.mean(group2))**2)
        Df = self.N - 2
        se = np.sqrt((s1 + s2) / Df)
        T  = self.Diffmean() / (se * np.sqrt(1/self.n1 + 1/self.n2))
        P  = stats.t.cdf(x=T, df=Df)*2
        print(" ----- Student's T-test -----\n"
              ,"T統計量:{0}\n 自由度:{1}\n p値:{2}\n".format(T, Df, P)
              ,"----------------------------")
        return T, Df, P
    
class WilcoxonRankSumTest(MyStat1):
    def __init__(self, group1, group2, ties=True):
        super().__init__(group1, group2)  #-- 親クラスのイニシャライザを継承
        self.ties = ties  #-- Wilcoxonの順位和検定はさらにタイ補正有無の引数を与えてみる
        print("継承しました")  #-- 継承したか見たいだけのプリント
        
    def TestResult(self):
        import numpy as np
        from scipy import stats
        import pandas as pd
        
        #-- データフレーム化
        ads = pd.DataFrame({"AVAL":group1 + group2
                            ,"TRT01AN":["group1"] * self.n1 + ["group2"] * self.n2
                            })
        #-- 全体順位
        ads["Rank"] = ads["AVAL"].rank()
        
        #-- タイ補正(同一順位の数で補正)
        adfrq = ads["AVAL"].value_counts()
        wil_t = np.sum(adfrq**3 - adfrq)
        
        if self.ties == False:
            wil_t = 0  #-- タイ補正しない場合、0に上書き。
        
        #-- 順位和        
        sumrank1 = float(np.sum(ads.loc[ads["TRT01AN"]=="group1", ["Rank"]]))
        sumrank2 = float(np.sum(ads.loc[ads["TRT01AN"]=="group2", ["Rank"]]))
        
        #-- 実測、期待値、分散        
        if sumrank1 < sumrank2:
            wil_W = sumrank1
            wil_E = self.n1 * (self.n1 + self.n2 + 1) / 2
        else:
            wil_W = sumrank2
            wil_E = self.n2 * (self.n1 + self.n2 + 1) / 2
            
        val1 = self.n1 * self.n2 * (self.n1 + self.n2 + 1) / 12
        val2 = self.n1 * self.n2 / (12 * (self.n1 + self.n2) * (self.n1 + self.n2 - 1))
        wil_V = val1 - val2 * wil_t
        wil_Z = (wil_W - wil_E) / np.sqrt(wil_V)
        
        wil_P = stats.norm.cdf(wil_Z)*2
        
        print("----- Wilcoxon Rank Sum Test -----\n"
              ,"順位和:{0} (group1), {1} (group2)\n".format(sumrank1, sumrank2)
              ,"W:{0}\n".format(wil_W)
              ,"E[W]:{0}\n".format(wil_E)
              ,"Var[W]:{0}\n".format(wil_V)
              ,"タイ補正の係数:{0}\n".format(wil_t)
              ,"\n"
              ,"Z統計量:{0}\n".format(wil_Z)
              ,"p値:{0}\n".format(wil_P)
              ,"----------------------------------")
        return wil_Z, wil_P


出来上がったクラスを試してみる。
サンプルデータは Wilcoxonの順位和検定 の記事で扱ったものと同じ。

group1 = [4, 9, 10, 11, 12, 13, 13, 15, 18, 18, 20]
group2 = [16, 16, 17, 19, 19, 22, 22, 23, 25]


T検定(スチューデント)

res1 = StudentTtest(group1, group2)
  
res1.Diffmean()
Out[1]: -6.888888888888889
 
res1.TestResult()
継承しました
 ----- Student's T-test -----
 T統計量:-3.761258193347809
 自由度:18
 p値:0.0014296297831112022
 ----------------------------
Out[1]: (-3.761258193347809, 18, 0.0014296297831112022)


親クラスのN数を self.n1, self.n2, self.N として子クラスで使っているが、問題なく使用できている。
また、StudentTtest クラス内の TestResult メゾッドで、同一クラス内の Diffmean メゾッドを self.Diffmean() で呼び出しているが、こちらも問題はなく動作している。
念のため検算。

from scipy import stats
stats.ttest_ind(group1, group2, equal_var = True)
Out[1]: Ttest_indResult(statistic=-3.7612581933478086, pvalue=0.001429629783111206)


Wilcoxonの順位和検定(タイ補正あり)

res2 = WilcoxonRankSumTest(group1, group2, ties=True)
 
res2.TestResult()
継承しました
----- Wilcoxon Rank Sum Test -----
 順位和:77.0 (group1), 133.0 (group2)
 W:77.0
 E[W]:115.5
 Var[W]:172.59868421052633
 タイ補正の係数:30
 
 Z統計量:-2.930501777999723
 p値:0.00338415057040675
 ----------------------------------
Out[1]: (-2.930501777999723, 0.00338415057040675)


Wilcoxonの順位和検定(タイ補正なし)

res3 = WilcoxonRankSumTest(group1, group2, ties=False)
 
res3.TestResult()
継承しました
----- Wilcoxon Rank Sum Test -----
 順位和:77.0 (group1), 133.0 (group2)
 W:77.0
 E[W]:115.5
 Var[W]:173.25
 タイ補正の係数:0
 
 Z統計量:-2.9249881291307074
 p値:0.0034446936330652616
 ----------------------------------
Out[1]: (-2.9249881291307074, 0.0034446936330652616)


念のため検算。余談だが、stats.ranksumsはタイ補正なしのようだった。

from scipy import stats
stats.ranksums(group1, group2)
Out[2]: RanksumsResult(statistic=-2.9249881291307074, pvalue=0.0034446936330652616)


本記事はここまで。

本ブログは個人メモです。 本ブログの内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。