### Install humanleague with uv for development Source: https://context7.com/virgesmith/humanleague/llms.txt Installation and testing commands for developers using uv. ```bash uv sync --dev uv run pytest ``` -------------------------------- ### Install humanleague with pip Source: https://github.com/virgesmith/humanleague/blob/main/README.md Install the humanleague package using pip. Requires Python 3.12 or newer. ```bash pip install humanleague ``` -------------------------------- ### Install humanleague development version with uv Source: https://github.com/virgesmith/humanleague/blob/main/README.md Install dependencies and build the humanleague package in a development environment using uv. ```bash uv sync --dev uv build uv run pytest ``` -------------------------------- ### Install humanleague R package Source: https://context7.com/virgesmith/humanleague/llms.txt Installation instructions for the humanleague package in R, including the development version. ```r install.packages("humanleague") # or development version: devtools::install_github("virgesmith/humanleague") ``` -------------------------------- ### Install humanleague R package Source: https://github.com/virgesmith/humanleague/blob/main/README.md Install the official release of the humanleague R package from CRAN. ```r install.packages("humanleague") ``` -------------------------------- ### Install humanleague R development version Source: https://github.com/virgesmith/humanleague/blob/main/README.md Install the development version of the humanleague R package from GitHub. ```r devtools::install_github("virgesmith/humanleague") ``` -------------------------------- ### Generate population with Quasirandom Integer Sampling (QIS) Source: https://context7.com/virgesmith/humanleague/llms.txt Example of using the QIS algorithm to create a 2D integer population array matching marginal sums. Ensures the generated population adheres to specified marginals and has a high p-value indicating a good fit. An alternative population can be generated by skipping values in the Sobol sequence. ```python import numpy as np import humanleague as hl # 2D: age band × employment status (100 people total) m_age = np.array([52, 48]) # 2 age bands m_status = np.array([10, 77, 13]) # 3 employment states pop, stats = hl.qis([0, 1], [m_age, m_status]) assert stats["conv"] assert stats["pop"] == 100.0 assert stats["pValue"] > 0.9 # high p-value → good fit assert np.allclose(pop.sum(axis=0), m_status) assert np.allclose(pop.sum(axis=1), m_age) print(pop) # e.g. array([[ 5, 40, 7], # [ 5, 37, 6]]) # 4D with scalar dimension indices m0 = np.array([52, 48]) m1 = np.array([87, 13]) m2 = np.array([67, 33]) m3 = np.array([55, 45]) pop4d, stats4d = hl.qis([[0], [1], [2], [3]], [m0, m1, m2, m3]) assert stats4d["conv"] assert stats4d["pop"] == 100 # Generate an alternative population by skipping ahead in the Sobol sequence pop_alt, _ = hl.qis([0, 1], [m_age, m_status], skips=100) ``` -------------------------------- ### Install humanleague R legacy version Source: https://github.com/virgesmith/humanleague/blob/main/README.md Install a specific legacy version (1.0.1) of the humanleague R package from GitHub. ```r devtools::install_github("virgesmith/humanleague@1.0.1") ``` -------------------------------- ### Generate population with Iterative Proportional Fitting (IPF) Source: https://context7.com/virgesmith/humanleague/llms.txt Demonstrates using the IPF algorithm with a seed population to match marginal sums. Supports both integer counts and probability distributions as seeds, and various marginal configurations. The example verifies convergence, iteration count, total population, and marginal sums. It also shows IPF with a non-uniform seed and with fractional marginals. ```python import numpy as np import humanleague as hl # 2D IPF: sex × economic activity m_sex = np.array([52.0, 48.0]) m_econ = np.array([87.0, 13.0]) seed = np.ones([2, 2]) pop, stats = hl.ipf(seed, [[0], [1]], [m_sex, m_econ]) assert stats["conv"] assert stats["iterations"] == 1 assert stats["pop"] == 100.0 assert np.allclose(pop.sum(axis=1), m_sex) assert np.allclose(pop.sum(axis=0), m_econ) print(pop) # array([[45.24, 6.76], # [41.76, 6.24]]) # 3D IPF with non-uniform seed m0 = np.array([52.0, 48.0]) m1 = np.array([87.0, 13.0]) m2 = np.array([55.0, 45.0]) seed3 = np.array([[[1.0, 1.0], [1.0, 1.0]], [[1.0, 1.0], [1.0, 1.0]]]) pop3, stats3 = hl.ipf(seed3, [[0], [1], [2]], [m0, m1, m2]) assert stats3["conv"] assert np.allclose(pop3.sum(axis=(1, 2)), m0) assert np.allclose(pop3.sum(axis=(2, 0)), m1) assert np.allclose(pop3.sum(axis=(0, 1)), m2) # IPF with fractional (probability) marginals seed_prob = np.full((2, 2, 2), 1 / 8) indices = [(0, 1), (0, 2), (1, 2)] marginals = [ np.array([[0.92625, 0.02375], [0.02375, 0.02625]]), np.array([[0.92625, 0.02375], [0.02375, 0.02625]]), np.array([[0.92625, 0.02375], [0.02375, 0.02625]]), ] pop_prob, stats_prob = hl.ipf(seed_prob, indices, marginals) assert pop_prob.sum() == 1.0 assert stats_prob["conv"] ``` -------------------------------- ### Load humanleague R package and view documentation Source: https://github.com/virgesmith/humanleague/blob/main/README.md Load the humanleague R package into the current session and access its documentation. ```r library(humanleague) ?humanleague ``` -------------------------------- ### One-Dimensional Integerisation with Humanleague Source: https://github.com/virgesmith/humanleague/blob/main/README.md Demonstrates how to use the `integerise` function for a one-dimensional discrete probability distribution. It takes a list of probabilities and a total count, returning an array of integers that sum to the count and closely match the distribution. ```python >>> import humanleague >>> p = [0.1, 0.2, 0.3, 0.4] >>> result, stats = humanleague.integerise(p, 11) >>> result array([1, 2, 3, 5], dtype=int32) >>> stats {'rmse': 0.3535533905932736} ``` -------------------------------- ### Fractional-to-Integer Population Conversion (integerise) Source: https://context7.com/virgesmith/humanleague/llms.txt Converts fractional distributions to their closest integer representation, preserving marginal sums for nD arrays. Internally uses the QISI algorithm for nD conversions. Returns the integer array and a statistics dictionary. ```python import numpy as np import humanleague as hl # 1D: snap probabilities to integer counts summing to N p = [0.1, 0.2, 0.3, 0.4] result, stats = hl.integerise(p, 11) print(result) # array([1, 2, 3, 5], dtype=int32) print(stats) # {'rmse': 0.3535...} ``` ```python # Exact case — zero error r_exact, s_exact = hl.integerise(np.array([0.4, 0.3, 0.2, 0.1]), 10) assert s_exact["rmse"] < 1e-15 assert np.array_equal(r_exact, [4, 3, 2, 1]) ``` ```python # 1D without specifying total (inferred from sum) a = np.array([1.1, 2.9, 1.0]) result_auto, stats_auto = hl.integerise(a) assert stats_auto["conv"] ``` ```python # nD: integerise a fractional matrix while preserving marginal sums a2d = np.array([[ 0.3, 1.2, 2.0, 1.5], [ 0.6, 2.4, 4.0, 3.0], [ 1.5, 6.0, 10.0, 7.5], [ 0.6, 2.4, 4.0, 3.0]]) result2d, stats2d = hl.integerise(a2d) assert stats2d["conv"] assert (result2d.sum(axis=0) == a2d.sum(axis=0)).all() assert (result2d.sum(axis=1) == a2d.sum(axis=1)).all() print(result2d) # array([[ 0, 2, 2, 1], # [ 0, 3, 4, 3], # [ 2, 6, 10, 7], # [ 1, 1, 4, 4]]) ``` ```python # Use IPF output as valid fractional input for nD integerise m0 = np.array([111., 112., 113., 114., 110.]) m1 = np.array([136., 142., 143., 139.]) seed = np.ones([5, 4, 5]) fpop, _ = hl.ipf(seed, [[0], [1], [2]], [m0, m1, m0]) int_pop, int_stats = hl.integerise(fpop) assert int_stats["conv"] assert int_stats["rmse"] < 1.06 ``` -------------------------------- ### Quasirandom Integer Sampling with IPF (qisi) Source: https://context7.com/virgesmith/humanleague/llms.txt Combines IPF and quasirandom sampling to produce high-entropy integer outputs from a fractional population. Accepts an optional 'skips' parameter for generating multiple independent realisations. ```python import numpy as np import humanleague as hl m0 = np.array([52, 48]) m1 = np.array([10, 77, 13]) seed = np.ones([len(m0), len(m1)]) pop, stats = hl.qisi(seed, [0, 1], [m0, m1]) assert stats["conv"] assert stats["pop"] == 100.0 assert stats["pValue"] > 0.9 assert np.allclose(pop.sum(axis=0), m1) assert np.allclose(pop.sum(axis=1), m0) ``` ```python # 3D QISI m0 = np.array([52, 40, 4, 4]) m1 = np.array([87, 10, 3]) m2 = np.array([55, 15, 6, 12, 12]) seed3 = np.ones((len(m0), len(m1), len(m2))) pop3, stats3 = hl.qisi(seed3, [[0], [1], [2]], [m0, m1, m2]) assert stats3["conv"] assert stats3["pop"] == 100.0 assert np.allclose(pop3.sum(axis=(0, 1)), m2) ``` ```python # Multiple realisations for sensitivity analysis realisation_a, _ = hl.qisi(seed, [0, 1], [m0[:2], m1], skips=0) realisation_b, _ = hl.qisi(seed, [0, 1], [m0[:2], m1], skips=100) ``` -------------------------------- ### Multidimensional Integerisation with Humanleague Source: https://github.com/virgesmith/humanleague/blob/main/README.md Illustrates the multidimensional `integerise` function. This function takes an n-dimensional array of real numbers and finds an integral array that satisfies the same marginal sums in every dimension. The QISI algorithm is used, which is a sampling algorithm and does not guarantee optimality or even finding a solution. ```python >>> import numpy as np >>> a = np.array([[ 0.3, 1.2, 2. , 1.5], [ 0.6, 2.4, 4. , 3. ], [ 1.5, 6. , 10. , 7.5], [ 0.6, 2.4, 4. , 3. ]]) # marginal sums >>> a.sum(axis=0) array([ 3., 12., 20., 15.]) >>> a.sum(axis=1) array([ 5., 10., 25., 10.]) # perform integerisation >>> result, stats = humanleague.integerise(a) >>> stats {'conv': True, 'rmse': 0.5766281297335398} >>> result array([[ 0, 2, 2, 1], [ 0, 3, 4, 3], [ 2, 6, 10, 7], [ 1, 1, 4, 4]]) # check marginals are preserved >>> (result.sum(axis=0) == a.sum(axis=0)).all() True >>> (result.sum(axis=1) == a.sum(axis=1)).all() True ``` -------------------------------- ### Sobol Sequence Generator (SobolSequence) Source: https://context7.com/virgesmith/humanleague/llms.txt Generates vectors uniformly distributed in (0,1)^dim using the Sobol quasirandom sequence. Supports dimensions 1-1111 and allows fast-forwarding the sequence with the 'skips' argument for independent stream generation. ```python import humanleague as hl import numpy as np # Basic 3D sequence s = hl.SobolSequence(3) v0 = next(s) print(v0) # array([0.5 , 0.5 , 0.5 ]) print(next(s)) # array([0.75, 0.25, 0.75]) print(next(s)) # array([0.25, 0.75, 0.25]) ``` ```python # Consume many values efficiently N = 1000 samples = np.array([next(s) for _ in range(N)]) # shape (1000, 3) assert samples.shape == (N, 3) assert samples.min() > 0.0 assert samples.max() < 1.0 ``` ```python # Skip ahead (actual skip = largest power-of-2 ≤ requested) s_skipped = hl.SobolSequence(2, 128) # skips 128 values s_fresh = hl.SobolSequence(2) for _ in range(128): next(s_fresh) assert np.array_equal(next(s_skipped), next(s_fresh)) ``` ```python # Use as a standard Python iterator dim = 5 seq = hl.SobolSequence(dim) for i, vec in enumerate(seq): if i >= 8: break assert vec.shape == (dim,) ``` -------------------------------- ### qisi — Quasirandom Integer Sampling of IPF Source: https://context7.com/virgesmith/humanleague/llms.txt Combines IPF and quasirandom sampling to build a fractional population using IPF from a seed, then samples an integer population from it using the Sobol sequence. Accepts an optional `skips` parameter for generating multiple independent realisations. ```APIDOC ## `qisi` — Quasirandom Integer Sampling of IPF Combines IPF and quasirandom sampling: builds a fractional population using IPF from the seed, then samples an integer population from it using the Sobol sequence. This hybrid approach preserves the seed's structure while producing high-entropy integer outputs. Like `qis`, it accepts an optional `skips` parameter for generating multiple independent realisations. ```python import numpy as np import humanleague as hl m0 = np.array([52, 48]) m1 = np.array([10, 77, 13]) seed = np.ones([len(m0), len(m1)]) pop, stats = hl.qisi(seed, [0, 1], [m0, m1]) assert stats["conv"] assert stats["pop"] == 100.0 assert stats["pValue"] > 0.9 assert np.allclose(pop.sum(axis=0), m1) assert np.allclose(pop.sum(axis=1), m0) # 3D QISI m0 = np.array([52, 40, 4, 4]) m1 = np.array([87, 10, 3]) m2 = np.array([55, 15, 6, 12, 12]) seed3 = np.ones((len(m0), len(m1), len(m2))) pop3, stats3 = hl.qisi(seed3, [[0], [1], [2]], [m0, m1, m2]) assert stats3["conv"] assert stats3["pop"] == 100.0 assert np.allclose(pop3.sum(axis=(0, 1)), m2) # Multiple realisations for sensitivity analysis realisation_a, _ = hl.qisi(seed, [0, 1], [m0[:2], m1], skips=0) realisation_b, _ = hl.qisi(seed, [0, 1], [m0[:2], m1], skips=100) ``` ``` -------------------------------- ### qis — Quasirandom Integer Sampling Source: https://context7.com/virgesmith/humanleague/llms.txt Constructs an n-dimensional integer population array that exactly matches the specified marginal sums using quasirandom (Sobol) sampling. No seed population is required. Returns a tuple of (population_array, stats_dict). ```APIDOC ## qis — Quasirandom Integer Sampling ### Description Constructs an n-dimensional integer population array that exactly matches the specified marginal sums using quasirandom (Sobol) sampling. No seed population is required. Returns a tuple of `(population_array, stats_dict)` where stats include `conv`, `chiSq`, `pValue`, and `pop`. An optional `skips` parameter controls how many Sobol values are skipped at the start of the sequence (rounded down to the nearest power of 2), enabling generation of multiple distinct populations for sensitivity analysis. ### Parameters - `dimensions` (list of lists): Specifies the dimensions for the marginals. - `marginals` (list of numpy.ndarray): A list of marginal sum arrays. - `skips` (int, optional): Number of Sobol values to skip at the start of the sequence. ### Returns - `population_array` (numpy.ndarray): The generated integer population array. - `stats_dict` (dict): A dictionary containing statistical diagnostics such as `conv`, `chiSq`, `pValue`, and `pop`. ### Request Example ```python import numpy as np import humanleague as hl # 2D: age band × employment status (100 people total) m_age = np.array([52, 48]) # 2 age bands m_status = np.array([10, 77, 13]) # 3 employment states pop, stats = hl.qis([0, 1], [m_age, m_status]) assert stats["conv"] assert stats["pop"] == 100.0 assert stats["pValue"] > 0.9 # high p-value → good fit assert np.allclose(pop.sum(axis=0), m_status) assert np.allclose(pop.sum(axis=1), m_age) print(pop) # 4D with scalar dimension indices m0 = np.array([52, 48]) m1 = np.array([87, 13]) m2 = np.array([67, 33]) m3 = np.array([55, 45]) pop4d, stats4d = hl.qis([[0], [1], [2], [3]], [m0, m1, m2, m3]) assert stats4d["conv"] assert stats4d["pop"] == 100 # Generate an alternative population by skipping ahead in the Sobol sequence pop_alt, _ = hl.qis([0, 1], [m_age, m_status], skips=100) ``` ``` -------------------------------- ### integerise — Fractional-to-Integer Population Conversion Source: https://context7.com/virgesmith/humanleague/llms.txt Converts a fractional distribution to the closest integer representation. In 1D it takes a probability/count vector and a total population and returns the best integer approximation. In nD it takes a fractional array whose 1D marginal sums are already integers and finds an integer array with identical marginals (using the QISI algorithm internally). Both forms return `(integer_array, stats_dict)`. ```APIDOC ## `integerise` — Fractional-to-Integer Population Conversion Converts a fractional distribution to the closest integer representation. In 1D it takes a probability/count vector and a total population and returns the best integer approximation. In nD it takes a fractional array whose 1D marginal sums are already integers and finds an integer array with identical marginals (using the QISI algorithm internally). Both forms return `(integer_array, stats_dict)`. ```python import numpy as np import humanleague as hl # 1D: snap probabilities to integer counts summing to N p = [0.1, 0.2, 0.3, 0.4] result, stats = hl.integerise(p, 11) print(result) # array([1, 2, 3, 5], dtype=int32) print(stats) # {'rmse': 0.3535...} # Exact case — zero error r_exact, s_exact = hl.integerise(np.array([0.4, 0.3, 0.2, 0.1]), 10) assert s_exact["rmse"] < 1e-15 assert np.array_equal(r_exact, [4, 3, 2, 1]) # 1D without specifying total (inferred from sum) a = np.array([1.1, 2.9, 1.0]) result_auto, stats_auto = hl.integerise(a) assert stats_auto["conv"] # nD: integerise a fractional matrix while preserving marginal sums a2d = np.array([[ 0.3, 1.2, 2.0, 1.5], [ 0.6, 2.4, 4.0, 3.0], [ 1.5, 6.0, 10.0, 7.5], [ 0.6, 2.4, 4.0, 3.0]]) result2d, stats2d = hl.integerise(a2d) assert stats2d["conv"] assert (result2d.sum(axis=0) == a2d.sum(axis=0)).all() assert (result2d.sum(axis=1) == a2d.sum(axis=1)).all() print(result2d) # array([[ 0, 2, 2, 1], # [ 0, 3, 4, 3], # [ 2, 6, 10, 7], # [ 1, 1, 4, 4]]) # Use IPF output as valid fractional input for nD integerise m0 = np.array([111., 112., 113., 114., 110.]) m1 = np.array([136., 142., 143., 139.]) seed = np.ones([5, 4, 5]) fpop, _ = hl.ipf(seed, [[0], [1], [2]], [m0, m1, m0]) int_pop, int_stats = hl.integerise(fpop) assert int_stats["conv"] assert int_stats["rmse"] < 1.06 ``` ``` -------------------------------- ### SobolSequence — Low-Discrepancy Sequence Generator Source: https://context7.com/virgesmith/humanleague/llms.txt A Python generator class that yields vectors uniformly distributed in `(0,1)^dim` using the Sobol quasirandom sequence. Supports dimensions 1–1111. The `skips` constructor argument fast-forwards the sequence to the nearest power-of-2 position, enabling parallel/independent stream generation. ```APIDOC ## `SobolSequence` — Low-Discrepancy Sequence Generator A Python generator class that yields vectors uniformly distributed in `(0,1)^dim` using the Sobol quasirandom sequence. Supports dimensions 1–1111. The `skips` constructor argument fast-forwards the sequence to the nearest power-of-2 position, enabling parallel/independent stream generation. ```python import humanleague as hl import numpy as np # Basic 3D sequence s = hl.SobolSequence(3) v0 = next(s) print(v0) # array([0.5 , 0.5 , 0.5 ]) print(next(s)) # array([0.75, 0.25, 0.75]) print(next(s)) # array([0.25, 0.75, 0.25]) # Consume many values efficiently N = 1000 samples = np.array([next(s) for _ in range(N)]) # shape (1000, 3) assert samples.shape == (N, 3) assert samples.min() > 0.0 assert samples.max() < 1.0 # Skip ahead (actual skip = largest power-of-2 ≤ requested) s_skipped = hl.SobolSequence(2, 128) # skips 128 values s_fresh = hl.SobolSequence(2) for _ in range(128): next(s_fresh) assert np.array_equal(next(s_skipped), next(s_fresh)) # Use as a standard Python iterator dim = 5 seq = hl.SobolSequence(dim) for i, vec in enumerate(seq): if i >= 8: break assert vec.shape == (dim,) ``` ``` -------------------------------- ### Tabulate Individuals to DataFrame Source: https://context7.com/virgesmith/humanleague/llms.txt Use `tabulate_individuals` to expand a population count array into a pandas DataFrame where each row represents an individual. This is useful for downstream analysis requiring individual-level data. Ensure the population array is generated first. ```python import numpy as np import humanleague as hl # Simple 2D example population = np.array([[0, 2], [3, 5]]) df = hl.tabulate_individuals(population, names=["Row", "Column"]) print(df) # Row Column # 0 0 1 # 1 0 1 # 2 1 0 # 3 1 0 # 4 1 0 # 5 1 1 # ... (10 rows total) assert len(df) == population.sum() assert len(df[(df.Row == 0) & (df.Column == 1)]) == 2 assert len(df[(df.Row == 1) & (df.Column == 0)]) == 3 # Real workflow: QIS → tabulate individuals m_age = np.array([52, 48]) m_nssec = np.array([10, 77, 13]) pop, stats = hl.qis([0, 1], [m_age, m_nssec]) assert stats["conv"] individuals = hl.tabulate_individuals(pop, names=["age_band", "nssec"]) print(individuals.head()) # age_band nssec # 0 0 0 # 1 0 1 # ... assert len(individuals) == 100 # 3D population pop3 = np.array([[[1, 0], [0, 2]], [[0, 1], [1, 0]]]) df3 = hl.tabulate_individuals(pop3, names=["Dim1", "Dim2", "Dim3"]) assert len(df3) == 5 assert list(df3.columns) == ["Dim1", "Dim2", "Dim3"] ``` -------------------------------- ### ipf — Iterative Proportional Fitting Source: https://context7.com/virgesmith/humanleague/llms.txt Constructs an n-dimensional array from a seed population that satisfies the given marginal sums using IPF. The seed can be integer counts or a probability distribution. Marginals can be 1-D vectors or higher-dimensional arrays. Returns a tuple of (fractional_population, stats_dict). ```APIDOC ## ipf — Iterative Proportional Fitting ### Description Constructs an n-dimensional array from a seed population that satisfies the given marginal sums using IPF. The seed can be integer counts or a probability distribution. Marginals can be 1-D vectors (one per axis) or higher-dimensional arrays covering multiple axes jointly. Returns a tuple of `(fractional_population, stats_dict)` where stats include `conv`, `iterations`, `maxError`, and `pop`. ### Parameters - `seed` (numpy.ndarray): The initial population array (integer counts or probability distribution). - `dimensions` (list of lists): Specifies the dimensions for the marginals. - `marginals` (list of numpy.ndarray): A list of marginal sum arrays. ### Returns - `fractional_population` (numpy.ndarray): The resulting fractional population array. - `stats_dict` (dict): A dictionary containing statistical diagnostics such as `conv`, `iterations`, `maxError`, and `pop`. ### Request Example ```python import numpy as np import humanleague as hl # 2D IPF: sex × economic activity m_sex = np.array([52.0, 48.0]) m_econ = np.array([87.0, 13.0]) seed = np.ones([2, 2]) pop, stats = hl.ipf(seed, [[0], [1]], [m_sex, m_econ]) assert stats["conv"] assert stats["iterations"] == 1 assert stats["pop"] == 100.0 assert np.allclose(pop.sum(axis=1), m_sex) assert np.allclose(pop.sum(axis=0), m_econ) print(pop) # 3D IPF with non-uniform seed m0 = np.array([52.0, 48.0]) m1 = np.array([87.0, 13.0]) m2 = np.array([55.0, 45.0]) seed3 = np.array([[[1.0, 1.0], [1.0, 1.0]], [[1.0, 1.0], [1.0, 1.0]]]) pop3, stats3 = hl.ipf(seed3, [[0], [1], [2]], [m0, m1, m2]) assert stats3["conv"] assert np.allclose(pop3.sum(axis=(1, 2)), m0) assert np.allclose(pop3.sum(axis=(2, 0)), m1) assert np.allclose(pop3.sum(axis=(0, 1)), m2) # IPF with fractional (probability) marginals seed_prob = np.full((2, 2, 2), 1 / 8) indices = [(0, 1), (0, 2), (1, 2)] marginals = [ np.array([[0.92625, 0.02375], [0.02375, 0.02625]]), np.array([[0.92625, 0.02375], [0.02375, 0.02625]]), np.array([[0.92625, 0.02375], [0.02375, 0.02625]]) ] pop_prob, stats_prob = hl.ipf(seed_prob, indices, marginals) assert pop_prob.sum() == 1.0 assert stats_prob["conv"] ``` ``` -------------------------------- ### tabulate_individuals Source: https://context7.com/virgesmith/humanleague/llms.txt Expands a multidimensional population count array into a flat DataFrame where every row represents a single individual. ```APIDOC ## `tabulate_individuals` — Population Array to pandas DataFrame Expands a multidimensional population count array into a flat `DataFrame` where every row represents a single individual and each column represents one categorical dimension. This is the individual-level counterpart to `tabulate_counts`. ```python import numpy as np import humanleague as hl # Simple 2D example population = np.array([[0, 2], [3, 5]]) df = hl.tabulate_individuals(population, names=["Row", "Column"]) print(df) # Row Column # 0 0 1 # 1 0 1 # 2 1 0 # 3 1 0 # 4 1 0 # 5 1 1 # ... (10 rows total) assert len(df) == population.sum() assert len(df[(df.Row == 0) & (df.Column == 1)]) == 2 assert len(df[(df.Row == 1) & (df.Column == 0)]) == 3 # Real workflow: QIS → tabulate individuals m_age = np.array([52, 48]) m_nssec = np.array([10, 77, 13]) pop, stats = hl.qis([0, 1], [m_age, m_nssec]) assert stats["conv"] individuals = hl.tabulate_individuals(pop, names=["age_band", "nssec"]) print(individuals.head()) # age_band nssec # 0 0 0 # 1 0 1 # ... assert len(individuals) == 100 # 3D population pop3 = np.array([[[1, 0], [0, 2]], [[0, 1], [1, 0]]]) df3 = hl.tabulate_individuals(pop3, names=["Dim1", "Dim2", "Dim3"]) assert len(df3) == 5 assert list(df3.columns) == ["Dim1", "Dim2", "Dim3"] ``` ``` -------------------------------- ### Population Array to pandas Series (tabulate_counts) Source: https://context7.com/virgesmith/humanleague/llms.txt Converts a multidimensional integer population array into a pandas Series indexed by a MultiIndex of state tuples. Each entry represents the count for a specific combination of categories. ```python import numpy as np import humanleague as hl ``` -------------------------------- ### Flatten Population Array (Deprecated) Source: https://context7.com/virgesmith/humanleague/llms.txt Use `flatten` to convert a population array into a list of lists representing individual indices. This function is deprecated in favor of `tabulate_individuals`. Ensure warnings are handled if using this function. ```python import warnings import numpy as np import humanleague as hl m0 = np.array([52, 40, 4, 4]) m1 = np.array([87, 10, 3]) m2 = np.array([55, 15, 6, 12, 12]) pop, stats = hl.qis([0, 1, 2], [m0, m1, m2]) assert stats["conv"] with warnings.catch_warnings(): warnings.simplefilter("ignore", UserWarning) table = hl.flatten(pop) # table is a list of 3 lists (one per dimension), each of length 100 assert len(table) == 3 assert len(table[0]) == int(stats["pop"]) # Verify marginals are preserved in the flattened table for i, count in enumerate(m0): assert table[0].count(i) == count for i, count in enumerate(m1): assert table[1].count(i) == count for i, count in enumerate(m2): assert table[2].count(i) == count ``` -------------------------------- ### flatten Source: https://context7.com/virgesmith/humanleague/llms.txt Converts an n-dimensional integer population array into an n-element list of lists, where each inner list contains one state-index value per individual along that dimension. This is the legacy equivalent of `tabulate_individuals` and is now deprecated. ```APIDOC ## `flatten` — Population Array to List of Individual Indices (Deprecated) Converts an n-dimensional integer population array into an n-element list of lists, where each inner list contains one state-index value per individual along that dimension. This is the legacy equivalent of `tabulate_individuals` and is now deprecated in favour of it. ```python import warnings import numpy as np import humanleague as hl m0 = np.array([52, 40, 4, 4]) m1 = np.array([87, 10, 3]) m2 = np.array([55, 15, 6, 12, 12]) pop, stats = hl.qis([0, 1, 2], [m0, m1, m2]) assert stats["conv"] with warnings.catch_warnings(): warnings.simplefilter("ignore", UserWarning) table = hl.flatten(pop) # table is a list of 3 lists (one per dimension), each of length 100 assert len(table) == 3 assert len(table[0]) == int(stats["pop"]) # Verify marginals are preserved in the flattened table for i, count in enumerate(m0): assert table[0].count(i) == count for i, count in enumerate(m1): assert table[1].count(i) == count for i, count in enumerate(m2): assert table[2].count(i) == count ``` ``` -------------------------------- ### tabulate_counts Source: https://context7.com/virgesmith/humanleague/llms.txt Tabulates the counts of individuals in a population array based on specified dimension names. ```APIDOC ## `tabulate_counts` — Population Array to Counts Tabulates the counts of individuals in a population array based on specified dimension names. ```python import numpy as np import humanleague as hl # Simple 2D example m_age = np.array([30, 50, 20]) # young / middle / older m_sex = np.array([52, 48]) # male / female pop, stats = hl.qis([[0], [1]], [m_age, m_sex]) assert stats["conv"] counts = hl.tabulate_counts(pop, names=["age_band", "sex"]) print(counts) # age_band sex # 0 0 16 # 1 14 # 1 0 26 # 1 24 # 2 0 10 # 1 10 # Name: count, dtype: int64 # Access a specific cell print(counts.loc[1, 0]) # count for middle-aged males # Without names → integer index levels counts_anon = hl.tabulate_counts(pop) assert counts_anon.sum() == 100 # 3D example pop3 = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) s3 = hl.tabulate_counts(pop3, names=["Dim1", "Dim2", "Dim3"]) assert s3.sum() == 36 assert s3.loc[1, 1, 1] == 8 ``` ``` -------------------------------- ### tabulate_counts — Population Array to pandas Series Source: https://context7.com/virgesmith/humanleague/llms.txt Converts a multidimensional integer population array into a pandas `Series` indexed by a `MultiIndex` of state tuples. Each entry in the series corresponds to the count for that combination of categories. ```APIDOC ## `tabulate_counts` — Population Array to pandas Series Converts a multidimensional integer population array into a pandas `Series` indexed by a `MultiIndex` of state tuples. Each entry in the series corresponds to the count for that combination of categories. ```python import numpy as np import humanleague as hl ``` -------------------------------- ### Tabulate Population Counts Source: https://context7.com/virgesmith/humanleague/llms.txt Use `tabulate_counts` to convert a population array into a pandas DataFrame of counts, optionally with named dimensions. Ensure the population array is generated prior to tabulation. ```python import numpy as np import humanleague as hl m_age = np.array([30, 50, 20]) # young / middle / older m_sex = np.array([52, 48]) # male / female pop, stats = hl.qis([[0], [1]], [m_age, m_sex]) assert stats["conv"] counts = hl.tabulate_counts(pop, names=["age_band", "sex"]) print(counts) # age_band sex # 0 0 16 # 1 14 # 1 0 26 # 1 24 # 2 0 10 # 1 10 # Name: count, dtype: int64 # Access a specific cell print(counts.loc[1, 0]) # count for middle-aged males # Without names → integer index levels counts_anon = hl.tabulate_counts(pop) assert counts_anon.sum() == 100 # 3D example pop3 = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) s3 = hl.tabulate_counts(pop3, names=["Dim1", "Dim2", "Dim3"]) assert s3.sum() == 36 assert s3.loc[1, 1, 1] == 8 ``` -------------------------------- ### Define NumPy Array Types Source: https://github.com/virgesmith/humanleague/blob/main/doc/type-stubs.md Define type aliases for 1D NumPy arrays and lists of floats, and sequences of integers for use in type stubs. ```python FloatArray1d = npt.NDArray[np.float64] | list[float] IntArray1d = typing.Sequence[int] ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.