diff --git a/.travis.yml b/.travis.yml index b6b28e9038..afc860a58f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,10 +18,10 @@ install: - > conda create -q -c synthicity -n test-environment python=$TRAVIS_PYTHON_VERSION - ipython-notebook jinja2 matplotlib numpy pandas pandana patsy pip pytables - pytest pyyaml scipy statsmodels toolz + cytoolz ipython-notebook jinja2 matplotlib numpy pandas pandana patsy pip + pytables pytest pyyaml scipy statsmodels toolz - source activate test-environment -- pip install simplejson bottle prettytable +- pip install bottle prettytable simplejson zbox - pip install https://github.com/synthicity/urbansim/archive/master.zip - pip install openmatrix - pip install pytest-cov coveralls pep8 diff --git a/activitysim/activitysim.py b/activitysim/activitysim.py index 580663396a..4dc54907b1 100644 --- a/activitysim/activitysim.py +++ b/activitysim/activitysim.py @@ -2,13 +2,14 @@ # Copyright (C) 2014-2015 Synthicity, LLC # See full license in LICENSE.txt. -from skim import Skims, Skims3D +from operator import itemgetter import numpy as np import pandas as pd - from urbansim.urbanchoice import interaction +from zbox import toolz as tz +from .skim import Skims, Skims3D from .mnl import utils_to_probs, make_choices @@ -264,3 +265,41 @@ def interaction_simulate( choices = model_design.index.take(positions + offsets) return pd.Series(choices, index=choosers.index), model_design + + +def other_than(groups, bools): + """ + Construct a Series that has booleans indicating the presence of + something- or someone-else with a certain property within a group. + + Parameters + ---------- + groups : pandas.Series + A column with the same index as `bools` that defines the grouping + of `bools`. The `bools` Series will be used to index `groups` and + then the grouped values will be counted. + bools : pandas.Series + A boolean Series indicating where the property of interest is present. + Should have the same index as `groups`. + + Returns + ------- + others : pandas.Series + A boolean Series with the same index as `groups` and `bools` + indicating whether there is something- or something-else within + a group with some property (as indicated by `bools`). + + """ + counts = groups[bools].value_counts() + merge_col = groups.to_frame(name='right') + pipeline = tz.compose( + tz.curry(pd.Series.fillna, value=False), + itemgetter('left'), + tz.curry( + pd.DataFrame.merge, right=merge_col, how='right', left_index=True, + right_on='right'), + tz.curry(pd.Series.to_frame, name='left')) + gt0 = pipeline(counts > 0) + gt1 = pipeline(counts > 1) + + return gt1.where(bools, other=gt0) diff --git a/activitysim/tests/test_other_than.py b/activitysim/tests/test_other_than.py new file mode 100644 index 0000000000..d607886b01 --- /dev/null +++ b/activitysim/tests/test_other_than.py @@ -0,0 +1,25 @@ +import pandas as pd +import pandas.util.testing as pdt +import pytest + + +from ..activitysim import other_than + + +@pytest.fixture(scope='module') +def people(): + return pd.DataFrame({ + 'household': [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], + 'ptype': [1, 2, 1, 3, 1, 2, 3, 2, 2, 1]}, + index=['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']) + + +def test_other_than(people): + expected = pd.Series( + [False, False, True, True, True, False, True, True, True, True], + index=people.index) + + bools = people['ptype'] == 2 + others = other_than(people['household'], bools) + + pdt.assert_series_equal(others, expected) diff --git a/setup.py b/setup.py index 53aeb9c4fd..2001d26ccd 100644 --- a/setup.py +++ b/setup.py @@ -18,10 +18,12 @@ ], packages=find_packages(exclude=['*.tests']), install_requires=[ - 'numpy>=1.8.0', - 'openmatrix>=0.2.2', - 'pandas>=0.13.1', - 'tables>=3.1.0', - 'urbansim>=1.3' + 'numpy >= 1.8.0', + 'openmatrix >= 0.2.2', + 'pandas >= 0.13.1', + 'tables >= 3.1.0', + 'toolz >= 0.7', + 'urbansim >= 1.3', + 'zbox >= 1.2' ] )