delhi09の勉強日記

技術トピック専用のブログです。自分用のメモ書きの投稿が多いです。あくまで「勉強日記」なので記事の内容は鵜呑みにしないでください。

Pythonでデシジョンテーブルを作成してみた

概要

ソフトウェアテストで使われるデシジョンテーブルという技法がある。

gihyo.jp

前からプログラムで作成できないのかな?と思っていたのだが、やってみたらPythonで意外と少ないコードで実現できた。

以下で実際にデシジョンテーブルを作成しながら説明していく。

【この1冊でよくわかる】ソフトウェアテストの教科書―品質を決定づけるテスト工程の基本と実践』という本のp86の「レンタルDVDの料金割引のデシジョンテーブル」を例とさせて頂く。

使ったライブラリ

  • 標準ライブラリ
    • itertools: 全パターンの組み合わせを出力するのに使う。
  • サードパーティライブラリ
    • pandas: 必須ではないと思うが使うと表の作成とExcel出力が簡単にできる。
    • xlsxwriter: pandasがExcel出力するときに内部で依存している。

やり方

「条件」と「アクション」を定義する。

以下のように「条件」と「アクション」をリストに定義する。

conditions = ["旧作", "年齢65歳以上","年齢18歳以下"]
actions = ["半額", "20%オフ", "10%オフ", "通常料金"]

※ 「条件」、「アクション」はデシジョンテーブルの用語だが、ここでは説明しない。

各条件の「Y/N」の全通りの組み合わせを求める。

以下の3つの条件について、Yes(=Y)の場合とNo(=N)の場合があるので、その全通りの組み合わせを求める。

  1. 旧作
  2. 年齢65歳以上
  3. 年齢18歳以下

※ 2.と3.は両方Yesがありえない条件だが、ここでは考慮しない。

ここで「直積」という考え方を使うとやりたいことができることを知った。

ja.wikipedia.org

すなわち、Y/Nの2つの要素を持つ集合が3つあると考えると、直積を求めればY/Nの全通りの組み合わせを求めることができる。

Pythonではitertools.productという関数を使えば以下のように簡単に書ける。

from itertools import product

combinations = list(product(["Y", "N"], repeat=len(conditions)))

この時点で、combinationsの中身は以下の通り、Y/Nの全通りの組み合わせのリストになっている。

[('Y', 'Y', 'Y'), ('Y', 'Y', 'N'), ('Y', 'N', 'Y'), ('Y', 'N', 'N'), ('N', 'Y', 'Y'), ('N', 'Y', 'N'), ('N', 'N', 'Y'), ('N', 'N', 'N')]

※ 「直積」を知ったきっかけは以下の記事だった。
qiita.com

DataFrameでデシジョンテーブルを作成する。

pandasのDataFrameを使って、プログラム上でデシジョンテーブルを作成する。
※ 普段あまりpandasを使わないので、pandasの使い方はぎこちないかもしれない。

まずはDataFrameを作成してY/Nと条件名を紐付ける。

df = pd.DataFrame(combinations, columns=conditions)

この時点でdfの中身は以下

  旧作 年齢65歳以上 年齢18歳以下
0  Y       Y       Y
1  Y       Y       N
2  Y       N       Y
3  Y       N       N
4  N       Y       Y
5  N       Y       N
6  N       N       Y
7  N       N       N

次にアクション名のカラムを追加する。(値は空文字)

df = df.reindex(columns=conditions + actions, fill_value="")

この時点でdfの中身は以下

  旧作 年齢65歳以上 年齢18歳以下 半額 20%オフ 10%オフ 通常料金
0  Y       Y       Y                    
1  Y       Y       N                    
2  Y       N       Y                    
3  Y       N       N                    
4  N       Y       Y                    
5  N       Y       N                    
6  N       N       Y                    
7  N       N       N                    

最後に、縦と横が逆なので転置する。

df = df.T

これで以下のようにプログラム上でデシジョンテーブルの雛形を作成できた。

         0  1  2  3  4  5  6  7
旧作       Y  Y  Y  Y  N  N  N  N
年齢65歳以上  Y  Y  N  N  Y  Y  N  N
年齢18歳以下  Y  N  Y  N  Y  N  Y  N
半額                             
20%オフ                          
10%オフ                          
通常料金                           

Excelに出力する

「プログラムでのデシジョンテーブルの作り方」という意味ではここまでで終わりなのでおまけだが、最後にExcelに出力する。

ただPandasでExcelに出力するだけなら簡単だが、プログラムの中で列幅も調整すると以下のようにちょっとごちゃごちゃしたコードになった。

writer = pd.ExcelWriter("decision_table.xlsx")
df.to_excel(
    writer,
    sheet_name="cases",
    header=[str(i + 1) for i in df],  # Excel出力時はカラムがNo.1から始まるようにする。
)
# 条件名とアクション名を記載する列のwidthを指定する。
writer.sheets["cases"].set_column(
    0,
    0,
    df.index.map(len).max() * 2  # マルチバイト文字の場合は文字数の2倍のwidthにしたら丁度よかった。
)
for column_idx in df:
    # 各列のwidthを指定する。「Y」か「N」しか入らないことが分かっているので固定長でもいいが、
    # 一応可変長に対応した書き方にした。
    column_width = max(df[column_idx].astype(str).map(len).max(), len(str(column_idx)))
    writer.sheets["cases"].set_column(column_idx + 1, column_idx + 1, column_width)
writer.save()

この辺は以下の記事を参考にさせて頂いた。

towardsdatascience.com

stackoverflow.com

成果物

以下のようなExcelの成果物ができた。

f:id:kamatimaru:20210707005012p:plain

あとは条件に対応する正しいアクションを手で埋めていけばよい。

f:id:kamatimaru:20210707005301p:plain

以上