jupyter上でユニットテスト
何らかの実現したいことがあって、それをするためにコードを書くときに、必ずテストケースを書いて、本当に正しい振る舞いかどうか確かめる必要性が出てくる。これをjupyter上で行う際のチップスをまとめた。
実現したいことの例
今回は、冗長なkeysとそのkeysに対応するパラメータの位置の情報が与えられており、同じkeysに対応するパラメータの値は、同じにするという問題を考える。
例えば、以下のような場合を考える。
keys = [(1,2), (1,3), (1,1,2), (1,1,2), (1,2,1), (1,2,1), (1,2), (1,3)]
paraminfo = [((1,2), 1), ((1,3), 2), ((1,1,2), 3), ((1,1,2),4), ((1,2,1), 5), ((1,2,1), 6), ((1,2), 7), ((1,3), 8)] # (keys, パラメータの位置)が配列の要素
thetas = [-1.0, -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, -8.0]
期待する結果は、[-1.0, -2.0, -3.0, -3.0, -5.0, -5.0, -1.0, -2.0] である。
コードを書く。
とりあえずコードを書いてみる。今回は辞書を使って、実装する。
keys = ["a", "b", "c", "c", "d", "d", "a", "b"]
paraminfo = [(("a"), 1), (("b"), 2), (("c"), 3), (("c"),4), (("d"), 5), (("d"), 6), (("a"), 7), (("b"), 8)]
thetas = [-1.0, -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, -8.0]
d = Dict()
θunique = []
for (i,k) in enumerate(keys)
if haskey(d, k) # ここで冗長なkeysは落としている。
continue
end
@show (i,k)
d[k] = i
push!(θunique, thetas[i])
end
d
冗長性を省いた辞書を定義した。こちらの情報をもとに
θ = zeros(Float64, length(thetas))
for (ik, k) in enumerate(keys)
#println(ik, k)
i = d[k] #dは上で定義した辞書
θ[ik] = thetas[i]
end
θ
テスト+関数化する
最初に関数を書いて、それによる出力結果と期待される結果が一致するというテストコードを最初に書く。
function update_param(keys, paraminfo, thetas)
*****
end
output = [-1.0, -2.0, -3.0, -3.0, -5.0, -5.0, -1.0, -2.0]
@assert update_param(keys, paraminfo, thetas) == output
次に上で書いたコードを関数化する。こうすることで、関数の振る舞いが正しいか正しくないかをセルを実行する度にチェックできる。
function update_param(keys, paraminfo, thetas)
d = Dict()
θunique = []
for (i,k) in enumerate(keys)
if haskey(d, k) # floatが入るとややこしい。
continue
end
#@show (i,k)
d[k] = i
push!(θunique, thetas[i])
end
θ = zeros(Float64, length(thetas))
for (ik, k) in enumerate(keys)
#println(ik, k)
i = d[k]
θ[ik] = thetas[i] # d は辞書ではなく、配列にする。
end
return θ
end
# このセルだけを実行すると、関数だけでなくテストも実行してくれるので楽。
output = [-1.0, -2.0, -3.0, -3.0, -5.0, -5.0, -1.0, -2.0]
@assert update_param(keys, paraminfo, thetas) == output
リファクタリングによっていい感じにする。
さらに関数化して、良い感じにする。こちらも同様に、期待される結果と関数の結果が一致するというテストコードを書いておく。
# make_compact_param_set
function make_compact_paraminfo(keys)
d = Dict()
#θunique = []
for (i,k) in enumerate(keys)
if haskey(d, k) # floatが入るとややこしい。
continue
end
#@show (i,k)
d[k] = i
end
return d
#push!(θunique, thetas[i])
end
@assert make_compact_paraminfo(keys) == Dict{Any, Any}(
(1, 2, 1) => 5,
(1, 2) => 1,
(1, 3) => 2,
(1, 1, 2) => 3
)
## パラメータの情報も一つのセルの中に含ませておくと、自己完結的で良いかも。
keys = [(1,2), (1,3), (1,1,2), (1,1,2), (1,2,1), (1,2,1), (1,2), (1,3)]
paraminfo = [((1,2), 1), ((1,3), 2), ((1,1,2), 3), ((1,1,2),4), ((1,2,1), 5), ((1,2,1), 6), ((1,2), 7), ((1,3), 8)]
thetas = [-1.0, -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, -8.0]
function update_param(keys, paraminfo, thetas)
d = make_compact_paraminfo(keys)
θ = zeros(Float64, length(thetas))
for (ik, k) in enumerate(keys)
#println(ik, k)
i = d[k]
θ[ik] = thetas[i] # d は辞書ではなく、配列にする。
end
return θ
end
# このセルだけを実行すると、関数だけでなくテストも実行してくれるので楽。
output = [-1.0, -2.0, -3.0, -3.0, -5.0, -5.0, -1.0, -2.0]
@assert update_param(keys, paraminfo, thetas) == output
まとめ
jupyterで書いたコードが期待される振る舞いかどうかテストするときの流れをまとめてみた。