統計検定準1級合格

1/6(土)に統計検定準1級のCBT試験を受けて合格した。
参考になるかどうかわからないが、一応、経緯や感想を残す。

■筆者のバックグラウンド
  • 49.9歳
  • 子供の頃から数学好き
  • 大学時代は理系の学部
    統計学に興味があり、教養課程と大学院の修士課程で統計学を計3回履修したが、推定・検定がさっぱり理解できず
  • 大学卒業後はソフトウェア開発の仕事で全く統計学に触れず
  • 35歳の頃にふと思い立って入門書で統計学を復習
    推定・検定をやっと理解して本weblogに色々書いたが、結局初学者に少し毛が生えた程度
  • 46歳の頃に仕事がデータ分析に変わる
    ある時、会議で推定か検定を使った資料が出てきて、質問しようとしたが、すっかり忘れており、「信頼区間」「有意水準」という言葉すら出て来なかった
  • 47歳の頃に統計検定2級合格

■準1級受験の目的
個人的興味。
昔から統計学に興味があるものの、理屈がわかっていない所が多く、実用的なレベルまで理解できていないのでもっと勉強したいと思っており、勉強する為の丁度良い目標だと思ったから。
2級と違って会社とは関係無し。ただ、あわよくば何かの役に立てば良いなと思っている。

■使用した教材
・統計学実践ワークブック(日本統計学会公式認定テキスト)
・統計検定 1級・準1級 公式問題集[2018~2019年]
今回は余裕が無かったので、「統計学の時間 | 統計WEB」は使わなかった。

■準1級受験までの経緯
2021年8月に2級に合格した直後に教材を購入したものの、色々あって1年以上放置して、ほとんどを忘れてしまった。

2023年1月〜3月、「統計学実践ワークブック」で勉強開始
 GWに受験しようと12章まで拾い読みしたが、理解不十分で到底無理と判断
4月〜5月、「統計学実践ワークブック」を8章までしっかり読む
6月〜8月、将棋大会と会社の研修優先の為中断
9月〜12月、「統計学実践ワークブック」を一通り読み終える

勉強時間は、やっている時は週末毎に4〜6時間くらいのペースで、おそらく計150時間くらいだった。「統計学実践ワークブック」の内容がページ数の割に多く、ノートにメモしながら文字通り一言一句理解するつもりで読み進めると、この本だけでそれだけ時間がかかってしまった。
しっかり理解しながら進めたのだが、2週間前に勉強したことはノートを見返してもほとんど思い出せないような状態が続き、最後まで読み切ることを優先したので、これから30時間くらいかけて復習してやっと合格ラインかなという感覚である。

しかし、仕事関係で他に勉強することが多々あるのと、40代の内にせめて準1級までは取りたい(当初は40代の内に1級合格が目標だった)という思いがあり、タイムリミットが迫っていたので、現在の実力を知る為に一度受験することにした。

試験を申し込んだ後の年末年始に時間無制限で過去問(記述問題あり)をやってみると、自己採点の正解率は、2018年分が55%、2019年分が70%で、制限時間の2倍以上の時間がかかったので、やっぱり1回では厳しいかなと思った。

■試験当日
2日前から風邪気味で頭が少しボーッとしており、一夜漬けの復習ができなかった。当日もその調子で、最後の復習をした喫茶店でも試験会場までの道のりでも鼻水が止まらなかった。
全て選択問題と思っていたが、試験会場に着いて受験のしおりを読むと、一部記述式の問題ありと書いてあって、これはダメだなと思った。試験の直前には、帰りに問題集を買おうなどと、40代の残り1ヶ月の作戦を考え始めていた。
ついでに、なんと前日に鞄に入れたつもりだった電卓が無く(家にあった)、会場でも貸出していないとのことだったので、諦めがついた。もはや電卓を忘れたことを悔やむ気も起きなかった。

試験が始まると、大問が21問と出てきて、少し安心した。2級の時は同じ90分で大問が32問で、全然時間が足りなかった。21問なら1問4分以上ある。
しかし、第1問(小問2つ)の条件付き独立を含む確率の計算問題でいきなり、計算した結果が選択肢に無く、8分くらい使って結局解けず、パスするしかなくなった。(帰ってからゆっくり力技でやったら完全に解けたのだが、やはりその選択肢が無かったような...問題を誤読したのだろうか。)
大問の中には、筆者には時間を使って考えようがない、最も適切な記述を選ぶ問題あり(確認方法がわかっていれば数式を使って確認できたのだろうが)、計算方法がわからない問題多数あり、また、電卓が無かったので小数点以下第1位まで入力せよという問題2つは飛ばしたので、何か考えられそうな問題に絞ってそれぞれ8分くらい使っても、時間は余ってしまった。
結局、自信のある回答は2割くらい、やや自信ありも2割くらいで、残りは山勘で選ぶか空欄にしてしまった。

結果は71点で合格だった。(60点で合格)

「統計学実践ワークブック」の中で見た覚えが無い、知らない単語は1つか2つくらいしか出なかった。

受験のしおりには、一部記述式とか穴埋め式と書いてあったように思ったが、記述式の問題は数字しか使わなかった。記述式の問題の中に何故か、選択肢の番号を数字で記入させる、選択問題と変わらない問題が複数あった。

2級の時は長文が多く、画面一杯が文字で埋まるくらいの長文もあって、パソコンの画面で読むのに苦労したが、今回はそれほどの長文が無く、問題を画面で見ることに関して苦労したことは無かった。1回、数式中に何でそこに「'」(プライム記号)が付くのかがわからず、2x3ピクセルくらいだったので何か文字が潰れているのかと思って拡大して確認した時があったが、やっぱり「'」で、問題文をスクロールしていくと、「なお、『'』(プライム記号)はXXXを表す」のような補足があった。

■感想
結果は50点と表示されても違和感無かったので、細かい所は覚えてなくても、しっかり勉強して勘が鍛えられていたのかなと思う。

今回、つくづく思ったのは、年齢的にきつかった。2級を受験した2年前にも増して、記憶力と集中力と体力の低下を感じた。2週間前に1時間とか2時間とか頑張って理解したはずの内容を全然思い出せないことがあったのは筆者としてはショックで、それが何回もあったので、自分にはこういうのはもう無理かなと思った。それに加えて、平日の夜は仕事の疲れで全く勉強できず、土日も疲れで集中できず、机の前に座っても時間だけが過ぎていったりして、歳だなと思った。

それから、準1級の出題範囲=「統計学実践ワークブック」の内容の分量が思ったより多かった。この内容を全て覚えるのはかなり難しいと思う。少なくとも筆者の頭脳では、統計学を専門にしない限り、人生においてこの内容を全て覚えられる可能性があった時期は無かったんじゃないかとすら思う。

今回受けた試験の感想として、一番気になったのは、「統計学実践ワークブック」の例題や2018年〜2019年の過去問と同じ解き方で解ける問題が1問も無かったように感じたことだった(筆者が忘れてるだけかも知れないが)。出題範囲としては「統計学実践ワークブック」に紹介されている範囲内だったと思うが、出題傾向が全然違ったと感じた。筆記試験じゃなくCBT試験なので難易度を上げざるを得ないのかなと想像するが、これだけやれば大丈夫、でないのは残念だった。
元々「統計学実践ワークブック」は準1級の出題範囲を紹介するのが目的の書籍であり、これ1冊だけでは足りないという評判があったのは、その通りだと思った。満点を狙うのであれば、もっともっと多くの応用問題を解けるようにならないといけないのだろう。

一方、準1級のCBT試験は選択式の問題が大半で、ある程度の理解があれば山勘でもいくらか当たってしまうことと、合格ラインが60点と低めであることを考えれば、合格するだけなら難しくないと思った。
今回は半分以上の問題を山勘で回答してしまったが、その中の3つか4つくらいは、公式や基礎知識を復習しておけばきちんと解けた。特に、モーメント母関数を使って確率分布の再生性を確認するのはこの1年で20回以上やったはずだが、そのやり方さえ思い出せれば簡単に解ける問題を解けなかったのは痛恨だった。

matplotlibで判別境界を描画する

ちょっとPython+matplotlibで判別境界を描画したくなって、昔やってたやり方を思い出した。
他の方法は調べていない。
次のコードは、線形判別分析と2次判別分析とSVMの判別境界を並べて描画したものである。

●コード
import numpy as np

import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap

from sklearn.discriminant_analysis import LinearDiscriminantAnalysis, QuadraticDiscriminantAnalysis
from sklearn.svm import SVC

def draw_contour(clf, x1_range, x2_range, ax=None):
    cmap = ListedColormap(['k'])
    if ax is None: ax = plt.gca()

    margin1 = (x1_range[1] - x1_range[0]) * 0.05  # 5% of original range
    margin2 = (x2_range[1] - x2_range[0]) * 0.05
    xx1, xx2 = np.meshgrid(np.arange(x1_range[0] - margin1, x1_range[1] + margin1, 0.01),
                           np.arange(x2_range[0] - margin2, x2_range[1] + margin2, 0.01))

    Z = clf.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
    Z = Z.reshape(xx1.shape)
    ax.contour(xx1, xx2, Z, cmap=cmap)

def draw_meshgrid(clf, x1_range, x2_range, ax=None):
    cmap = ListedColormap(['lightgreen', 'lightpink'])
    if ax is None: ax = plt.gca()

    margin1 = (x1_range[1] - x1_range[0]) * 0.05
    margin2 = (x2_range[1] - x2_range[0]) * 0.05
    xx1, xx2 = np.meshgrid(np.arange(x1_range[0] - margin1, x1_range[1] + margin1, 0.01),
                           np.arange(x2_range[0] - margin2, x2_range[1] + margin2, 0.01))

    Z = clf.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
    Z = Z.reshape(xx1.shape)
    ax.contourf(xx1, xx2, Z, cmap=cmap)

def draw_sample(X, y, ax=None):
    if ax is None: ax = plt.gca()
    ax.scatter(X[y == 0, 0], X[y == 0, 1], color='b', marker='o')
    ax.scatter(X[y == 1, 0], X[y == 1, 1], color='r', marker='x')

# sample data
np.random.seed(0)
X = np.vstack([
    np.random.randn(10, 2) + [1.0, 1.0],  # 10 points around (1.0, 1.0)
    np.random.randn(10, 2) + [3.0, 2.5]   # 10 points around (3.0, 2.5)
])
y = np.array([0] * 10 + [1] * 10)  # class labels

fig, ax = plt.subplots(2, 3, figsize=(10,6))

# 線形判別分析
clf_lda = LinearDiscriminantAnalysis()
clf_lda.fit(X, y)
draw_contour(clf_lda, (X[:,0].min(), X[:,0].max()), (X[:,1].min(), X[:,1].max()), ax=ax[0,0])
draw_sample(X, y, ax=ax[0,0])
draw_meshgrid(clf_lda, (X[:,0].min(), X[:,0].max()), (X[:,1].min(), X[:,1].max()), ax=ax[1,0])
draw_sample(X, y, ax=ax[1,0])
ax[0, 0].set_title("LDA")

# 2次判別分析
clf_qda = QuadraticDiscriminantAnalysis()
clf_qda.fit(X, y)
draw_contour(clf_qda, (X[:,0].min(), X[:,0].max()), (X[:,1].min(), X[:,1].max()), ax=ax[0,1])
draw_sample(X, y, ax=ax[0,1])
draw_meshgrid(clf_qda, (X[:,0].min(), X[:,0].max()), (X[:,1].min(), X[:,1].max()), ax=ax[1,1])
draw_sample(X, y, ax=ax[1,1])
ax[0, 1].set_title("QDA")

# SVM
clf_svm = SVC()
clf_svm.fit(X, y)
draw_contour(clf_svm, (X[:,0].min(), X[:,0].max()), (X[:,1].min(), X[:,1].max()), ax=ax[0,2])
draw_sample(X, y, ax=ax[0,2])
draw_meshgrid(clf_svm, (X[:,0].min(), X[:,0].max()), (X[:,1].min(), X[:,1].max()), ax=ax[1,2])
draw_sample(X, y, ax=ax[1,2])
ax[0, 2].set_title("SVM")

plt.show()

●結果

優勝

日曜は開催していれば毎年参加している、地域の将棋大会に行った。
コロナ明けで再開して2回目であり、今年も参加者が少なかった。A級は16人しか居らず、8人が決勝トーナメント進出だった。

筆者はA級に参加以後、9回中8回は予選突破しているものの、決勝トーナメントでは8回中7回は初戦敗退で、残りの1回も1勝しただけである。大体、1回戦負けしに行っているようなものである。今年もそのつもりで、予選突破も際どかったのだが、何故か優勝してしまった。

■戦績
予選:○×○
決勝:○○○

決勝の1局目は予選の2局目で負けた人に当たった。2局とも居飛車急戦対四間飛車穴熊で、途中までほぼ同じ形だった。予選では先手の私の攻めが空振りして負けたが、決勝では先手の相手の攻めが空振りして労せず勝てた。棋力に差は無い感じで、先に1回負けて相手の穴熊の指し方がわかったので対策を立てやすかった。

予選3局目では無理攻めをしてしまって、予選落ちを覚悟した。

角換わり模様から棒銀で銀交換の後、横歩を取り、この△3三角に▲2二歩△同金▲3一銀△3二金▲2二歩と絡んだ。

この後△3一金▲2一歩成で技ありという読みだったのだが、そこで△4二金上と逃げられると空振りしてまずいことに気付いた(実際、水匠5は-700くらいで後手良しと言っている)。▲2五桂△2四角▲3一飛成としても、壁銀の悪形で銀2枚持たれて△5七角成とされると耐えられる気がしないし、▲1一ととすると△4五銀〜△3四銀打で飛車を殺されてしまう。
相手の方がここで長考されたので、これは負けだろうなと思い、決勝に進めなかった人の中から抽選で受けられるプロ棋士の指導対局を申し込もうと思い始めていた。
実戦はここで△4五銀と飛車を殺しに来られた。▲3五飛に△3一金▲2一歩成△3四銀打

で飛車を殺す手があったのだが、相手の方がこれに気付かず(私も気付かず)、飛車が生還して助かった。

決勝の2局目は先手早繰り銀の受け方がわからず、開始早々収拾がつかない状態になり、厳しい手を食らって必敗になった。ショックで記憶が欠けているのだが、多分こんな感じだった。(筆者が後手)

これで早速受け無し(▲3五歩が受からない)と思い(水匠5曰くここで△4三銀や▲3五歩に△4五歩の切り返しあり)、苦し紛れに△8五飛として、訳がわからなくなった。

△3一玉と入って一安心と思ったら、▲2四歩△同歩▲2三歩とされて終了。(水匠5の評価値は先手+1700)
しかしこの後、先手からなかなか決め手が来なかった。感想戦で聞くと▲2四飛△2一歩の形でずっと▲2二角があるのはわかってたそうだが、何故指さなかったのかはわからず終い。
この後、飛車交換だけを目指して指し続けたら、25手後くらいにたまたま成功して、それと同時に絶好の十字飛車があって、大逆転した。自力ではなく、勝ちが空から降ってきた気分だった。

決勝は昨年優勝、その前にも優勝ありの若い人が相手だった。せめて出だしだけでもそれらしくと思って、図書館で借りた将棋世界2022年5月号(あれ、最近借りたにしては新しくないな)に載っていたのを前日に覚えた、▲3八銀型の角換わり▲4五桂強襲を敢行した。

▲3五歩△同歩を入れ忘れて▲4五桂としてしまったので終わったと思ったが、後手の形が微妙に違うのでたまたま問題無かったようだ。この後△2二銀▲2四歩△同歩までは局後の感想戦で審判の長沼八段からも特にコメント無し。
相手の人には用意があったようで、疑問手の後の▲2四飛に、用意していたという△1三角から逆襲された。その数手後、観戦者からもどよめきが起こった意表の手順で大技を掛けに来られたが、実際その局面になったら皆そうするだろうという素朴な受けがあって実現せず、それで私が指しやすくなり、そのまま労せず勝ててしまった。

振り返ってみると、自力で勝ったのは予選の1局目だけで、後はただ受けていただけだった。会心の一手とか、心の中でガッツポーズできる時は無かった。