Source code for datascience.predicates

"""Predicate functions."""

import functools
import numbers
import numpy as np
import warnings
warnings.simplefilter('always', SyntaxWarning)

__all__ = ['are']


[docs] class are: """Predicate functions. The class is named "are" for calls to where. For example, given a table, predicates can be used to pick rows as follows. >>> from datascience import Table >>> t = Table().with_columns([ ... 'Sizes', ['S', 'M', 'L', 'XL'], ... 'Waists', [30, 34, 38, 42], ... ]) >>> t.where('Sizes', are.equal_to('L')) Sizes | Waists L | 38 >>> t.where('Waists', are.above(38)) Sizes | Waists XL | 42 >>> t.where('Waists', are.above_or_equal_to(38)) Sizes | Waists L | 38 XL | 42 >>> t.where('Waists', are.below(38)) Sizes | Waists S | 30 M | 34 >>> t.where('Waists', are.below_or_equal_to(38)) Sizes | Waists S | 30 M | 34 L | 38 >>> t.where('Waists', are.strictly_between(30, 38)) Sizes | Waists M | 34 >>> t.where('Waists', are.between(30, 38)) Sizes | Waists S | 30 M | 34 >>> t.where('Waists', are.between_or_equal_to(30, 38)) Sizes | Waists S | 30 M | 34 L | 38 >>> t.where('Sizes', are.equal_to('L')) Sizes | Waists L | 38 >>> t.where('Waists', are.not_above(38)) Sizes | Waists S | 30 M | 34 L | 38 >>> t.where('Waists', are.not_above_or_equal_to(38)) Sizes | Waists S | 30 M | 34 >>> t.where('Waists', are.not_below(38)) Sizes | Waists L | 38 XL | 42 >>> t.where('Waists', are.not_below_or_equal_to(38)) Sizes | Waists XL | 42 >>> t.where('Waists', are.not_strictly_between(30, 38)) Sizes | Waists S | 30 L | 38 XL | 42 >>> t.where('Waists', are.not_between(30, 38)) Sizes | Waists L | 38 XL | 42 >>> t.where('Waists', are.not_between_or_equal_to(30, 38)) Sizes | Waists XL | 42 >>> t.where('Sizes', are.containing('L')) Sizes | Waists L | 38 XL | 42 >>> t.where('Sizes', are.not_containing('L')) Sizes | Waists S | 30 M | 34 >>> t.where('Sizes', are.contained_in('MXL')) Sizes | Waists M | 34 L | 38 XL | 42 >>> t.where('Sizes', are.contained_in('L')) Sizes | Waists L | 38 >>> t.where('Sizes', are.not_contained_in('MXL')) Sizes | Waists S | 30 """
[docs] @staticmethod def equal_to(y): """Equal to y.""" check_iterable(y) return _combinable(lambda x: _equal_or_float_equal(x, y))
[docs] @staticmethod def above(y): """Greater than y.""" check_iterable(y) return _combinable(lambda x: x > y)
[docs] @staticmethod def below(y): """Less than y.""" check_iterable(y) return _combinable(lambda x: x < y)
[docs] @staticmethod def above_or_equal_to(y): """Greater than or equal to y.""" check_iterable(y) return _combinable(lambda x: x >= y or _equal_or_float_equal(x, y))
[docs] @staticmethod def below_or_equal_to(y): """Less than or equal to y.""" check_iterable(y) return _combinable(lambda x: x <= y or _equal_or_float_equal(x, y))
[docs] @staticmethod def strictly_between(y, z): """Greater than y and less than z.""" return _combinable(lambda x: y < x < z)
[docs] @staticmethod def between(y, z): """Greater than or equal to y and less than z.""" return _combinable(lambda x: (y <= x < z) or _equal_or_float_equal(x, y))
[docs] @staticmethod def between_or_equal_to(y, z): """Greater than or equal to y and less than or equal to z.""" return _combinable(lambda x: (y <= x <= z) or _equal_or_float_equal(x, y) or _equal_or_float_equal(x, z))
[docs] @staticmethod def containing(substring): """A string that contains within it the given substring.""" return _combinable(lambda x: substring in x)
[docs] @staticmethod def contained_in(superstring): """A string that is part of the given superstring.""" return _combinable(lambda x: x in superstring)
[docs] @staticmethod def not_equal_to(y): """Is not equal to y""" check_iterable(y) return -(are.equal_to(y))
[docs] @staticmethod def not_above(y): """Is not above y""" check_iterable(y) return are.below_or_equal_to(y)
[docs] @staticmethod def not_below(y): """Is not below y""" check_iterable(y) return are.above_or_equal_to(y)
[docs] @staticmethod def not_below_or_equal_to(y): """Is neither below y nor equal to y""" check_iterable(y) return are.above(y)
[docs] @staticmethod def not_above_or_equal_to(y): """Is neither above y nor equal to y""" check_iterable(y) return are.below(y)
[docs] @staticmethod def not_strictly_between(y, z): """Is equal to y or equal to z or less than y or greater than z""" return -(are.strictly_between(y,z))
[docs] @staticmethod def not_between(y, z): """Is equal to y or less than y or greater than z""" return -(are.between(y,z))
[docs] @staticmethod def not_between_or_equal_to(y,z): """Is less than y or greater than z""" return -(are.between_or_equal_to(y,z))
[docs] @staticmethod def not_containing(substring): """A string that does not contain substring""" return -(are.containing(substring))
[docs] @staticmethod def not_contained_in(superstring): """A string that is not contained within the superstring""" return -(are.contained_in(superstring))
############### # Combination # ############### class _combinable: """A wrapper that allows one-arg predicate functions to be combined.""" def __init__(self, f): self.f = f functools.update_wrapper(self, f) def __call__(self, x): return self.f(x) def __and__(self, other): return _combinable(lambda x: self.f(x) and other.f(x)) def __or__(self, other): return _combinable(lambda x: self.f(x) or other.f(x)) def __neg__(self): return _combinable(lambda x: not self.f(x)) def __xor__(self, other): return (self & -other) | (-self & other) ############ # Negation # ############ def _not(f): return lambda *args: -f(*args) def _equal_or_float_equal(x, y): if isinstance(x, numbers.Real): return x == y or np.nextafter(x, 1) == y or np.nextafter(x, 0) == y else: return x == y ############ # Utility # ############ def check_iterable(y): if isinstance(y, list) or isinstance(y, np.ndarray): warnings.warn("Do not pass an array or list to a predicate. \ If you are trying to find rows where two columns are the \ same, use table.where('c', are.equal_to, table.column('d'))\ instead of table.where('c', are.equal_to(table.column('d'))).", SyntaxWarning)