Metadata-Version: 1.0
Name: sample-space
Version: 0.2.0
Summary: A simple API for defining sample spaces (to run simple statistical simulations)
Home-page: http://github.com/asross/sample_space
Author: Andrew Ross
Author-email: andrewslavinross@gmail.com
License: MIT
Description: Sample Space
        ============
        
        ...is a very lightweight Python API for simulating sample spaces,
        events, random variables, and (conditional) distributions.
        
        Example
        -------
        
        Check out the `iPython notebook <./example.ipynb>`__ or read the
        following:
        
        .. code:: python
        
            from sample_space import *
        
            class NCoinTosses(Experiment):
                def __init__(self, n, p):
                    self.n = n
                    self.p = p
        
                def rerun(self):
                    self.tosses = [Bern(self.p) for _ in range(self.n)]
        
                def heads(self):
                    return sum(self.tosses)
        
                def there_are_at_least_two_heads(self):
                    return self.heads() >= 2
        
                def first_toss_heads(self):
                    return self.tosses[0]
        
            space = SampleSpace(NCoinTosses(10, 0.5), iters=20000)
        
            # ask for probability of any truthy method
            print('        P(#H>=2):', space.probability_that('there_are_at_least_two_heads'))
        
            # alias for the above, if it's more grammatical
            print('           P(H1):', space.probability_of('first_toss_heads'))
        
            # change the number of iterations
            print(' P(H1), 1K iters:', space.probability_of('first_toss_heads', iters=1000))
        
            # ask for probabilities of functions of random variables
            print('         P(#H>5):', space.probability_that(['heads', is_greater_than(5)]))
        
            # ask for conditional probabilities
            print('      P(#H>5|H1):', space.probability_that(['heads', is_greater_than(5)], given=['first_toss_heads']))
            print('      P(H1|#H>5):', space.probability_of('first_toss_heads', given=[['heads', is_greater_than(5)]]))
            print(' P(#H>5|H1,H>=2):', space.probability_that(['heads', is_greater_than(5)],
                given=['first_toss_heads', 'there_are_at_least_two_heads']))
        
            # ask for expectations, variances, and moments, conditionally or absolutely
            print('           E(#H):', space.expected_value_of('heads'))
            print('        E(#H|H1):', space.expected_value_of('heads', given=['first_toss_heads']))
            print('         Var(#H):', space.variance_of('heads'))
            print('      Var(#H|H1):', space.variance_of('heads', given=['first_toss_heads']))
            print('1st moment of #H:', space.nth_moment_of('heads', 1))
            print('2nd moment of #H:', space.nth_moment_of('heads', 2))
            print('3rd moment of #H:', space.nth_moment_of('heads', 3))
            print('4th moment of #H:', space.nth_moment_of('heads', 4))
            print('  Skewness of #H:', space.nth_moment_of('heads', 3, central=True, normalized=True), '(using nth_moment_of w/ central=True, normalized=True)')
            print('  Skewness of #H:', space.skewness_of('heads'), '(using skewness_of)')
            print('  Kurtosis of #H:', space.kurtosis_of('heads'))
        
            # some plots
            fig = plt.figure(figsize=(14,3))
        
            # plot distribution histograms
            fig.add_subplot(121)
            space.plot_distribution_of('heads') # pass kwargs
            plt.legend()
        
            # plot conditional distribution histograms
            fig.add_subplot(122)
            space.plot_distribution_of('heads', given=['first_toss_heads'], bins=10) # can pass kwargs
            plt.legend()
            plt.show()
        
        Which should output (plus some plots):
        
        ::
        
                    P(#H>=2): 0.98975
                       P(H1): 0.502
             P(H1), 1K iters: 0.48
                     P(#H>5): 0.37665
                  P(#H>5|H1): 0.5076294006183305
                  P(H1|#H>5): 0.6580109757729888
             P(#H>5|H1,H>=2): 0.49361831442463533
                       E(#H): 4.9983
                    E(#H|H1): 5.48924623116
                     Var(#H): 2.4486457975
                  Var(#H|H1): 2.31806506582
            1st moment of #H: 4.99245
            2nd moment of #H: 27.5097
            3rd moment of #H: 163.13055
            4th moment of #H: 1015.54155
              Skewness of #H: -0.00454435802967 (using nth_moment_of w/ central=True, normalized=True)
              Skewness of #H: 0.00414054522343 (using skewness_of)
              Kurtosis of #H: 2.78225928171
        
        Why?
        ----
        
        Mostly to avoid bugs / reduce boilerplate in statistical simulations for
        sanity-checking homework solutions. But also to get a better
        understanding of probability theory.
        
        `Sample spaces <https://en.wikipedia.org/wiki/Sample_space>`__ are a
        core concept in probability theory. They encapsulate the idea of
        repeatedly running an experiment with random results. Almost every
        important statistical quantity -- the probability of an event, or any
        moment of a random variable -- is always defined relative to a sample
        space. So if you're trying to program meaningful simulations (and if
        you're more concerned with expressiveness than performance), you might
        as well organize your code by explicitly defining one.
        
        Installation / Usage
        --------------------
        
        First run
        
        ::
        
            pip install sample_space
        
        and ``import`` the library. Then define a subclass of ``Experiment``
        that responds to ``rerun(self)``. ``rerun`` should perform a random
        experiment and store one or more basic results as instance variables. If
        you want to define more complex events or random variables, you can
        express them as instance methods.
        
        Then, initialize a ``SampleSpace`` with an instance of your
        ``Experiment``. You can query your sample space for the
        ``probability_that``/``probability_of`` an event, or you can query it
        for the ``distribution_of``, ``expected_value_of``, ``variance_of``,
        ``skewness_of``, ``kurtosis_of``, or ``nth_moment_of`` of random
        variable (which can also just be an event, in which case it will be
        interpreted as an indicator). Finally, for any of these methods, you can
        pass a ``given`` keyword argument with a list of events, which will make
        any results you obtain conditional on all of those events occurring.
        Behind the scenes, ``SampleSpace`` will just ``rerun`` your experiment
        10000 times and average your random variable or count how often an event
        occurs (conditionally). You can pass an ``iters`` keyword argument to
        any method or to ``SampleSpace.__init__`` to increase the number of
        iterations.
        
        To reference events or random variable, pass the string name of an
        instance variable or instance method of your experiment, or pass an
        array with a variable/method name and a lambda function. For example:
        
        .. code:: python
        
            space = SampleSpace(CoinTossExperiment(10))
            space.probability_that('first_toss_is_heads')
            space.probability_that(['n_heads', lambda h: h > 5])
            space.expected_value_of('n_heads')
            space.expected_value_of('n_heads', given=['first_toss_is_heads'])
            space.probability_that('first_toss_is_heads, given=[['n_heads', lambda h: h > 3], 'last_toss_is_heads'])
        
        Additionally, ``sample_space`` defines a few helpful lambda-returning
        methods (``is_greater_than(x)``, ``is_less_than(x)``,
        ``is_at_least(x)``, ``is_at_most(x)``, ``equals(x)``) for convenience.
        Of course, you could also define instance methods on your ``Experiment``
        to accomplish the same goal.
        
        The library also exposes a few basic sampling functions (``Bern(p)``,
        ``Bin(n,p)``, ``RandomSign(p)``, and ``Categ(categories, weights)``) to
        assist with defining experiments.
        
        Lite Version
        ------------
        
        If you'd prefer not to define a full ``Experiment`` class, you can also
        just define a random event / random variable function that returns
        either a boolean or a number, and call
        ``probability_that``/``expected_value_of``:
        
        .. code:: python
        
            import sample_space as ss
        
            def weighted_coin_flip_is_heads(p=0.4):
              return ss.Bern(p)
        
            def n_weighted_heads(n=100, p=0.4):
              return sum(weighted_coin_flip_is_heads(p) for _ in range(n))
        
            print(ss.probability_that(weighted_coin_flip_is_heads))
            print(ss.probability_that(lambda: weighted_coin_flip_is_heads(0.5))
            print(ss.expected_value_of(n_weighted_heads))
            print(ss.expected_value_of(lambda: n_weighted_heads(200, 0.3)))
        
        License
        -------
        
        `MIT <http://opensource.org/licenses/MIT>`__
        
Platform: UNKNOWN
