.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_examples/basic/example_results_module.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_auto_examples_basic_example_results_module.py: ================== Simulation Results ================== This example demonstrates how to collect and analyze simulation results using the ``SimulationResults`` class. Learn how to: - Collect data during simulation runs - Access data via ``results["Producer.price"]`` or ``results.Producer.price`` - Use ``results.get()`` for programmatic access with optional aggregation - Use ``results.available()`` to discover collected variables - Export data to pandas DataFrames - Generate summary statistics The results module makes it easy to extract insights from simulations without manual data collection. .. GENERATED FROM PYTHON SOURCE LINES 21-26 Basic Data Collection --------------------- Pass ``collect=True`` to ``sim.run()`` to collect data automatically. This returns a ``SimulationResults`` object containing time series data. .. GENERATED FROM PYTHON SOURCE LINES 26-57 .. code-block:: Python import numpy as np import bamengine as bam # Run simulation with data collection # We collect Worker employed data without aggregation to calculate unemployment sim = bam.Simulation.init(n_firms=100, n_households=500, seed=42) results = sim.run( n_periods=50, collect={ "Producer": True, "Worker": ["employed"], # Boolean employed status for each worker "Employer": True, "Borrower": True, "Lender": True, "Consumer": True, # Capture timing: when to snapshot each variable during the period # Worker.employed should be captured after production runs (steady state) "capture_timing": { "Worker.employed": "firms_run_production", }, }, ) print(f"Collected results: {results}") print("\nMetadata:") print(f" Periods simulated: {results.metadata.get('n_periods', 'N/A')}") print(f" Firms: {results.metadata.get('n_firms', 'N/A')}") print(f" Households: {results.metadata.get('n_households', 'N/A')}") .. rst-class:: sphx-glr-script-out .. code-block:: none Collected results: SimulationResults(periods=50, firms=100, households=500, roles=[Worker, Producer, Employer, Borrower, Lender, Consumer], relationships=[None]) Metadata: Periods simulated: 50 Firms: 100 Households: 500 .. GENERATED FROM PYTHON SOURCE LINES 58-63 Discovering Available Data -------------------------- Use ``results.available()`` to list all collected variables as ``"Name.variable"`` strings. .. GENERATED FROM PYTHON SOURCE LINES 63-68 .. code-block:: Python print("All available data:") for key in results.available(): print(f" {key}") .. rst-class:: sphx-glr-script-out .. code-block:: none All available data: Borrower.credit_demand Borrower.gross_profit Borrower.loan_apps_head Borrower.loan_apps_targets Borrower.net_profit Borrower.net_worth Borrower.projected_fragility Borrower.retained_profit Borrower.total_funds Borrower.wage_bill Consumer.income Consumer.income_to_spend Consumer.largest_prod_prev Consumer.propensity Consumer.savings Consumer.shop_visits_head Consumer.shop_visits_targets Economy.avg_price Economy.inflation Economy.n_bank_bankruptcies Economy.n_firm_bankruptcies Employer.current_labor Employer.desired_labor Employer.n_vacancies Employer.recv_job_apps Employer.recv_job_apps_head Employer.total_funds Employer.wage_bill Employer.wage_offer Employer.wage_shock Lender.credit_supply Lender.equity_base Lender.interest_rate Lender.opex_shock Lender.recv_loan_apps Lender.recv_loan_apps_head Producer.breakeven_price Producer.desired_production Producer.expected_demand Producer.inventory Producer.labor_productivity Producer.price Producer.price_shock Producer.prod_mask_dn Producer.prod_mask_up Producer.prod_shock Producer.production Producer.production_prev Worker.employed .. GENERATED FROM PYTHON SOURCE LINES 69-75 Primary Data Access ------------------- The primary API uses bracket notation ``results["Name.variable"]`` or attribute access ``results.Name.variable``. Economy metrics are always collected automatically (no need to request them). .. GENERATED FROM PYTHON SOURCE LINES 75-116 .. code-block:: Python # Bracket access (recommended for most use cases) avg_price = results["Economy.avg_price"] inflation = results["Economy.inflation"] # Attribute access (convenient for interactive exploration) avg_price_attr = results.Economy.avg_price print(f"\nAverage price (bracket): {bam.ops.mean(avg_price):.3f}") print(f"Average price (attr): {bam.ops.mean(avg_price_attr):.3f}") # Role data works the same way worker_employed = results["Worker.employed"] print(f"Worker employed shape: {worker_employed.shape}") # Helper function to calculate unemployment rate from Worker employed data def calc_unemployment_rate(worker_employed: np.ndarray) -> np.ndarray: """Calculate unemployment rate per period from Worker employed boolean array. Args: worker_employed: 2D boolean array of shape (n_periods, n_workers) where True indicates employed, False indicates unemployed. Returns: 1D array of unemployment rates per period. """ # Employment rate = mean of employed (True=1, False=0) across workers # Unemployment rate = 1 - employment rate return 1.0 - np.mean(worker_employed.astype(float), axis=1) # Calculate unemployment rate from Worker employed data unemployment = calc_unemployment_rate(worker_employed) print("\nUnemployment rate:") print(f" Mean: {bam.ops.mean(unemployment):.2%}") print(f" Final: {unemployment[-1]:.2%}") print("\nAverage price:") print(f" Mean: {bam.ops.mean(avg_price):.3f}") print(f" Final: {avg_price[-1]:.3f}") .. rst-class:: sphx-glr-script-out .. code-block:: none Average price (bracket): 0.590 Average price (attr): 0.590 Worker employed shape: (50, 500) Unemployment rate: Mean: 0.14% Final: 0.00% Average price: Mean: 0.590 Final: 0.760 .. GENERATED FROM PYTHON SOURCE LINES 117-122 Accessing Role Data ------------------- Role data contains per-period snapshots of agent states. With dict-form collect, data is full per-agent arrays (periods x agents) by default. .. GENERATED FROM PYTHON SOURCE LINES 122-130 .. code-block:: Python # Access Producer price data - shape is (periods, firms) with full per-agent data price_data = results["Producer.price"] print(f"Price data shape: {price_data.shape}") # Calculate mean price per period avg_prices = np.mean(price_data, axis=1) print(f"Price trend (first 5): {avg_prices[:5].round(3)}") .. rst-class:: sphx-glr-script-out .. code-block:: none Price data shape: (50, 100) Price trend (first 5): [0.504 0.504 0.504 0.503 0.502] .. GENERATED FROM PYTHON SOURCE LINES 131-137 Programmatic Access with get() -------------------------------- Use ``results.get()`` for programmatic access, especially when the role or variable name comes from a variable. It also supports on-the-fly aggregation. .. GENERATED FROM PYTHON SOURCE LINES 137-164 .. code-block:: Python # get() for role data prices_via_get = results.get("Producer", "price") print("\nUsing get():") print(f" results.get('Producer', 'price').shape: {prices_via_get.shape}") # get() for economy data - use "Economy" as the name price_via_get = results.get("Economy", "avg_price") print(f" results.get('Economy', 'avg_price').shape: {price_via_get.shape}") # Aggregation on-the-fly (useful when you have full per-agent data) full_data_sim = bam.Simulation.init(n_firms=50, n_households=250, seed=42) full_data_results = full_data_sim.run( n_periods=20, collect={ "Producer": ["price"], # Specific variables for Producer }, ) # Get full 2D data (periods x firms) prices_2d = full_data_results.get("Producer", "price") print(f"\n Full data shape: {prices_2d.shape}") # Get mean aggregated on-the-fly prices_mean = full_data_results.get("Producer", "price", aggregate="mean") print(f" With aggregate='mean': {prices_mean.shape}") .. rst-class:: sphx-glr-script-out .. code-block:: none Using get(): results.get('Producer', 'price').shape: (50, 100) results.get('Economy', 'avg_price').shape: (50,) Full data shape: (20, 50) With aggregate='mean': (20,) .. GENERATED FROM PYTHON SOURCE LINES 165-171 Legacy Access ------------- The underlying data is also available via ``role_data``, ``economy_data``, and ``relationship_data`` dictionaries. The ``data`` property merges them into a single dict with an "Economy" key. .. GENERATED FROM PYTHON SOURCE LINES 171-180 .. code-block:: Python print("\nLegacy dict access:") print(f" results.role_data keys: {list(results.role_data.keys())}") print(f" results.economy_data keys: {list(results.economy_data.keys())}") # The unified data property all_data = results.data print(f"\nresults.data keys: {list(all_data.keys())}") .. rst-class:: sphx-glr-script-out .. code-block:: none Legacy dict access: results.role_data keys: ['Worker', 'Producer', 'Employer', 'Borrower', 'Lender', 'Consumer'] results.economy_data keys: ['avg_price', 'inflation', 'n_firm_bankruptcies', 'n_bank_bankruptcies'] results.data keys: ['Worker', 'Producer', 'Employer', 'Borrower', 'Lender', 'Consumer', 'Economy'] .. GENERATED FROM PYTHON SOURCE LINES 181-185 Visualizing Results ------------------- Plot key economic indicators over time. .. GENERATED FROM PYTHON SOURCE LINES 185-231 .. code-block:: Python import matplotlib.pyplot as plt fig, axes = plt.subplots(2, 2, figsize=(12, 8)) # Unemployment rate ax1 = axes[0, 0] if len(unemployment) > 0: ax1.plot(unemployment * 100, linewidth=2, color="tab:blue") ax1.set_ylabel("Unemployment Rate (%)") ax1.set_title("Unemployment") ax1.grid(True, alpha=0.3) # Average price ax2 = axes[0, 1] if len(avg_price) > 0: ax2.plot(avg_price, linewidth=2, color="tab:green") ax2.set_ylabel("Price Level") ax2.set_title("Average Market Price") ax2.grid(True, alpha=0.3) # Inflation ax3 = axes[1, 0] if len(inflation) > 0: ax3.plot(inflation * 100, linewidth=2, color="tab:orange") ax3.set_ylabel("Inflation Rate (%)") ax3.set_title("Annual Inflation") ax3.axhline(y=0, color="black", linestyle="--", alpha=0.5) ax3.grid(True, alpha=0.3) # Production - compute mean across firms since data is 2D ax4 = axes[1, 1] production_data = results["Producer.production"] # Average across firms (axis=1) since data is (periods, firms) avg_production = np.mean(production_data, axis=1) ax4.plot(avg_production, linewidth=2, color="tab:red") ax4.set_ylabel("Avg Production") ax4.set_title("Average Firm Production") ax4.grid(True, alpha=0.3) for ax in axes.flat: ax.set_xlabel("Period") plt.tight_layout() plt.show() .. image-sg:: /auto_examples/basic/images/sphx_glr_example_results_module_001.png :alt: Unemployment, Average Market Price, Annual Inflation, Average Firm Production :srcset: /auto_examples/basic/images/sphx_glr_example_results_module_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 232-238 Custom Data Collection ---------------------- Use a dictionary to specify exactly what data to collect. Keys are role names (or "Economy"), values are ``True`` for all variables or a list of specific variable names. .. GENERATED FROM PYTHON SOURCE LINES 238-252 .. code-block:: Python # Collect only specific roles (economy metrics are always included automatically) custom_results = bam.Simulation.init(n_firms=100, n_households=500, seed=42).run( n_periods=30, collect={ "Producer": True, # All Producer variables "Worker": True, # All Worker variables "aggregate": "mean", # Average across agents }, ) print("Custom collection:") print(f" Roles collected: {list(custom_results.role_data.keys())}") .. rst-class:: sphx-glr-script-out .. code-block:: none Custom collection: Roles collected: ['Producer', 'Worker'] .. GENERATED FROM PYTHON SOURCE LINES 253-257 Full Agent-Level Data --------------------- Dict-form ``collect`` returns full per-agent data by default (larger arrays). .. GENERATED FROM PYTHON SOURCE LINES 257-274 .. code-block:: Python # Warning: This collects full arrays - can be memory intensive! full_results = bam.Simulation.init(n_firms=50, n_households=250, seed=42).run( n_periods=20, collect={ "Producer": ["price", "production"], # Specific variables only }, ) prices_full = full_results["Producer.price"] print(f"Full price data shape: {prices_full.shape}") print(f" (periods x firms): ({prices_full.shape[0]} x {prices_full.shape[1]})") # Access individual firm's price history firm_0_prices = prices_full[:, 0] print(f"Firm 0 price history: {firm_0_prices[:5].round(3)}...") .. rst-class:: sphx-glr-script-out .. code-block:: none Full price data shape: (20, 50) (periods x firms): (20 x 50) Firm 0 price history: [0.5 0.5 0.5 0.5 0.5]... .. GENERATED FROM PYTHON SOURCE LINES 275-280 Export to pandas DataFrame -------------------------- Convert results to pandas DataFrames for further analysis. Note: pandas is an optional dependency. .. GENERATED FROM PYTHON SOURCE LINES 280-308 .. code-block:: Python try: import pandas # Run a new simulation for DataFrame export df_results = bam.Simulation.init(n_firms=100, n_households=500, seed=42).run( n_periods=50, collect=True, ) # Get all data as a single DataFrame (aggregated) df = df_results.to_dataframe(aggregate="mean") print("Full DataFrame:") print(f" Shape: {df.shape}") print(f" Columns: {list(df.columns)[:5]}...") # Get economy metrics only df_economy = df_results.economy_metrics print("\nEconomy metrics DataFrame:") print(df_economy.head()) # Get specific role data df_producer = df_results.get_role_data("Producer", aggregate="mean") print(f"\nProducer DataFrame columns: {list(df_producer.columns)}") except ImportError: print("pandas not installed. Install with: pip install pandas") .. rst-class:: sphx-glr-script-out .. code-block:: none Full DataFrame: Shape: (50, 52) Columns: ['Producer.production.mean', 'Producer.production_prev.mean', 'Producer.inventory.mean', 'Producer.expected_demand.mean', 'Producer.desired_production.mean']... Economy metrics DataFrame: avg_price inflation n_firm_bankruptcies n_bank_bankruptcies period 0 0.500000 0.000000 4 0 1 0.500000 0.000000 4 0 2 0.500000 0.000000 4 0 3 0.499194 0.000000 4 0 4 0.498755 -0.001612 4 0 Producer DataFrame columns: ['Producer.production.mean', 'Producer.production_prev.mean', 'Producer.inventory.mean', 'Producer.expected_demand.mean', 'Producer.desired_production.mean', 'Producer.labor_productivity.mean', 'Producer.breakeven_price.mean', 'Producer.price.mean', 'Producer.prod_shock.mean', 'Producer.prod_mask_up.mean', 'Producer.prod_mask_dn.mean', 'Producer.price_shock.mean'] .. GENERATED FROM PYTHON SOURCE LINES 309-313 Summary Statistics ------------------ Get descriptive statistics for all collected metrics. .. GENERATED FROM PYTHON SOURCE LINES 313-330 .. code-block:: Python try: import pandas # noqa: F401 - check if pandas is installed summary_results = bam.Simulation.init(n_firms=100, n_households=500, seed=42).run( n_periods=100, collect=True, ) # Get summary statistics summary = summary_results.summary print("Summary Statistics:") print(summary[["mean", "std", "min", "max"]].round(4)) except ImportError: print("pandas not installed for summary statistics") .. rst-class:: sphx-glr-script-out .. code-block:: none Summary Statistics: mean std min max Producer.production.mean 2.4818 0.1147 2.1976 2.5947 Producer.production_prev.mean 2.4818 0.1147 2.1976 2.5947 Producer.inventory.mean 0.5006 0.2330 0.0000 0.9006 Producer.expected_demand.mean 2.4236 0.1266 2.1378 2.6229 Producer.desired_production.mean 2.4236 0.1266 2.1378 2.6229 Producer.labor_productivity.mean 0.5000 0.0000 0.5000 0.5000 Producer.breakeven_price.mean 0.4886 0.0952 0.0000 0.6285 Producer.price.mean 0.7289 0.1530 0.5024 0.9703 Producer.prod_shock.mean 0.0501 0.0028 0.0433 0.0552 Producer.prod_mask_up.mean 0.3027 0.2167 0.0100 1.0000 Producer.prod_mask_dn.mean 0.2516 0.1156 0.0000 0.5200 Producer.price_shock.mean 0.0501 0.0028 0.0443 0.0575 Worker.employer.mean 42.3932 13.8191 1.6520 49.8120 Worker.employer_prev.mean 44.4916 12.6545 -1.0000 49.5920 Worker.wage.mean 0.2266 0.0816 0.0099 0.3372 Worker.periods_left.mean 3.5008 1.9385 0.0560 6.6080 Worker.contract_expired.mean 0.1148 0.2694 0.0000 0.9440 Worker.fired.mean 0.0025 0.0069 0.0000 0.0520 Worker.job_apps_head.mean -1.0000 0.0000 -1.0000 -1.0000 Employer.desired_labor.mean 5.1148 0.3582 4.4200 5.9600 Employer.current_labor.mean 4.2899 1.3551 0.2800 5.0000 Employer.wage_offer.mean 0.2590 0.0413 0.1674 0.3300 Employer.wage_bill.mean 1.2800 0.2275 0.8088 1.7387 Employer.n_vacancies.mean 0.2467 0.2135 0.0000 1.0400 Employer.total_funds.mean 12.1788 1.8487 7.6912 15.0621 Employer.recv_job_apps_head.mean -1.0000 0.0000 -1.0000 -1.0000 Employer.wage_shock.mean 0.0106 0.0064 0.0019 0.0271 Borrower.net_worth.mean 12.1788 1.8487 7.6912 15.0621 Borrower.total_funds.mean 12.1788 1.8487 7.6912 15.0621 Borrower.wage_bill.mean 1.2800 0.2275 0.8088 1.7387 Borrower.credit_demand.mean 0.0029 0.0093 0.0000 0.0515 Borrower.projected_fragility.mean 0.0263 0.0708 0.0000 0.4539 Borrower.gross_profit.mean 0.0725 0.1127 -0.0885 0.3916 Borrower.net_profit.mean 0.0723 0.1126 -0.0885 0.3916 Borrower.retained_profit.mean 0.0471 0.1065 -0.0972 0.3524 Borrower.loan_apps_head.mean -0.3932 1.1959 -1.0000 3.8200 Lender.equity_base.mean 4.1240 1.0118 2.3605 5.0000 Lender.credit_supply.mean 41.2986 10.1039 23.3304 50.0000 Lender.interest_rate.mean 0.0209 0.0004 0.0186 0.0214 Lender.recv_loan_apps_head.mean -1.0000 0.0000 -1.0000 -1.0000 Lender.opex_shock.mean 0.0488 0.0089 0.0293 0.0708 Consumer.income.mean 0.0000 0.0000 0.0000 0.0000 Consumer.savings.mean 0.2532 0.1590 0.0991 0.9332 Consumer.income_to_spend.mean 0.0000 0.0000 0.0000 0.0000 Consumer.propensity.mean 0.7781 0.0292 0.6639 0.8269 Consumer.largest_prod_prev.mean 47.7601 2.6988 41.5620 53.3240 Consumer.shop_visits_head.mean 499.0000 0.0000 499.0000 499.0000 Shareholder.dividends.mean 0.0050 0.0018 0.0017 0.0088 avg_price 0.7224 0.1517 0.4988 0.9646 inflation 0.0166 0.0420 -0.0519 0.0905 n_firm_bankruptcies 3.8900 2.2870 0.0000 13.0000 n_bank_bankruptcies 0.0200 0.1407 0.0000 1.0000 .. GENERATED FROM PYTHON SOURCE LINES 331-335 Comparing Multiple Simulation Runs ---------------------------------- Run multiple simulations and compare their results. .. GENERATED FROM PYTHON SOURCE LINES 335-381 .. code-block:: Python # Run with different random seeds n_runs = 5 all_unemployment = [] print("Running ensemble of simulations...") for i in range(n_runs): run_results = bam.Simulation.init(n_firms=100, n_households=500, seed=42 + i).run( n_periods=50, collect={ "Worker": ["employed"], "capture_timing": {"Worker.employed": "firms_run_production"}, }, ) # Calculate unemployment from Worker employed data unemp_rate = calc_unemployment_rate(run_results["Worker.employed"]) all_unemployment.append(unemp_rate) # Plot ensemble results if all_unemployment: fig, ax = plt.subplots(figsize=(10, 6)) for i, unemp in enumerate(all_unemployment): ax.plot(bam.ops.multiply(unemp, 100), alpha=0.5, label=f"Run {i + 1}") # Calculate and plot mean unemployment across runs # Stack arrays and compute mean along axis 0 stacked = bam.ops.asarray([list(u) for u in all_unemployment]) mean_unemployment = bam.ops.multiply(bam.ops.mean(stacked, axis=0), 100) ax.plot(mean_unemployment, "k-", linewidth=2, label="Mean") ax.set_xlabel("Period") ax.set_ylabel("Unemployment Rate (%)") ax.set_title(f"Ensemble of {n_runs} Simulation Runs") ax.legend(loc="upper right") ax.grid(True, alpha=0.3) plt.tight_layout() plt.show() # Summary across runs final_rates = bam.ops.asarray([unemp[-1] * 100 for unemp in all_unemployment]) print("\nFinal unemployment rates:") print(f" Mean: {bam.ops.mean(final_rates):.2f}%") print(f" Std: {bam.ops.std(final_rates):.2f}%") print(f" Range: {bam.ops.min(final_rates):.2f}% - {bam.ops.max(final_rates):.2f}%") .. image-sg:: /auto_examples/basic/images/sphx_glr_example_results_module_002.png :alt: Ensemble of 5 Simulation Runs :srcset: /auto_examples/basic/images/sphx_glr_example_results_module_002.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none Running ensemble of simulations... Final unemployment rates: Mean: 0.60% Std: 0.91% Range: 0.00% - 2.40% .. GENERATED FROM PYTHON SOURCE LINES 382-387 Collecting Relationship Data ---------------------------- Relationships (like ``LoanBook``) can be collected alongside role data. Unlike roles, relationships are **opt-in only** and NOT included with ``collect=True``. .. GENERATED FROM PYTHON SOURCE LINES 387-419 .. code-block:: Python # Manually add some loans to demonstrate relationship data collection rel_sim = bam.Simulation.init(n_firms=50, n_households=250, seed=42) loans = rel_sim.get_relationship("LoanBook") loans.append_loans_for_lender( lender_idx=np.intp(0), borrower_indices=np.array([0, 1, 2, 3, 4], dtype=np.int64), amount=np.array([1000.0, 1500.0, 2000.0, 500.0, 750.0]), rate=np.array([0.02, 0.03, 0.025, 0.018, 0.022]), ) rel_results = rel_sim.run( n_periods=20, collect={ "Producer": ["price"], # Role data "LoanBook": ["principal", "rate", "debt"], # Relationship data "aggregate": "sum", # Sum across all active loans }, ) print("Relationship data collection:") print(f" Available: {rel_results.available()}") # Access via bracket notation total_principal = rel_results["LoanBook.principal"] print(f" Total principal over time shape: {total_principal.shape}") print(f" Initial total principal: {total_principal[0]:.2f}") # Access via get() total_debt = rel_results.get("LoanBook", "debt") print(f" Total debt (last period): {total_debt[-1]:.2f}") .. rst-class:: sphx-glr-script-out .. code-block:: none Relationship data collection: Available: ['Economy.avg_price', 'Economy.inflation', 'Economy.n_bank_bankruptcies', 'Economy.n_firm_bankruptcies', 'LoanBook.debt', 'LoanBook.principal', 'LoanBook.rate', 'Producer.price'] Total principal over time shape: (20,) Initial total principal: 0.00 Total debt (last period): 0.00 .. GENERATED FROM PYTHON SOURCE LINES 420-426 Analyzing Loan Distribution --------------------------- Without aggregation (the default for dict-form collect), you get full edge data per period as variable-length arrays. Useful for analyzing distributions but cannot be exported to DataFrame. .. GENERATED FROM PYTHON SOURCE LINES 426-452 .. code-block:: Python loan_dist_sim = bam.Simulation.init(n_firms=50, n_households=250, seed=42) loans = loan_dist_sim.get_relationship("LoanBook") # Add loans with varying amounts loans.append_loans_for_lender( lender_idx=np.intp(0), borrower_indices=np.array([0, 1, 2], dtype=np.int64), amount=np.array([100.0, 200.0, 300.0]), rate=np.array([0.02, 0.03, 0.025]), ) dist_results = loan_dist_sim.run( n_periods=5, collect={ "LoanBook": ["principal"], # Full edge data (variable-length per period) }, ) principal_per_period = dist_results["LoanBook.principal"] print("\nLoan distribution (full per-edge data):") print(f" Type: {type(principal_per_period).__name__}") print(f" Number of periods: {len(principal_per_period)}") if principal_per_period: print(f" Period 0 loans: {len(principal_per_period[0])} active") print(f" Period 0 principals: {principal_per_period[0]}") .. rst-class:: sphx-glr-script-out .. code-block:: none Loan distribution (full per-edge data): Type: list Number of periods: 5 Period 0 loans: 0 active Period 0 principals: [] .. GENERATED FROM PYTHON SOURCE LINES 453-497 Key Takeaways ------------- **Basic collection:** - Use ``collect=True`` (the default) for full per-agent data collection - Economy metrics are always collected automatically - Use ``collect={"Producer": ["price"]}`` for specific variables - Use ``True`` for all variables: ``{"Worker": True}`` **Data access (primary API):** - ``results["Producer.price"]`` -- bracket notation (recommended) - ``results.Producer.price`` -- attribute access (interactive use) - ``results.get("Producer", "price")`` -- programmatic access - ``results.get("Producer", "price", aggregate="mean")`` -- with aggregation - ``results.available()`` -- discover all collected variables **Per-agent data and capture timing:** - Dict-form ``collect`` returns full per-agent data by default (shape: periods x agents) - Use ``capture_timing`` to control when variables are captured during each period - Example: ``"capture_timing": {"Worker.employed": "firms_run_production"}`` **Computing derived metrics:** - Unemployment rate can be computed from ``Worker.employed``: ``1.0 - np.mean(employed.astype(float), axis=1)`` - When computing from per-agent data, aggregate across agents with ``axis=1`` **Relationship data:** - Relationships (like ``LoanBook``) are opt-in: use ``"LoanBook": ["principal"]`` - NOT included with ``collect=True`` (must specify explicitly) - Aggregations: ``sum`` (total), ``mean`` (average), ``std`` (variation) - Without aggregation (default): list of variable-length arrays (can't export to DataFrame) - Access via ``results["LoanBook.principal"]`` or ``results.get("LoanBook", "principal")`` **Legacy access (still works):** - ``results.economy_data``, ``results.role_data``, ``results.relationship_data`` - ``results.data`` for unified dict access (includes "Economy" key and relationships) - Export to pandas with ``to_dataframe()`` for further analysis - Get quick statistics with ``results.summary`` .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 3.356 seconds) .. _sphx_glr_download_auto_examples_basic_example_results_module.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: example_results_module.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: example_results_module.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: example_results_module.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_