Source code for gameanalysis.bootstrap

"Module for using bootstrap in analysis"
import multiprocessing

import numpy as np
import numpy.random as rand

from gameanalysis import regret


[docs]def game_function(game, function, num_resamples, num_returned, percentiles=None, processes=None): """Bootstrap the value of a function over a sample game Parameters ---------- game : SampleGame The sample game to bootstrap the function value over. function : f(Game) -> float or f(Game) -> [float] The function of the game to compute. It must be pickleable unless processes is 1, and it must return either a float or an iterable of floats. If an iterable of floats, this bootstrap all indices of the return value independently. num_resamples : int The number of bootstrap samples. Higher will take longer but also give better accuracy. num_returned : int The number of float values your function returns. percentiles : int or [int] The percentiles to compute on the resulting data in [0, 100]. Standard percentiles are 95, or [2.5, 97.5]. By default, return all samples. processes : int (optional) The number of processes to use for computation. By default this is the number of cores. Returns ------- bootstrap_percentiles : ndarray An ndarray of the percentiles from bootstrapping. The shape will depend on the number of percentiles and the number of values returned from your function. """ func = _BootstrapPickleable(game, function) def process(gen): result = np.empty((num_resamples, num_returned)) for i, res in enumerate(gen): result[i] = res return result if processes == 1: results = process(func(_) for _ in range(num_resamples)) else: with multiprocessing.Pool(processes) as pool: results = process(pool.imap_unordered(func, range(num_resamples))) if percentiles is None: results.sort(0) return results.T else: return np.percentile(results, percentiles, 0).T
class _BootstrapPickleable(object): """A pickleable game function combo""" def __init__(self, game, function): self.game = game self.function = function def __call__(self, _): self.game.resample() return self.function(self.game)
[docs]def profile_function(game, function, profiles, num_resamples, percentiles=None, processes=None): """Compute a function over profiles Parameters ---------- game : SampleGame The sample game to bootstrap the function value over. function : Game, profile -> float The function of the game profile pair to compute. It must be pickleable, and it must return a float (e.g. regret.mixture_regret). profiles : ndarray The profiles to compute bootstrap bounds over for function. num_resamples : int The number of bootstrap samples. Higher will take longer but also give better accuracy. percentiles : int or [int] The percentiles to compute on the resulting data in [0, 100]. Standard percentiles are 95, or [2.5, 97.5]. By default, return all samples. processes : int (optional) The number of processes to use for computation. By default this is the number of cores. Returns ------- bootstrap_percentiles : ndarray An ndarray of the percentiles from bootstrapping for each profile. The shape will depend on the number of percentiles and the number of profiles. """ if profiles.ndim == 1: profiles = profiles[None] func = _ProfilePickleable(profiles, function) return game_function(game, func, num_resamples, profiles.shape[0], percentiles, processes)
class _ProfilePickleable(object): def __init__(self, profiles, function): self.profiles = profiles self.function = function def __call__(self, game): return [self.function(game, prof) for prof in self.profiles]
[docs]def mixture_regret(game, mixtures, num_resamples, percentiles=None, processes=None): """Compute percentile bounds on mixture regret Parameters ---------- game : SampleGame The sample game to bootstrap the function value over. mixtures : ndararay The profiles to compute mixture regret bounds for. num_resamples : int The number of bootstrap samples. Higher will take longer but also give better accuracy. percentiles : int or [int] The percentiles to compute on the resulting data in [0, 100]. Standard percentiles are 95, or [2.5, 97.5]. By default, return all samples. processes : int (optional) The number of processes to use for computation. By default this is the number of cores. Returns ------- regret_percentiles : ndarray An ndarray of the percentiles for bootstrap regret for each profile. """ return profile_function(game, regret.mixture_regret, mixtures, num_resamples, percentiles, processes)
[docs]def mixture_welfare(game, mixtures, num_resamples, percentiles=None, processes=None): """Compute percentile bounds on mixture welfare Parameters ---------- game : SampleGame The sample game to bootstrap the function value over. mixtures : ndarray The profiles to compute mixture welfare bounds for. num_resamples : int The number of bootstrap samples. Higher will take longer but also give better accuracy. percentiles : int or [int] The percentiles to compute on the resulting data in [0, 100]. Standard percentiles are 95, or [2.5, 97.5]. By default, return all samples. processes : int (optional) The number of processes to use for computation. By default this is the number of cores. Returns ------- bootstrap_percentiles : ndarray An ndarray of the percentiles for bootstrap welfare for each profile. """ return profile_function(game, regret.mixed_social_welfare, mixtures, num_resamples, percentiles, processes)
[docs]def mean(data, num_resamples, percentiles=None): """Compute bootstrap bounds for the mean of a data set One particular use is compute bootstrap bounds on social welfare of a mixture if all of the samples are iid draws of welfare from the mixture. Parameters ---------- data : [float] or ndarray The data to get bootstrap estimates around the mean of. num_resamples : int The number of bootstrap samples. Higher will take longer but also give better accuracy. percentiles : int or [int] The percentiles to compute on the resulting data in [0, 100]. Standard percentiles are 95, or [2.5, 97.5]. """ data = np.asarray(data, float).ravel() samples = rand.multinomial(data.size, np.ones(data.size) / data.size, num_resamples) result = samples.dot(data) / data.size if percentiles is None: result.sort() return result else: return np.percentile(result, percentiles)
[docs]def sample_regret(game, mixture_payoffs, deviation_payoffs, num_resamples, percentiles=None): """Compute bootstrap bounds on the mixture regret with samples Parameters ---------- game : BaseGame The game the samples are from. mixture_payoffs : ndarray A sample of payoffs by role. The distribution must come from the desired mixture deviation payoffs : ndarray The payoff to the deviator when everyone else is played according to the mixture. The strategy payoffs can either by a mapping of strategy names, or just a list of payoffs. num_resamples : int The number of bootstrap samples. Higher will take longer but also give better accuracy. percentiles : int or [int] The percentiles to compute on the resulting data in [0, 100]. Standard percentiles are 95, or [2.5, 97.5]. Returns ------- Note: The lengths of every list must be the same """ num_samples = mixture_payoffs.shape[0] samples = rand.multinomial(num_samples, np.ones(num_samples) / num_samples, num_resamples) / num_samples dev_samples = game.role_reduce(samples.dot(deviation_payoffs), ufunc=np.maximum) mix_samples = samples.dot(mixture_payoffs) result = np.max(dev_samples - mix_samples, 1) if percentiles is None: result.sort() return result else: return np.percentile(result, percentiles)