伝染病のモデル化
この課題の目的は,Pythonの基本を練習し,いくつかの簡単なアルゴリズムをコードに変換 する方法について考えてもらうことです.
疫病や伝染病は,生物学的,社会的な要因が絡む,非常に複雑な現象である.コンピュー タモデルは,不完全ではあるが,病気の広がりに関する洞察を与えることができ,様々な 程度の複雑さで感染を表現することができる.
SIRは,単純ではあるが,よく使われる伝染病のモデルである.SIRモデルでは,人は3つ の状態のうちの1つの状態にあると捉える.病気にかかりやすい状態 (Susceptible),病気に感染した状態(Ill),感染後に病気から回復した状態 (Recovered) である(よって,S-I-R).このモデルでは,流行が起こりうる地域社会のような人々のネットワークに焦点を当てる.単純ではあるが,SIRモデルは,社会的要因(ネットワークの形状,例えば,ネットワーク内の人々が互いに交流する頻度など)と生物学的要因(感染期間など)の両方が病気の広がりを媒介することを捉えている.
この課題では,SIR流行モデルの簡略版をシミュレートするコードを書いていただきます.あなたのコードは,感染が住民から隣人へ都市を通じてどのように広がるかをモデル化する.高度なレベルでは,あなたのコードは,シミュレーションの終了まで各人の状態を追跡しながら,一日ごとに都市の病気の状態を繰り返し計算することになります.また,複雑なモデ リングプロセスを簡素化するために,互いに積み重なる関数 (functions)の使用方法を学びます.
はじめに
課題を始める前に,課題に使用するファイルを 課題の作業を始める前に,Coursework Basicsのページで説明されている手順に従って ,この課題のファイルを入手してください(これらの手順は,Short Assignment #1の ファイルを入手するために行った手順と同じです). 課題ファイルを取得するまで,課題に取りかかることができないことに注意してください (リポジトリ内のpa1ディレクトリに表示されます). そのページで説明されているように,あなたのコードで実験するためにIPythonセッション を開始することを確認する必要があります.ここでも,手順は短い演習の場合と同様です. 新しいターミナルウィンドウを開き,pa1ディレクトリに移動してください.そして,Linux のコマンドラインからipython3を起動し,オートリロードを設定し,次のようにコードをイ ンポートしてください. $ ipython3 1]: %load_ext 自動再読み込み DeepL Proに登録すると,より大きなサイズの文書ファイルを翻訳できます. 詳しくは,www.DeepL.com/pro をご覧ください. 2]において: %autoreload 2 3] では,インポートサー 最後に,この課題では,関数に渡される入力は正しい形式であると仮定してよい.関数に 渡された入力は,一切変更してはいけません.一般的に,関数の入力として渡されたデー タ構造を変更することは,その関数の明確な目的でない限り,悪いスタイルです.もし誰 かがあなたの関数を呼び出したら,その人はそのデータを他の用途で使うかもしれません し,予期せぬ変更に驚かないようにしなければなりません. モデル SIRモデルの構築を始めるには,モデルの詳細を指定する必要があります. 病態:シミュレーションの中で,各人の健康状態を表す方法. 都市の構造.都市の表現方法,都市内の各個人の隣人. 都市内における疾病伝播の伝達ルール. 伝染病ルール:病気に対する免疫を回復・獲得するためのルールであり 停止条件.シミュレーションを停止するタイミング.以下,それぞ れの詳細を明記する. 病気の状態:シミュレーション内のすべての人は,Susceptible,Infected, Recoveredの3つの状態のうちの1つで存在することができます. 感受性:健康であるが,将来的に感染する可能性がある人.ここでは,感受性の高い人を 表すために「S」を使用します. 感染している:現在,感染症にかかっている個体.これらの個体を「I0」「I1」「I2」な どで表すことにする.Iの後の数字は,その個体が感染している日数を表す(後述の「伝染 ルール」参照). 回復した:この個体は感染から回復し,シミュレーションの残りの期間,感染に対して免 疫がある.このような個人を'R'で表現する.(SIRモデルのいくつかのバージョンでは, 回復した人々をモデルから除外している.我々のモデルでは,回復者は都市に留まる). なお,課題6と課題7では,追加の状態を導入します.ワクチン接種('V'で表される). この状態や予防接種については,最後のタスクまで気にする必要はありません. 都市の構造:本シミュレーションにおける都市は,人々のリストとして表現され,それぞれ が病気の状態によって表現される.例えば S', 'I1', 'R']の都市は3人で構成され,1人目は感受性,2人目は感染(具体的には感 染後1日),3人目は回復しています. どの都市にも少なくとも一人はいると考えてよい. この単純化されたモデルでは,人は最大2人の隣人を持ちます.リストですぐ前の人(左隣人 と呼ばれる)と,リストですぐ後の人(右隣人と呼ばれる)です.リストの最初の人は左隣 人を持たず,リストの最後の人は右隣人を持ちません.例えば,次のような人々のリストを 考えてみましょう: ['Mark', 'Sarah', 'Lorraine', 'Marshall']: マークには隣人が一人いる.サラです. サラには二人の隣人がいる.マークとロレインです.ロレインには 2人の隣人がいますサラとマーシャル マーシャルには1人の隣人が いる.ロレイン 感染ルール:感染は常に感染者('I0','I1'など)から感受性の高い人('S')に広がる .つまり,少なくとも1人の感染した隣人がいる感受性の高い人は,翌日には必ず感染する . 伝染のルール人が感染して伝染力を維持する日数は,シミュレーションのパラメータである .我々は,人が感染している日数を,その人の状態の一部として追跡する. 感染した人は,まず「I0」の状態からスタートする.I0」は「I1」になり,「I1」は「I2 」になる,というように,感染している日数に応じてカウンターを1つずつ増やしていく.カ ウンタが指定された感染日数に達すると,回復した('R'),もう感染していない,と宣言す る.その時点で,彼らは病気に対する免疫ができ,再感染することはありません.例えば,3 日間感染し続ける感染症のシミュレーションを行う場合,新たに感染した人は状態 'I0' で 始まり,1日後に 'I1' に,2日後に 'I2' に,そして3日後に状態 'R' に移行し,シミ ュレーションの残りの期間,その状態を維持することになります. 停止条件:市内に感染者がいなくなった時点でシミュレーションを停止すること. あなたのタスク この課題では,あなたが実装しなければならない一連の関数を指定します.最初の短い練習 問題のように,関数を理解することはこの課題を完了するために必須ではありません.そし て,あなたがコードを追加する必要があるファイルの場所を正確に指定しています. 基本的な機能から始めて,より複雑なタスクに取り組んでいただきます.また,豊富なテ ストコードも提供します.この期間中,私たちが提供する情報は少なくなっていきます. を,コードに適した構造にします. 課題1.ある都市の感染者数を数える Pythonでは,キー定義をカプセル化し,数行のヘルパー関数を書くのが一般的です.最初の 仕事は,そのような関数の1つであるcount_infectedを完成させることです. 以下は,sir.py ファイルにあるコードです: def count_infected(city): ''' 感染者数のカウント 入力です. city (文字列のリスト): シミュレーションに登場するすべての人の,その日の始 まりの状態 戻り値(int):現在感染している人の数 '''
your code here
-1 を適切な整数に置き換える
戻り値-1 関数のdocstring(三重引用符で囲まれた部分)は,関数への入力を指定します.これについ ては,授業で関数を取り上げる際に詳しく説明しますが,今回の課題では,cityには docstringで指定された値,より具体的には,シミュレーションに参加しているすべての人の 一日の始まりの状態を表す文字列のリストが入ると仮定すれば十分でしょう. 次に,このcity変数を受け取って,感染した隣人の数を数えるコードを書かなければなりま せん.その際,return -1 の -1 を適切な値に置き換える必要があります.たとえば, num_infected という変数を使って感染した隣人の数をカウントするコードなら,return -1 を return num_infected に置き換える. 例えば,都市 ['I0', 'I0', 'I2', 'S', 'R'] が与えられた場合,この関数は3を返す (複数の感染状態があることを考慮する必要があることに注意).都市が ['S', 'S', 'S', 'S'] のような場合,この関数は0を返すだろう. テストタスク1 短い練習問題と同様に,この課題でも自動化されたテスト群を用意しました.これらのテス トの実行方法と,自動テストに移る前の手動テストの重要性を理解するために,「コードの テスト」のページを少し読んでみてください. 特に,IPythonから手動でテストすることから始めることをお勧めします.例えば,ここに count_infectedの呼び出しのサンプルがあります. In [6]: sir.count_infected(['I0', 'I0', 'I2', 'S', 'R']) アウト[6].3 In [7]: sir.count_infected(['S', 'S', 'S', 'S']) Out[7]:0 ModuleNotFound エラーが発生した場合,IPython で import sir を実行し,sir.py に含まれるコードを実行できるようにしたことを確認してください. 自動テストを実行する準備ができたら,15のテストケースを用意しました.テストされた都 市は,1人から20人までと規模が異なり,病気の状態の組み合わせも様々です(例:すべて 感受性,すべて回復,感染日数が異なる感染者がいる,など). count_infected Cityに関するテ スト 期待される結果 説明 ['I0']. 1 感染者がいる一人街.['I2000'] 1 感染日数の多い感染者がいる一人街. ['R'] 0 回復者のいる一人街 ['S'] 0 感受性の強い人と一人の都市 ['S', 'S', 'S', 'S']. 0 すべての影響を受けやすい小都市 ['R', 'R', 'R', 'R']. 0 全回復した小さな街 20人都市 0 影響を受けやすい人と回復した人が混在する大きな都市 ['I1', 'S', 'S', 'S']. 1 スロット0に感染者1名,残りは感受性が高い小都市 ['S', 'I1', 'S', 'S']. 1 スロット1に感染者1名,残りは感受性が高い小都市 ['S', 'S', 'I1', 'S']. 1 スロット2に感染者1名,残りは感受性が高い小都市 ['S', 'S', 'S', 'I1']. 1 スロット3に感染者1名,残りは感受性が高い小都市 ['I1', 'R', 'R', 'R']. 1 スロット0に感染者1名,残りは回復した小都市 ['I0', 'S', 'I1', 'R']. 2 タイプが混在する小都市 20人都市 20 オール・イン・ステート「I0」の大きな都市 20人都市 20 さまざまな感染症が混在する大きな都市 また,上の表の情報は,tests/ サブディレクトリにある count_infected.json という ファイルでも確認することができます.私たちがあなたに書いてもらう各関数について,対応 する json ファイルに列挙されたテストを提供します.このファイルを編集してはいけませ んし,これらのファイルの正確なフォーマットを理解する必要もありませんが,時折参照する 必要があるかもしれません (例えば,最後の 2 つのテストケースで使用した 20- 人の都市 を正確に確認したい場合, count_infected.json ファイルでそれらを見つけることがで きます). 私たちの目標は,十分なテストカバレッジを確保することです.つまり,コードの中で可 能な限り多くの異なるケースを想定したテストを実施することです.たとえば,次のふた つの都市についてのみテストを書きたくなるかもしれません. ['s', 'i0', 'i0', 's', 'r']. ['s', 's', 's', 's', 's']. しかし,I0以外の感染状態を考慮し忘れたり,感染日数が常に一桁であると仮定して解答 を書いたらどうだろうか.上記のどちらのテストも,そのようなケースをカバーすること はできない. このタスクのテストを実行するには,次のように実行するだけです. py.test−xvkcountこのコマンドが何をしているのか,出力が何を意味しているのか,よくわからない?詳細については,コードのテストのページを参照してください.タスク1に関するデバッグの提案とヒントデバッグ中にコードを変更した場合は,エディタに保存することを忘れないようにしてください.このステップをスキップするのはよくある誤りです.幸いなことに,autoreloadパッケージを使用することで,コードを変更した後にリロードするのを忘れるという,もうひとつのよくある誤りをなくすことができます.(入門編を読み飛ばした方は,戻ってオートリロードとインポートサーをセットアップする手順に従ってください)タスク2:隣人が感染しているか?次に,hasinfectedneighborという関数を書いて,リスト中のある位置にいる感染者が,少なくとも1人の感染した隣人を持つかどうかを判断することにします.具体的には,都市と人の位置が与えられたら,その都市における指定した人の左隣と右隣の位置(存在する場合)を計算し,どちらかが感染状態にあるかどうかを判断するコードである.都市の最初の人は右隣人を持つが左隣人を持たず,都市の最後の人は左隣人を持つが右隣人を持たないことを思い出してください.あなたのコードでは,以下の処理を行う必要があります.これらの特殊なケースこの関数は,影響を受けやすい人物を含むポジションで呼び出すのが筋なので,コードを見ると,次のような行が含まれていることがわかります.assertcity[position]=="S"を使用して,この関数が感染しやすい人に呼び出されたことを検証します.一般に,アサーションは次のような形式をとる.assert<ブール式>アサーションは,コードが有効な入力を受け取っているかどうかを確認するための便利な方法です.アサーションの条件として指定した真偽値がFalseと評価されると,アサーション文はその関数を失敗させます.単純なアサーションは,関数が誤ってコールされた場合にそれを強調することで,デバッグ作業を大幅に簡略化することができます.タスク2のテスト前のタスクと同様に,自動テストを実行する前に,ipython3でコードを試してみることから始めることをお勧めします.例えば,hasaninfectedneighborの呼び出しのサンプルは以下の通りです.In[8]:sir.hasinfectedneighbor([′I1′,′S′,′S′],1)Out[8]:真In[9]:sir.hasinfectedneighbor([′S′,′I1′,′IO′],0)Out[9]:真In[9]:sir.hasinfectedneighbor([′S′,′R′,′IO′],0)Out[9]:FalseIn[10]:sir.hasinfectedneighbor([′S′,′I0′,′S′],2)Out[10]:真In[10]:sir.hasinfectedneighbor([′S′],0)Out[10]:偽最初のサンプル呼び出しでは,位置1にいる感受性の高い人が,感染した隣人を持つかどうかを調べています.左隣人(位置0)は感染しているので,結果はTrueになるはずです.次の呼び出しでは,位置0にいる感受性の高い人が感染した隣人を持つかどうかをチェックします.この人物は左隣人を持っていません.しかし,右隣の1番目の人は感染しているので,結果は真になるはずです.この場合,位置1の人は感染していないので,期待される結果はFalseである.4回目の呼び出しで,位置2の人物をチェックします.この人物には右隣がいません.その左隣人,位置1にはが感染したため,期待される結果はTrueになりますが.最後に呼び出すとFalseを返します.なぜか?なぜなら,この都市に一人しかいない人は,隣人を持たないので,定義上,感染した隣人を持たないからです.正しい解答は,「この都市に一人しかいない場合」をチェックする条件を含む必要がないことを考慮に入れてください.このコードは,1人しかいない都市を含む,あらゆる規模の都市で動作するはずである.ヒント:この一人都市の人は,リストの最初と最後の両方の要素である.以下の表はhasaninfectedneighborのテストに関する情報です.各行には,そのテストのcityおよびposition引数に渡される値,期待される結果,そしてテストの目的についての簡単な説明が含まれています.このデータはtests/hasinfectedneighbortests.jsonでも見ることができます.hasaninfectedneighbor市に関するテスト位置期待される結果説明[′I0′,′S′,′S′]1真左隣が感染.[′i1000′,′s′,′s′].1真左隣が数桁の感染日数で感染している.[′r′,′s′,′i0′].1真右隣が感染.[′r′,′s′,′i1000′].1真右隣が複数桁の日数で感染している.[′i1′,′s′,′i0′].1真両隣が感染している[′S′,′S′,′R′].1偽どちらの隣人も感染していない.[′r′,′s′,′s′,′i1′]です.2真人以上いる街.右隣が感染.[′r′,′i200′,′s′,′r′].2真人以上いる街.左隣が感染.[′i0′,′s′,′s′,′r′].2偽人以上いる街.どちらの隣人も感染していない.[′s′,′s′,′s′,′i1′].0偽一人称,隣人(右)は感染していない.[′s′,′i1′,′s′,′i1′]です.0真一人称,シングルネイバー(右)感染.[′i0′,′s′,′s′,′s′].3偽最後の人,シングルネイバー(左)は感染していない[′I0′,′S′,′I10′,′S′].3真最後の人,シングルネイバー(左)が感染[′S′]0偽街で一人.これらのテストは,Linuxのコマンドラインから以下のコマンドを実行することで行うことができます. py.test -xvk has タスク2に関するデバッグの提案とヒント print文は,コードが実際に何を行っているかを確認するための最も直感的な方法の一つで あり,デバッグの常套手段となるでしょう.もし,コードを書き始めたり,関数から正しい 値を返したりするのに苦労しているなら,次のようなデバッグ方法を考えてみてください. どの隣人が存在するかを印刷します. これらの近傍領域について計算した位置を印刷し,これらの近傍領域について抽出し た値を印刷します. これらの値で,あなたのコードは期待通りに動作していますか? また,関数から目的の値を出力するのではなく,返していることを確認してください. 解答を提出する前に,デバッグ用のコード(print文など)を削除するのを忘れないでく ださい. タスク3:ポジションに人を進める 3つ目のタスクは,関数 advance_person_at_position を完成させることです.この関 数の目的は,ある人の病状をある日から次の日に進めることです.都市,その都市内の人の 位置,そして感染が伝染するまでの日数が与えられたとき,あなたの関数はその人の次の状 態を決定しなければならない.具体的には,もしその人が Susceptible ('S'): 感染した隣人がいるかどうかを判断し (has_an_infected_neighbor 関数を使用する),いる場合は最初の感染状態 ('I0') に変更する必要があります. それ以外の場合は,Susceptible('S')状態のままです. 感染('I'の後に整数が続く;この整数をxと呼ぶことにする):その人が感染したままかど うかを判断する(つまり. ) に移動し,次の感染状態 (例えば 'I0' は 'I1' に,'I1' は 'I2' に,等) に移行 するか,回復状態 ('R') に切り替わります.感染者の新しい状態を計算するには,状態か ら感染日数を文字列として取り出し,それを整数に変換し,感染日数cと比較する必要がある .感染者が感染のままと判断した場合は,'I'と . 回復者('R'):何もしない方がいい.回復した人はその状態のままです. 例として,advance_person_at_position の次の呼び出しを考えてみる. In [22]: sir.advance_person_at_position(['I0', 'I1', 'R'], 0, 2) Out[22]:"I1" In [22]: sir.advance_person_at_position(['I0', 'I1', 'R'], 1, 2) Out[22]:"R" In [22]: sir.advance_person_at_position(['I0', 'I1', 'R'], 2, 2) Out[22]:"R" 最初の呼び出しは,位置0の人が状態'I0'から'I1'に移動することを決定する.2回目の呼 び出しで,位置1の人が状態「R」に移行したと判定される.これは,パラメータで人が2日 間しか感染しないことが指定されているからである.そして最後に,3回目の呼び出しで, 位置2の人がすでに状態「R」であるため,「R」が返される. タスク3のテスト 下の表は advance_person_at_position のテストに関する情報を提供します.各行には ,そのテストの city, position, days_contagious 引数に渡される値,期待される結 果,そしてテストの簡単な説明が含まれています. advance_person_at_position Cityに関す るテスト ポジション 日数 伝染性結果説明 ['I1', 'S', 'S'] 1 3 I0 左隣が感染,感受性が高い人が感染.['s', 's', 'i0' ]. 1 3 I0 右隣が感染,感受性が高い人が感染.['i20', 's', 'i0'] とする. 1 3 I0 両隣が感染している,感受性の高い人が感染する['r', 's', 'r' ]. 1 3 S 隣人も感染せず,感受性の高い人も感染しない. ['i1', 's', 's', 's']です. 2 3 S 隣人も感染せず,感受性の高い人も感染しない. [s', 's', 'i0'] 0 3 S 右隣のみ,感受性が高い人は感染しない.['s', 'i1500', 'i0' ]である. 0 3 I0 右隣の人だけが,感染しやすい.['i1', 'r', 's'] とする. 2 3 S 左隣のみ,感受性が高い人は感染しない.['i1', 'i1500', 's'] とする. 2 3 I0 左隣のみ,感受性が高い人が感染する.['i1', 'i1500', 's']である. 0 3 I2 Infectedはインクリメントする必要がありま す.['i2', 'i1500', 's']です. 0 3 R Infectedはrecoverに変換する必要があります. ['i2', 'i1500', 's']です. 1 2000 I1501 感染した場合はインクリメントする必要があります.感染した日数が多い['i2', 'i1500', 's'] とする. 1 1501 R 感染者は回復する.伝染する日数が多い.['i2', 'i1500', 'r']である. 2 2000 R 回収され,変化なし. これらのテストは,Linuxのコマンドラインから以下のコマンドを実行することで行うことが できます. py.test−xvkadvanceタスク4:シミュレーションを1日前倒しする4つ目の課題は,simulateoneday関数を完成させることです.この関数はシミュレーションの1日をモデル化し,runsimulationのヘルパー関数として動作します.より具体的には,simulateonedayは以下のようになります.は,一日の始まりにおける都市の状態と,人が感染している日数cを取り,新しい病気の状態のリスト(すなわち,一日後の都市の状態)を返す.この関数の実装では,advancepersonatpositionを使用して,市内にいる各人の新しい状態を決定する必要があります.例えば,こんな感じです.24]:sir.simulateoneday([′S′,′I0′,′S′],2)Out[24]です.[′i0′,′i1′,′i0′].0と2の位置にいる感受性の高い人がともに感染し(ともに感染した隣人がいる),1の位置にいる人が次の感染状態(「I0」から「I1」)に進んでいることに注目してください.タスク4のテスト下の表はsimulateonedayのテストに関する情報を提供します.各行には,そのテストのcityとdayscontagiousの引数に渡される値,期待される結果,そしてテストの簡単な説明が含まれています.simulateoneday市に関するテスト伝染性日数期待される結果説明[i0′,′i1′,′i100′]200[′i1′,′i2′,′i101′].I値は正しくインクリメントされていますか?[′i2′,′i2′,′i2′]となります.3[r′,′r′,′r′].I値からR値への変換は正しく行われていますか?[′r′,′r′,′r′].3[r′,′r′,′r′].Rの値は変更しないでください.[′i1′,′s′,′i1′].3[′i2′,′i0′,′i2′].感受性の強い人が感染する[′I1′,′S′,′I1′].2[′r′,′i0′,′r′].感染しやすい人は,その日のうちに隣の人が回復しても,感染してしまうのです.[′s′,′i0′,′s′]2[′i0′,′i1′,′i0′].感受性の高い2人が感染する.[彡′′s′′s′′s′′s′′s′′s′′です.]2[′s′,′s′,′s′].感染しやすい人は誰も感染しない30人都市2テストを見る大都市・中程度の感染率これらのテストは,Linuxのコマンドラインから以下のコマンドを実行することで行うことができます. py.test -xvk one タスク4に関するデバッグの提案 もし,開始時に苦労したり,関数で正しい値を返すことができない場合は,各人の旧病状と 新病状をプリントアウトして,新病状がすべてのケースで正しいことを確認することを検討 してください. タスク 5: シミュレーションの実行 5番目のタスクはrun_simulation関数を完成させることです.この関数は,都市の開始状 態と人が感染している日数を受け取り,都市の最終状態とシミュレーションした日数をタプ ルとして返します.この関数はさらに2つのオプションのパラメータ(random_seedと vaccine_effectiveness)を取ることに気づきますが,これらのパラメータは次のタスク まで使用しませんので,無視してかまいません. この関数は,シミュレーションの停止条件である「都市に感染者がいなくなる」まで simulate_one_day を繰り返し呼び出すことで,1つのシミュレーションを実行する必 要があります.このとき,この関数はシミュレーションを行った日数もカウントしなけれ ばなりません. シミュレーション開始時に停止条件が真であれば,シミュレーション日数は0になることを 考慮に入れてください. ここでは,この機能の使用例を2つ紹介します. 32]: sir.run_simulation(['S', 'S', 'I0'], 3) Out[32]: (['R', 'R', 'R'], 5) 33]: sir.run_simulation(['S', 'R', 'I0'], 3) Out[33]: (['S', 'R', 'R'], 3) テストタスク5 このタスクのために,5つのテストを用意しました. run_simulation のテスト スターティングシテ ィデイズ伝染 期待される成果:都市,シミュレーション日数 説明 [s', 's', 'i0'] 3 (['r', 'r', 'r'], 5) 誰もが感染し,回復する.['s', 'r', 'i0' ]です. 3 (['s', 'r', 'r'], 3) 1回のみの感染,回復者が感受性者を防ぐ 感染から['r', 's', 's']. 2 (['r', 's', 's'], 0) 出発都市に感染者がいないため,1日もシミュレートしない.['r', 'i0', 's', 'i1', 's', 'r', 's']. 10 (['r', 'r', 'r', 'r', 'r', 's'], 11) 中都市. 30人都市 2 (想定される都市はrun_simulation_tests.json参照,8) 大都市. これらのテストは,Linuxのコマンドラインから以下のコマンドを実行することで行うことが できます. py.test−xvkrunタスク5のデバッグのヒント都市の最終状態が間違っている場合は,日(0,1,2など),simulateonedayの呼び出し前の疾病状態,simulateonedayの呼び出し後の疾病状態を表示してみてください.タスク6:都市をワクチンで守る次のタスクは,新しい状態のサポートを追加することです.Vaccinated(′V′)です.これにはvaccinatecity関数の実装とrunsimulation関数の更新が必要です.V′状態は,実際には′R′状態と全く同じ振る舞いをする.つまり,ワクチンを接種した人は病気に対する免疫があり,感染することはない.しかし,′R′状態は人が感染を経験した後にシミュレーション中に到達するのに対し,′V′状態はシミュレーション開始前に到達し,街中の感染しやすい人にワクチンを接種することになるのです.しかし,100%有効なワクチンはありません.つまり,感受性の高い人にワクチンを投与しても,無条件に「V」状態に変化するわけではありません.その代わりに,我々のシミュレーションでは,0.0から1.0までのワクチン効果率vというパラメータを追加します.この演習では,ワクチンの有効性を,重みのあるコインをはじくことに似ていると考えることができます.つまり,vが0.8であれば,コインは80裏」を表します.ここで,コインに「表」の代わりに「ワクチンは免疫を与える」,「裏」の代わりに「ワクチンは免疫を与えない」と書かれていると想像してください.そこで,ワクチンを接種した感受性の高い人それぞれについて,この重みのあるコインを投げて,ワクチンが効く(その人は「V」状態に切り替わる)か効かない(その人は「S」状態のまま)かを判断します.Pythonで「コインをはじく」ためには,乱数発生器を使います.具体的には,0.0から1.0の間のランダムな浮動小数点数を返す関数,random.random()を呼びます.返された値を次のように解釈します.乱数が厳密にはvより小さい場合,ワクチンは機能する.乱数がv以上の場合,ワクチンは効きません.つまり,vaccinatecityは都市とワクチン効果率を受け取り,上記の規則に従って各罹患者がワクチンを接種した新しい都市を返します.vaccinatecityを実装したら,runsimulationを修正して,どの日のシミュレーションを行う前にも一度,vaccinatecityを呼び出すようにする必要があります.つまり,このタスクを完了すると,以前のテストが壊れることはありません.また,advancepersonatpositionが′V′ステートで正しく動作することを確認する必要があります.特に,′V′状態にある人は,その状態を維持しなければなりません.この関数が期待通りに動作しているかどうかは,次のようにテストすることができます.In[3]:sir.advancepersonatposition([′S′,′V′,′S′],1,2)Out[3]:′V′.もし上記の呼び出しが′V′以外を返した場合,advancepersonatpositionで′V′状態を正しく処理していることを確認します.さて,乱数を使うにはちょっとした工夫が必要です.random.random()を試してみましょう.これを行うには,まず,randomモジュールをインポートする必要があります.1]:インポートランダムでは,この関数を何度か呼び出してみてください.2]では,ランダム.random()アウト[2].0.5952992477552623]では,ランダム.random()アウト[3].0.81596063434746484]では,ランダム.random()アウト[4].0.30061626031208444乱数の厄介な点は,random.random()を試したときに,ほぼ確実に異なる数値が表示されることです(これは理にかなっています:この関数は乱数を返すことを目的としています!)これはデバッグやテストを複雑にします.なぜなら,(vaccinatecityなどの)random.random()に依存している関数を呼ぶと,毎回異なる結果になる可能性があるからです.幸い,random.random()をシード値で初期化することで,呼び出されたときに同じ数値列を返すようにすることができます.乱数生成器のシード値を設定するのは,デバッグの際によく行われることです.シードを積極的に設定しない場合,乱数生成器は通常,システムクロックからシードを導出します.多くのテストが同じシード(20170217)を使用しているため,利便性のためにsir.pyでTESTSEEDという定数をこの値で定義しています.この値はテストにのみ使用し,あなたが書くコードのどこにも現れてはいけません.試しにsir.TESTSEEDの値で種を設定し,ipython3の乱数生成器をいくつか呼び出してみましょう.11]:sir.TESTSEEDアウト[11]:2017021712]:random.seed(sir.TESTSEED)13]では:random.random()アウト[13].0.4897149250460921514]では,ランダム.random()アウト[14].0.2301056661921078215]で:random.seed(sir.TESTSEED)16]では:random.random()アウト[16].0.4897149250460921517]では,random.random()です.アウト[17].0.23010566619210782random.random()の3回目と4回目の呼び出しは,最初の2回の呼び出しとまったく同じ値を生成していることに注目してください.なぜでしょうか?1回目と3回目の呼び出しの前に,種をまったく同じ値に設定したからです.このrandomの挙動にはもう一つ意味があります.vaccinatecityは,感受性の強い人に出会ったときだけrandom.random()を呼び出すことが重要です.もし,すべての人(′S′状態でない人も含む)に対して乱数発生器を呼び出すと,あなたのコードはその後のタスクで私たちのものとは異なる答えを生成する可能性があります.タスク6のテストこれまでのタスクとは異なり,vaccinatecityを呼び出す前にランダムシードを初期化し,期待通りの結果が得られるように注意する必要があります.例えば22]:random.seed(sir.TESTSEED)23]:sir.vaccinatecity([′S′,′S′,′S′,′S′,′I0′,′S′],0.8)Out[23]です.[′v′,′v′,′v′,′s′,′i0′,′v′].24]:random.seed(sir.TESTSEED)25]:sir.vaccinatecity([′S′,′S′,′S′,′S′,′I0′,′S′],0.3).Out[25]:[′s′,′v′,′s′,′s′,′i0′,′s′].ただし,更新したrunsimulationをテストする際には,この関数が乱数種をパラメータとして受け取ることを考慮に入れてください.つまり,runsimulationの内部でrandom.seedを呼び出す必要があるということです.以下は使用例です.34]:sir.runsimulation([′S′,′S′,′S′,′S′,′I0′,′S′],2,sir.TESTSEED,0.0)を実行.Out[34]:([′R′,′R′,′R′,′R′,′R′,′R′],′R′,′R′),7)35]:sir.runsimulation([′S′,′S′,′S′,′S′,′I0′,′S′],2,sir.TESTSEED,0.3)を実行.Out[35]:([′S′,′V′,′R′,′R′,′R′,′R′],′R′,′R′),5)36]:sir.runsimulation([′S′,′S′,′S′,′S′,′I0′,′S′],2,sir.TESTSEED,0.8)を実行.Out[36]:([′V′,′V′,′V′,′R′,′R′,′V′],3)この結果がどのように意味を持つかに注目してください.ワクチンの有効性が増すと,流行の期間は短くなります.以下の表はvaccinatecityの自動テストに関する情報を提供します.各行には,乱数生成器を初期化するために使用されるシード,そのテストでcityとvaccineeffectiveness引数に渡される値,そして期待される結果が含まれています.最後の列は,そのテストについて簡単に説明しています.vaccinatecityのテストシード都市ワクチンの効果期待される結果説明20170217[′s′,′s′,′s′,′s′,′i0′,′s′]0.0[′s′,′s′,′s′,′s′,′i0′,′s′].全く効果のないワクチン.誰も接種してはいけない.20170217[′s′,′s′,′s′,′s′,′i0′,′s′]1.0[′v′,′v′,′v′,′v′,′i0′,′v′].完全に有効なワクチン.感受性の高い人は全員接種すべき.20170217[i0′,′i1′,′i2′,′r′]1.0[′i0′,′i1′,′i2′,′r′].完全に有効なワクチンだが,感受性の高い人はダメだ.みんな元の状態のままでいい.20170217[′s′,′s′,′s′,′s′,′i0′,′s′]0.3[′s′,′v′,′s′,′s′,′i0′,′s′].部分的に有効なワクチン.1人だけの接種で終了20170217[′s′,′s′,′s′,′s′,′i0′,′s′]0.8[′v′,′v′,′v′,′s′,′i0′,′v′].部分的に有効なワクチン.感受性の高い1人を除いて,すべて接種終了.20170218[′s′,′s′,′s′,′s′,′i0′,′s′].0.8[′v′,′v′,′v′,′v′,′i0′,′v′].部分的に有効なワクチンだが,結果に影響を与える種が違う.そして,この表は,(タスク5のテストとは異なり)0より大きいワクチン効果率を使用するrunsimulationの一連の追加テストに関する情報を提供します.runsimulationのテスト(ワクチン接種あり)Seedスターティングシティデイズ伝染ワクチンの効果期待される成果:都市,シミュレーション日数説明20170217[′s′,′s′,′s′,′s′,′i0′,′s′]20.0[[′r′,′r′,′r′,′r′,′r′,′r′],7].完全に効果のないワクチン(誰も接種していない,タスク5のようなもの)20170217[′s′,′s′,′s′,′s′,′i0′,′s′]20.3[[′s′,′v′,′r′,′r′,′r′,′r′],5].ワクチン効果=0.320170218[′s′,′s′,′s′,′s′,′i0′,′s′]20.3[[′s′,′s′,′s′,′v′,′v′,′r′,′r′],3].ワクチン効果=0.3(種が違う)20170217[′s′,′s′,′s′,′s′,′i0′,′s′]20.8[[′v′,′v′,′v′,′r′,′v′],3].ワクチン効果=0.820170218[′s′,′s′,′s′,′s′,′i0′,′s′]20.8[[′v′,′v′,′v′,′v′,′r′,′v′],2].ワクチン効果=0.8(種が違う)20170217[′s′,′s′,′s′,′s′,′i0′,′s′]21.0[[′v′,′v′,′v′,′v′,′r′,′v′],2].完全に有効なワクチン2017021730人都市20.5(予想都市はsimulationwithvaccine.json参照,5)大都市Linuxのコマンドラインから以下のコマンドを実行することで,これらのテストをすべて実行することができます. py.test -xvk vac vaccinate_cityのテストのみ,または更新されたrun_simulationのテストのみを実 行したい場合は,以下のいずれかを実行してください. py.test−xvkvaccinatecity py.test -xvk simulation_with_vaccine デバ ッグの提案とタスク 6 のヒント もし,コードを書き始めたり,関数で正しい値を返したりするのに苦労しているようでした ら,以下の提案を参考にデバッグを検討してみてください. random.random()が返す値を表示します. random.randomの呼び出し回数が正しいことを確認してください(都市で「S」に遭遇し たときだけ呼び出すようにします). ipython3でテストする場合,vaccinate_cityの各テスト呼び出しの前に,乱数生成器の種 をリセットしていることを確認してください. ただし,random.seed(sir.TEST_SEED)をコード内で呼び出さないように注意してくださ い.run_simulationの中でrandom.seedを呼び出し,常にrandom_seedパラメータを指 定する必要があります. タスク7:感染症がゼロになるまでの平均時間の決定 最後のタスクは, 関数を完成させることです. calc_avg_days_to_zero_infections は,ある都市の感染率がゼロになるまでの平均日 数を計算する.この関数は,都市の開始状態,伝染日数,ランダムシード,ワクチン効果率 ,実行する試行回数を引数にとり,num_trials の異なる試行回数にわたって都市が感染ゼ ロに到達するまでの平均日数を返す.都市が感染ゼロになるまでの日数は,単純に run_simulationが返す日数である. シミュレーションの試行を行うたびに,乱数種を 1 つずつ増やしていく必要があります.ま た,指定された方法とは異なる方法でシードを増加させた場合,あなたのコードは異なる結 果を生成する可能性があります(したがって,我々のテストに合格しない可能性があります) . 実装はrun_simulationを呼んでシードを設定するはずなので,前のタスクと違って, ipython3ではこの関数を実行する前にrandom.seedを呼ぶ必要はないです. この関数の使用例を紹介します. 52]: sir.calc_avg_days_to_zero_infections(['S', 'S', 'S', 'S', 'I0', 'S']), ...: 2, sir.TEST_SEED, 0.65, 5) アウト[52]:2.6 この関数はどのようにして平均2.6日に到達したのだろうか?各試行について,使用した種, 開始状態,終了状態,都市が感染ゼロになるまでの日数を示した表がこちらです. calc_avg_days_to_zero_infections による中間値 シミュレーション 番号 シード シミュレーション実行の開始状態 シミュレ ーション実行の終了状態 感染がゼロになる までの日数 0 20170217 ['s', 's', 's', 's', 'i0', 's']. [v', 'v', 's', 'v', 'r', 'r', 'r'] 3 1 20170218 ['s', 's', 's', 's', 'i0', 's']. ['v', 'v', 'v', 'v', 'r', 'v'] 2 2 20170219 ['s', 's', 's', 's', 'i0', 's']. ['s', 'v', 'v', 'r', 'r', 'v'] 4 3 20170220 ['s', 's', 's', 's', 'i0', 's']. [v', 'v', 'v', 's', 'v', 'r', 'v'] 2 4 20170221 ['s', 's', 's', 's', 'i0', 's']. ['v', 'v', 's', 's', 'v', 'r', 'v'] 2 試行ごとに種を変えているので,各試行で同じ結果が得られるとは限りません.試行の日数 は3,2,4,2,2なので,平均は次のようになる. . テストタスク7 このタスクのために10個のテストを用意しました.最初の3つはprint文で簡単にチェックで きます.4番目と5番目のタスクでは,多くの試行回数(100回)と異なる種を使用します. 試行回数が増えるにつれて,開始時の種はあまり重要でなくなることがわかるだろう.6番目 と7番目のテストでは,より大きな都市を使用します.そして最後の3つのテストはエッジケ ースをチェックする.1回の試行,100%有効なワクチン,感受性のある人がいない都市であ る. calc_avg_num_newly_infected のテスト スタート シード スターティングシテ ィデイズ伝染 ワクチンの有効性 試験の数 期 待される結果 説明 20170217 ['s', 'i1', 's', 'i0'] 2 0.8 5 2.2 手計算が可能なテストケース.20170217 ['s', 'i1', 's', 'i0'] 2 0.3 5 2.8 効果の低いワクチンは,流行を長引かせる 20170219 ['s', 'i1', 's', 'i0'] 2 0.8 5 2.4 異種格闘技戦 20170217 ['s', 'i1', 's', 'i0'] 2 0.8 100 2.31 試行回数が多いこと20170218 ['s', 'i1', 's', 'i0' ]. 2 0.8 100 2.31 種を変えての大量トライアル20170217 30人都市 2 0.8 10 3.5 30人都市,有効なワクチン,伝染する日数も少ない 20170217 49人市 2 0.3 100 5.48 49人市,ワクチンの効果が低い,伝染する日数が少ない. 20170217 ['s', 's', 'i1', 'i1', 'i1', 's'] 2 0.5 1 3.0 エッジケース1審20170217 ['S', 'S', 'i1','i1','i1','i1'. 'I1', 'S'] 2 1.0 10 1.0 エッジケース 100%有効なワクチン 20170217 ['R', 'R', 'R','R']です. 2 0.5 10 0.0 エッジケース:人口がすでに回復しているため,すべてのシミュレーションで日数がゼロ になるはずです. これらのテストは,Linuxのコマンドラインから以下のコマンドを実行することで行うことが できます. py.test−xvkavg全部まとめてみるsir.pyには,お手持ちの関数を呼び出して1回のシミュレーションを実行したり,ある都市の感染率がゼロになるまでの平均日数を計算したりするコードが含まれています.このプログラムを−−helpフラグ付きで実行すると,引数ごとに使用するフラグが表示されます. python3 sir.py --help 使用 法: sir.py [OPTIONS] CITY コマンドライン引数を処理し,作業を行う. オプションです. --伝染性日数 INTEGER --random_seed INTEGER --ワクチン効果 FLOAT --num-trials INTEGER -タスクタイプ [シングル|アベレージ]の場合 --debug --help このメッセージを表示して終了します . 都市は,"S, S, I0 "のようにカンマ区切りの文字列で指定します. ここでは,1つのシミュレーションを実行するこのプログラムの使用例を紹介します. python3sir.py"S,S,I0"−−randomseed=20170217−−vaccineeffectiveness=0.5−−days−contagious=3−−task−type=singleで,これが出力されるはずです.シミュレーションを1回実行すると...最終的な都市:[′V′,′V′,′R′]シミュレーションされた日数:3ここでは,このプログラムの使用例として,市が感染ゼロになるまでの平均日数を計算します. python3 sir.py "S, S, I0" --random_seed=20170217 --vaccineeffectiveness=0.5 --days-contagious=3 --num-trials=5 --tasktype=average 複数のトライアルを実行中... 5回の試験で,感染者数がゼロになるまでに平均3.4日かかった. グレーディング プログラミングの課題は,一般的なルーブリックに従って採点されます.具体的には,完全 性,正確性,デザイン,スタイルについて点数をつけます.(カテゴリの詳細については,PA ルーブリックのページを参照してください). 各カテゴリーの正確な加重は,課題によって異なります.この課題では,重みは次のようにな ります. 完成度:75 正答率:15 デザイン:0 スタイル:10 スコアの完全性の部分は,自動化されたテストを使用して決定されます.自動テストのス コアを得るには,「コードのテスト」ページで説明したように,採点者スクリプトを実行 するだけです. クリーンアップ 最終的なソリューションを提出する前に,以下を削除する必要があります. デバッグのために追加したprintステートメントと 形式のすべてのインラインコメント."あなたのコードはここです" と "REPLACE ..." また,あなたのコードをスタイルガイドと照らし合わせてみてください.変数名は適切なもの を使用したか?長すぎる行はないか,など. ヘッダーコメント(各関数の目的,入力,戻り値を記述した三重引用符付きの文字列)は 削除しないでください. クリーンアップを行う際には,定期的にファイルを保存し,テストを通してコードを実行し, その過程でコードが壊れていないことを確認する必要があります. サブミッション 提出は Gradescope (Canvas サイトからリンク) を通して行ってください. Programming Assignment #1」の課題では,ファイル sir.py をアップロードしてくだ さい(他のファイルはアップロードしないでください!).注意してください. 締め切り前であれば,何度でも応募が可能です. 提出遅延に関するポリシーをご確認の上,送信してください. 完全性のスコアは自動テストのみに基づいて決定されますが,暗記によってテストに合格し ようとした場合(例:考えられる各テスト入力に対して期待される出力をハードコードした コードを書いた場合)には,スコアを調整することがあります. Gradescope は,お客様のコードを実行したときに取得したテストのスコアを報告します .私たちの採点スクリプトを実行したときに得られるスコアと Gradescope が報告する スコアの間に矛盾がある場合は,私たちが調査しますのでお知らせください. 謝辞この課題は,EasleyとKleinbergの著書「Networks, Crowds, and Markets」の 中のSIRモデルについての議論に触発されました.この課題のオリジナルはEmma Nechamkinが書きました.