From data-science
Converts between PolicyEngine YEAR and MONTH definition periods in formulas, including period.this_year and period.first_month patterns for correct variable access.
How this skill is triggered — by the user, by Claude, or both
Slash command
/data-science:policyengine-period-patterns-skillThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Essential patterns for handling different definition periods (YEAR, MONTH) in PolicyEngine.
Essential patterns for handling different definition periods (YEAR, MONTH) in PolicyEngine.
| From | To | Method | Example |
|---|---|---|---|
| MONTH formula | YEAR variable | period.this_year | age = person("age", period.this_year) |
| YEAR formula | MONTH variable | period.first_month | person("monthly_rent", period.first_month) |
| Any | Year integer | period.start.year | year = period.start.year |
| Any | Month integer | period.start.month | month = period.start.month |
| Annual → Monthly | Divide by 12 | / MONTHS_IN_YEAR | monthly = annual / 12 |
| Monthly → Annual | Multiply by 12 | * MONTHS_IN_YEAR | annual = monthly * 12 |
Note: QUARTER is NOT used in PolicyEngine US
from policyengine_us.model_api import *
class annual_income(Variable):
definition_period = YEAR # Annual amount
class monthly_benefit(Variable):
definition_period = MONTH # Monthly amount
class is_head(Variable):
definition_period = ETERNITY # Never changes
When accessing a variable with a different definition period than your formula, you must specify the target period explicitly.
# ✅ CORRECT - MONTH formula accessing YEAR variable
def formula(person, period, parameters):
age = person("age", period.this_year) # Gets actual age
# ❌ WRONG - Would get age/12
def formula(person, period, parameters):
age = person("age", period) # BAD: gives age divided by 12!
Use Case: Monthly benefits need annual demographic data
class monthly_benefit_eligible(Variable):
value_type = bool
entity = Person
definition_period = MONTH # Monthly eligibility
def formula(person, period, parameters):
# Age is YEAR-defined, use period.this_year
age = person("age", period.this_year) # ✅ Gets full age
# is_pregnant is MONTH-defined, just use period
is_pregnant = person("is_pregnant", period) # ✅ Same period
return (age < 18) | is_pregnant
Stock variables (point-in-time values like assets) are typically YEAR-defined
class tanf_countable_resources(Variable):
value_type = float
entity = SPMUnit
definition_period = MONTH # Monthly check
def formula(spm_unit, period, parameters):
# Assets are stocks (YEAR-defined)
cash = spm_unit("cash_assets", period.this_year) # ✅
vehicles = spm_unit("vehicles_value", period.this_year) # ✅
p = parameters(period).gov.tanf.resources
return cash + max_(0, vehicles - p.vehicle_exemption)
period vs period.this_yearWhen accessing a YEAR variable from a MONTH formula, should the value be divided by 12?
period (let auto-conversion happen)period.this_year (prevent auto-conversion)period)Flow variables where you want the monthly portion:
class monthly_benefit(Variable):
definition_period = MONTH
def formula(person, period, parameters):
# ✅ Use period - want $2,000/month from $24,000/year
monthly_income = person("employment_income", period)
# Compare to monthly threshold
p = parameters(period).gov.program
return monthly_income < p.monthly_threshold
Why: If annual income is $24,000, you want $2,000/month for monthly eligibility checks.
period.this_year)Stock variables and counts where division by 12 is nonsensical:
1. Age
# ❌ WRONG - gives age/12
age = person("age", period) # 30 years → 2.5 "monthly age" ???
# ✅ CORRECT - gives actual age
age = person("age", period.this_year) # 30 years
2. Assets/Resources (Stocks)
# ❌ WRONG - gives assets/12
assets = spm_unit("spm_unit_assets", period) # $12,000 → $1,000 ???
# ✅ CORRECT - gives point-in-time value
assets = spm_unit("spm_unit_assets", period.this_year) # $12,000
3. Counts (Household Size, Number of Children)
# ❌ WRONG - gives count/12
size = spm_unit("household_size", period) # 4 people → 0.33 people ???
# ✅ CORRECT - gives actual count
size = spm_unit("household_size", period.this_year) # 4 people
4. Boolean/Enum Variables
# ❌ WRONG - weird fractional conversion
status = person("is_disabled", period)
# ✅ CORRECT - actual status
status = person("is_disabled", period.this_year)
Accessing YEAR variable from MONTH formula?
│
├─ Is it an INCOME or FLOW variable?
│ └─ YES → Use period (auto-convert to monthly) ✅
│ Example: employment_income, self_employment_income
│
└─ Is it AGE, ASSET, COUNT, or BOOLEAN?
└─ YES → Use period.this_year (prevent conversion) ✅
Examples: age, assets, household_size, is_disabled
class monthly_tanf_eligible(Variable):
value_type = bool
entity = Person
definition_period = MONTH
def formula(person, period, parameters):
# Age: Use period.this_year (don't want age/12)
age = person("age", period.this_year) # ✅
# Assets: Use period.this_year (don't want assets/12)
assets = person("assets", period.this_year) # ✅
# Income: Use period (DO want monthly income from annual)
monthly_income = person("employment_income", period) # ✅
p = parameters(period).gov.tanf.eligibility
age_eligible = (age >= 18) & (age <= 64)
asset_eligible = assets <= p.asset_limit
income_eligible = monthly_income <= p.monthly_income_limit
return age_eligible & asset_eligible & income_eligible
class monthly_income_limit(Variable):
definition_period = MONTH
def formula(household, period, parameters):
# Get annual parameter
annual_limit = parameters(period).gov.program.annual_limit
# Convert to monthly
monthly_limit = annual_limit / MONTHS_IN_YEAR # ✅
return monthly_limit
class federal_poverty_guideline(Variable):
definition_period = MONTH
def formula(entity, period, parameters):
# Get year and month as integers
year = period.start.year # e.g., 2024
month = period.start.month # e.g., 1-12
# FPG updates October 1st
if month >= 10:
instant_str = f"{year}-10-01"
else:
instant_str = f"{year - 1}-10-01"
# Access parameters at specific date
p_fpg = parameters(instant_str).gov.hhs.fpg
return p_fpg.first_person / MONTHS_IN_YEAR
def formula(entity, period, parameters):
# Parameters use current period
p = parameters(period).gov.program.benefit
return p.amount
def formula(entity, period, parameters):
# Access parameters at specific instant
p = parameters("2024-10-01").gov.hhs.fpg
return p.amount
Important: Never use parameters(period.this_year) - parameters always use the formula's period
For MONTH period tests (period: 2025-01):
Example 1: Basic MONTH Test
- name: Monthly income test
period: 2025-01 # MONTH period
input:
people:
person1:
employment_income: 12_000 # Input: Annual
output:
employment_income: 1_000 # Output: Monthly (12_000/12)
Example 2: Mixed Variables
- name: Eligibility with age and income
period: 2024-01 # MONTH period
input:
age: 30 # Age doesn't convert
employment_income: 24_000 # Annual input
output:
age: 30 # Age stays same
employment_income: 2_000 # Monthly output
monthly_eligible: true
Example 3: YEAR Period Test
- name: Annual calculation
period: 2024 # YEAR period
input:
employment_income: 18_000 # Annual
output:
employment_income: 18_000 # Annual output
annual_tax: 2_000
12_000 not 12000# WRONG - From MONTH formula
def formula(person, period, parameters):
age = person("age", period) # Gets age/12!
# CORRECT
def formula(person, period, parameters):
age = person("age", period.this_year) # Gets actual age
# WRONG - Double-converting; period already auto-divides by 12
fpg = spm_unit("spm_unit_fpg", period.this_year) / MONTHS_IN_YEAR
# CORRECT - Core auto-converts annual → monthly when you use period
fpg = spm_unit("spm_unit_fpg", period)
This applies to all YEAR-defined flow variables (income, FPG, dollar amounts). Using period from a MONTH formula auto-divides by 12. Using period.this_year / MONTHS_IN_YEAR is equivalent but redundant — just use period.
# WRONG - Comparing different units
monthly_income = person("monthly_income", period)
annual_limit = parameters(period).gov.limit
if monthly_income < annual_limit: # BAD comparison
# CORRECT - Convert to same units
monthly_income = person("monthly_income", period)
annual_limit = parameters(period).gov.limit
monthly_limit = annual_limit / MONTHS_IN_YEAR
if monthly_income < monthly_limit: # Good comparison
# WRONG - Expecting annual in MONTH test
period: 2024-01
input:
employment_income: 12_000
output:
employment_income: 12_000 # Wrong!
# CORRECT
period: 2024-01
input:
employment_income: 12_000 # Annual input
output:
employment_income: 1_000 # Monthly output
class tanf_income_eligible(Variable):
value_type = bool
entity = SPMUnit
definition_period = MONTH # Monthly eligibility
def formula(spm_unit, period, parameters):
# YEAR variables need period.this_year
household_size = spm_unit("spm_unit_size", period.this_year)
state = spm_unit.household("state_code", period.this_year)
# MONTH variables use period
gross_income = spm_unit("tanf_gross_income", period)
# Parameters use period
p = parameters(period).gov.states[state].tanf
# Convert annual limit to monthly
annual_limit = p.income_limit[household_size]
monthly_limit = annual_limit / MONTHS_IN_YEAR
return gross_income <= monthly_limit
npx claudepluginhub policyengine/policyengine-claude --plugin analysis-toolsEnforces PolicyEngine code style: direct parameter access, direct returns, combined boolean logic, and correct period usage.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.