def to_property(obj, prop=None,<EOL>dtype=Ellipsis,<EOL>outliers=None,  data_range=None,    clipped=np.inf,<EOL>weights=None,   weight_min=<NUM_LIT:0>,       weight_transform=Ellipsis,<EOL>mask=None,      valid_range=None,   null=np.nan,<EOL>transform=None, yield_weight=False):
<EOL>if pimms.is_vector(obj) and len(obj) < <NUM_LIT:4> and prop is None:<EOL><INDENT>kw0 = dict(dtype=dtype,           null=null,<EOL>outliers=outliers,     data_range=data_range,<EOL>clipped=clipped,       weights=weights,<EOL>weight_min=weight_min, weight_transform=weight_transform,<EOL>mask=mask,             valid_range=valid_range,<EOL>transform=transform,   yield_weight=yield_weight)<EOL>if   len(obj) == <NUM_LIT:2>: return to_property(obj[<NUM_LIT:0>], obj[<NUM_LIT:1>], **kw0)<EOL>elif len(obj) == <NUM_LIT:3>: return to_property(obj[<NUM_LIT:0>], obj[<NUM_LIT:1>], **pimms.merge(kw0, obj[<NUM_LIT:2>]))<EOL>else: raise ValueError('<STR_LIT>')<EOL><DEDENT>if prop is None: raise ValueError('<STR_LIT>')<EOL>if isinstance(obj, VertexSet): (vset, obj) = (obj,  obj.properties)<EOL>elif pimms.is_map(obj):        (vset, obj) = (None, obj)<EOL>elif obj is None:              (vset, obj) = (None, None)<EOL>else: ValueError('<STR_LIT>')<EOL>if pimms.is_str(prop):<EOL><INDENT>if obj is None: raise ValueError('<STR_LIT>')<EOL>else: prop = obj[prop]<EOL><DEDENT>if is_set(prop):<EOL><INDENT>def _lazy_prop(kk):<EOL><INDENT>return lambda:to_property(obj, kk,<EOL>dtype=dtype,           null=null,<EOL>outliers=outliers,     data_range=data_range,<EOL>clipped=clipped,       weights=weights,<EOL>weight_min=weight_min, weight_transform=weight_transform,<EOL>mask=mask,             valid_range=valid_range,<EOL>transform=transform,   yield_weight=yield_weight)<EOL><DEDENT>return pimms.itable({k:_lazy_prop(k) for k in prop})<EOL><DEDENT>elif (pimms.is_matrix(prop) or<EOL>(pimms.is_vector(prop) and all(pimms.is_str(p) or pimms.is_vector(p) for p in prop))):<EOL><INDENT>return np.asarray([to_property(obj, k,<EOL>dtype=dtype,           null=null,<EOL>outliers=outliers,     data_range=data_range,<EOL>clipped=clipped,       weights=weights,<EOL>weight_min=weight_min, weight_transform=weight_transform,<EOL>mask=mask,             valid_range=valid_range,<EOL>transform=transform,   yield_weight=yield_weight)<EOL>for k in prop])<EOL><DEDENT>elif not pimms.is_vector(prop):<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>else: prop = np.asarray(prop)<EOL>if dtype is Ellipsis:  dtype = prop.dtype<EOL>if pimms.is_str(weights):<EOL><INDENT>if obj is None: raise ValueError('<STR_LIT>')<EOL>else: weights = obj[weights]<EOL><DEDENT>weights_orig = weights<EOL>if weights is None or weight_min is None: low_weight = np.asarray([], dtype=np.int)<EOL>else:<EOL><INDENT>if weight_transform is Ellipsis:<EOL><INDENT>weights = np.array(weights, dtype=np.float)<EOL>weights[weights < <NUM_LIT:0>] = <NUM_LIT:0><EOL><DEDENT>elif weight_transform is not None:<EOL><INDENT>weight = weight_transform(np.asarray(weights))<EOL><DEDENT>if not pimms.is_vector(weights, '<STR_LIT>'):<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>low_weight = (np.asarray([], dtype=np.int) if weight_min is None else<EOL>np.where(weights < weight_min)[<NUM_LIT:0>])<EOL><DEDENT>outliers = np.asarray([], dtype=np.int) if outliers is None else np.arange(len(prop))[outliers]<EOL>outliers = np.union1d(outliers, low_weight) <EOL>mask = to_mask(obj, mask, indices=True)<EOL>if pimms.is_array(prop, '<STR_LIT>'):<EOL><INDENT>if pimms.is_array(prop, '<STR_LIT:int>'): prop = np.array(prop, dtype=np.float)<EOL>else: prop = np.array(prop) <EOL>if not np.isnan(null): prop[prop == null] = np.nan<EOL>mask_nan = np.isnan(prop)<EOL>mask_inf = np.isinf(prop)<EOL>where_nan = np.where(mask_nan)[<NUM_LIT:0>]<EOL>where_inf = np.where(mask_inf)[<NUM_LIT:0>]<EOL>where_ok  = np.where(np.logical_not(mask_nan | mask_inf))[<NUM_LIT:0>]<EOL>if valid_range is None: where_inv = np.asarray([], dtype=np.int)<EOL>else: where_inv = where_ok[(prop[where_ok] < valid_range[<NUM_LIT:0>]) |<EOL>(prop[where_ok] > valid_range[<NUM_LIT:1>])]<EOL>where_nan = np.union1d(where_nan, where_inv)<EOL>mask = np.setdiff1d(mask, where_nan)<EOL>outliers = np.intersect1d(outliers, mask) <EOL>if data_range is not None:<EOL><INDENT>if not pimms.is_vector(data_range): data_range = (<NUM_LIT:0>, data_range)<EOL>mii = mask[(prop[mask] < data_range[<NUM_LIT:0>]) | (prop[mask] > data_range[<NUM_LIT:1>])]<EOL>outliers = np.union1d(outliers, mii)<EOL><DEDENT>outliers = np.union1d(outliers, mask[np.isinf(prop[mask])])<EOL>unmask = np.setdiff1d(np.arange(len(prop), dtype=np.int), mask)<EOL>if len(outliers) > <NUM_LIT:0>:  prop[outliers]  = clipped<EOL>if len(unmask) > <NUM_LIT:0>: prop[unmask] = null<EOL>prop = prop.astype(dtype)<EOL><DEDENT>elif len(mask) < len(prop) or len(outliers) > <NUM_LIT:0>:<EOL><INDENT>tmp = np.full(len(prop), null, dtype=dtype)<EOL>tmp[mask] = prop[mask]<EOL>if len(outliers) > <NUM_LIT:0>: tmp[outliers] = clipped<EOL><DEDENT>if yield_weight:<EOL><INDENT>if weights is None or not pimms.is_vector(weights): weights = np.ones(len(prop))<EOL>else: weights = np.array(weights, dtype=np.float)<EOL>weights[where_nan] = <NUM_LIT:0><EOL>weights[outliers] = <NUM_LIT:0><EOL><DEDENT>if transform: prop = transform(prop)<EOL>return (prop, weights) if yield_weight else prop<EOL>
to_property(obj, prop) yields the given property from obj after performing a set of filters on
  the property, as specified by the options. In the property array that is returned, the values
  that are considered outliers (data out of some range) are indicated by numpy.inf, and values
  that are not in the optionally-specified mask are given the value numpy.nan; these may be
  changed with the clipped and null options, respectively.
to_property((obj, prop)) is equivalent to to_property(obj, prop).

The property argument prop may be either specified as a string (a property name in the object)
or as a property vector. The weights option may also be specified this way. Additionally, the
prop arg may be a list such as ['polar_angle', 'eccentricity'] where each element is either a
string or a vector, in which case the result is a matrix of properties. Finally, prop may be
a set of property names, in which case the return value is an itable whose keys are the property
names.

The obj argument may be either a VertexSet object (such as a Mesh or Tesselation) or a mapping
object such as a pimms ITable. If no strings are used to specify properties, it may additionally
be omitted or set to None.

The following options are accepted:
  * outliers (default:None) specifies the vertices that should be considered outliers; this
    may be either None (no outliers explicitly specified), a list of indices, or a boolean
    mask.
  * data_range (default:None) specifies the acceptable data range for values in the
    property; if None then this paramter is ignored. If specified as a pair of numbers
    (min, max), then data that is less than the min or greater than the max is marked as an
    outlier (in addition to other explicitly specified outliers). The values np.inf or 
    -np.inf can be specified to indicate a one-sided range.
  * clipped (default:np.inf) specifies the value to be used to mark an out-of-range value in
    the returned array.
  * mask (default:None) specifies the vertices that should be included in the property 
    array; values are specified in the mask similarly to the outliers option, except that
    mask values are included rather than excluded. The mask takes precedence over the 
    outliers, in that a null (out-of-mask) value is always marked as null rather than
    clipped.
  * valid_range (default: None) specifies the range of values that are considered valid; 
    i.e., values outside of the range are marked as null. Specified the same way as
    data_range.
  * null (default: np.nan) specifies the value marked in the array as out-of-mask.
  * transform (default:None) may optionally provide a function to be passed the array prior
    to being returned (after null and clipped values are marked).
  * dtype (defaut:Ellipsis) specifies the type of the array that should be returned.
    Ellipsis indicates that the type of the given property should be used. If None, then a
    normal Python array is returned. Otherwise, should be a numpy type such as numpy.real64
    or numpy.complex128.
  * weights (default:Ellipsis) specifies the property or property array that should be
    examined as the weights. The default, Ellipsis, simply chops values that are close to or
    less than 0 such that they are equal to 0. None specifies that no transformation should
    be applied.
  * weight_min (default:0) specifies the value at-or-below which the weight is considered 
    insignificant and the value is marked as clipped.
  * weight_transform (default:None) specifies a function that should be applied to the
    weight array before being used in the function.
  * yield_weight (default:False) specifies, if True, that instead of yielding prop, yield
    the tuple (prop, weights).