OpenFisca-coreの設計、実装調査
調査して分かった内容を適宜更新
理解がまとまったらopenfisca-japan側のドキュメントとしてコミット
クラス
Variable
制度を実装する場合はこのクラスを継承
クラス属性
value_type: 変数の型(内部的にはnumpyのベクトル)
float は float32
entity: 世帯、個人等の計算単位
definition_period: 計算の期間(日、月、「永遠」(=単発or不変の値))
formula メソッドに計算式を実装
def formula(person, period, parameters)
※世帯の場合は household
parameters は省略可能
person('salary', period) で他のVariableの計算結果を取得できる
名前で参照しているので衝突注意!
ない場合は、デフォルト値指定orユーザー入力しない限り値を決められない
命名の参考
(英語の場合なので少し勝手が違うかも)
よく使う略語以外は略さない
接頭辞を付けることで名前衝突を防ぐ
income_tax_nb_parents(日本語なら 所得税における親の数 or 所得税_親の数)
衝突回避する場合全てに接頭辞を付けること(例:housing_tax_nb_parents)
Entity
計算単位を表わす(人物、世帯)
世帯の人数
household.nb_persons()
household.nb_persons(Household.ADULT) 特定の役割の世帯員の人数
判定
person.has_role(Household.ADULT) 世帯員が特定の役割かどうか
集計
household.members('salary', period) 各世帯員に対し変数の値を取得
逆に人物から世帯の変数も参照可能 person.household('basic_income', period)
特定の役割の世帯員の変数のみ抽出 household.partner('salary', period)
Period
期間を定義
変数を定義した期間と異なる機関に対して値を計算できない
その値が変わらない時間を期間として定義
例:「月給」は同じ月であれば変わらないので MONTH
書式
色々あるが YYYY, YYYY-MM, YYYY-MM-DD に統一すればよさそう
変数の値の計算に使用
simulation.calculate('housing_allowance', '2019-05')
異なる期間の変数を計算したい場合: ADD, DEVIDE を使用
person('salary', period, options = [ADD]) (年額のvariableから月額のvarable salaryを呼び出す)
異なる期間の変数を参照
period.offset(n, 'year') n年前
period('2023-11-01') の形式でも書ける
(日本の制度によくある「当年12月31日」はopenfiscaではシンプルには書けなさそう)
Parameter
yamlファイルで記述
.metadata.util: 単位
currency: 通貨(金額)
/1:割合
パラメータの名前(yamlファイル名)は 小文字とアンダースコアのみ使用する
values に値を記述
期間ごとに定義可能
Enum
種類を表わす
Enum クラスを継承
クラス属性に定義した値がそれぞれの要素になる
Enum を継承したクラスは種類そのものを定義
Enum の 値を表わすクラスは Variable を継承、その value_type にEnumを指定
Enum の各要素の属性
name: 名前(属性名そのものが文字列で得られる)
value:値(定義するときに代入した値)
内部実装
調査対象:41.2.0
formulaはどうやって呼び出される?
Variableにformula というメソッドがあると呼び出される仕組み
formulaの初期化: __init__ でのインスタンス初期化時
formula ではじまる名前
メソッド名に日付がある場合、日付情報とともに登録
formulaの取得: get_formula
例: 現在2023-11-11, 候補 formula_2023_10_01, formula_2023_11_01, formula_2023_12_01の場合 formula_2023_11_01Simulation#calculate
シミュレータ実行: Simulation#calculate
formulaの引数は?
上記で取得されたformulaを呼び出す
引数が (person, period) の場合と (person, period, parameters) の場合は、定義の仮引数の違いで判別している
第一引数には population
どこからきている?
Simulation のインスタンスは SimulationBuilder で作られる
simulation_builder.build_from_entities(tax_benefit_system, TEST_CASE)
名前の通り、第2引数は世帯情報をテストケースのyamlとおなじdict形式で記述
populationは tax_benefit_system.instantiate_entities() で初期化
tax_benefit_system は CountryTaxBenefitSystem で作られる
CountryTaxBenefitSystem :各国の制度の初期化を行う
TaxBenefitSystem を継承
(instantiate_entities の定義が見つからない…)
simulationのvariableの初期化
PopulationはProtocol を継承している→interfaceを表わしているだけでは?
Goみたいに継承を明示しないタイプのインターフェース
→実体はこのメソッドを定義している具象クラスを探せばよい
ここに実装があった
variableの探索はどうやって行われる?
ここでsimlation#calculateを呼び出す
simlation#calculateがformulaを探して実行→formula内のpopulation#__call__でsimlation#calculate実行とお互いに呼び合っている
variableの形式はどこで決まる?
parameterはどうやってプログラム上に読み込まれる?
ディレクトリ配下のyamlファイルをparameterとして読み込み
load_parameter_fileでyamlファイル読み込み
ディレクトリの場合は配下を読み込むことで再帰的に取得
→ディレクトリ構造をそのままフィールド名として取得できる
Simulation.tax_benefit_system.get_parameters_at_instant
呼び出されるのはこれ
instantで現在に適用可能なものを選択
python -m build とは?
ビルドし、配布可能な形式にする
OpenFiscaとは関係ない
テスト
input に変数を指定
変数名: 値の形式だが、定義されている期間が period で指定したものと異なる場合は 期間: 値 の形式で指定
世帯員や世帯について計算する場合は入れ子にする必要がある
code:yaml
- name: "IRPP - 給与収入が20,000ユーロの世帯"
period: 2012
absolute_error_margin: 0.5
input:
世帯員:
親1:
誕生年月日: 1972-01-01
公務員ボーナス: 500
# ...
家族:
- 親
- 親1
- 親2
- 子
- 子1
- 子2
世帯: # 続柄を書ける?
- 本人
- 親1
- 配偶者
- 親2
- 子
- 子1
- 子2
課税世帯:
# ...