Skip to content

Commit

Permalink
Basic usage documentation for tag filtering expressions.
Browse files Browse the repository at this point in the history
Tempted to add an AggTags.filter method for documenting this directly in
the library, but I think we'll save that for later. Nothing new is
gained from it.

Next: covering fuzzy matching.
  • Loading branch information
dotsdl committed Mar 19, 2016
1 parent 9d0eb6d commit 560dc5b
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 2 deletions.
122 changes: 121 additions & 1 deletion docs/Bundles.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,35 @@ get Bundles of the Treants you actually want to work with (see below).

Basic member selection
======================
All the same selection patterns that work for Views (see :ref:`Views_selecting`).
This includes indexing with integers::

>>> b = dtr.discover()
>>> b[1]
<Treant: 'sprout'>

slicing::

>>> b[1:]
<Bundle([<Treant: 'sprout'>, <Treant: 'acorns'>])>

fancy indexing::

>>> b[[1, 2, 0]]
<Bundle([<Treant: 'sprout'>, <Treant: 'acorns'>, <Treant: 'beans'>])>

boolean indexing::

>>> b[[False, False, True]]
<Bundle([<Treant: 'acorns'>])>

and indexing by Treant name::

>>> b['sprout']
<Bundle([<Treant: 'sprout'>])>

Note that since Treant names need not be unique, indexing by name always yields
a Bundle.


Filtering on Treant tags
Expand All @@ -58,13 +86,105 @@ characteristics beyond just their path in the filesystem. Tags are one of these
distinguishing features, and Bundles can use them directly to filter their
members.


The aggregated tags for all members in a Bundle are accessible via
:attr:`datreant.core.Bundle.tags`. Just calling this property gives a view of
the tags present in every member Treant::

>>> b.tags
<AggTags(['plant'])>

But our Treants probably have more than just this one tag. We can get at the
tags represented by at least one Treant in the Bundle with ::

>>> b.tags.any
{'for eating',
'for squirrels',
'fruitless',
'not for humans',
'plant',
'plentiful',
'tiny'}

Since tags function as a set, we get back a set. Likewise we have ::

>>> b.tags.all
{'plant'}

which we've already seen.


Using tag expressions to select members
---------------------------------------

We can use getitem syntax to query the members of Bundle. For example, giving a
single tag like ::

>>> b.tags['for eating']
[True, False, False]

gives us back a list of booleans. This can be used directly on the Bundle as
a boolean index to get back a subselection of its members::

>>> b[b.tags['for eating']]
<Bundle([<Treant: 'beans'>])>

We can also provide multiple tags to match more Treants::

>>> b[b.tags['for eating', 'not for humans']]
<Bundle([<Treant: 'beans'>, <Treant: 'acorns'>])>

The above is equivalent to giving a tuple of tags to match, as below::

>>> b[b.tags[('for eating', 'not for humans')]]
<Bundle([<Treant: 'beans'>, <Treant: 'acorns'>])>

Using a tuple functions as an "or"-ing of the tags given, in which case
the resulting members are those that have at least one of the tags inside
the tuple.

But if we give a list instead, we get::

>>> b[b.tags[['for eating', 'not for humans']]]
<Bundle([])>

...something else, in this case nothing. Giving a list functions as an
"and"-ing of the tags given inside, so the above query will only give members
that have both 'for eating' and 'not for humans' as tags. There were none in
this case.

Lists and tuples can be nested to build complex and/or selections. In addition,
sets can be used to indicate negation ("not")::

>>> b[b.tags[{'for eating'}]]
<Bundle([<Treant: 'sprout'>, <Treant: 'acorns'>])>

Putting multiple tags inside a set functions as a negated "and"-ing of the
contents::

>>> b[b.tags[{'for eating', 'not for humans'}]]
<Bundle([<Treant: 'beans'>, <Treant: 'sprout'>, <Treant: 'acorns'>])>


Fuzzy matching for tags
-----------------------


Reference: AggTags
------------------
.. autoclass:: datreant.core.agglimbs.AggTags
:members:
:inherited-members:



Grouping with Treant categories
===============================

Reference: AggCategories
------------------------
.. autoclass:: datreant.core.agglimbs.AggCategories
:members:
:inherited-members:

Reference: Bundle
=================
Expand Down
2 changes: 2 additions & 0 deletions docs/Views.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ A **View** makes it possible to work with arbitrary Trees and Leaves as a
single logical unit. It is an ordered set of its members.


.. _Views_selecting:

Building a View and selecting members
=====================================
Views can be built from a list of paths, existing or not. Taking our working
Expand Down
2 changes: 1 addition & 1 deletion src/datreant/core/limbs.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def __getitem__(self, value):
if isinstance(value, set):
# a set of tags gives only members WITHOUT ALL the tags
# can be used for `not`, basically
fits = all([not self[item] for item in value])
fits = not all([self[item] for item in value])
elif isinstance(value, string_types):
fits = value in self

Expand Down
3 changes: 3 additions & 0 deletions src/datreant/core/tests/test_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,5 +323,8 @@ def test_tags_set_behavior(self, collection, testtreant, testgroup,
def test_tags_getitem(self, collection, testtreant, testgroup, tmpdir):
pass

def test_tags_fuzzy(self, collection, testtreant, testgroup, tmpdir):
pass

class TestAggCategories:
pass
3 changes: 3 additions & 0 deletions src/datreant/core/tests/test_treants.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,9 @@ def test_tags_set_behavior(self, tmpdir, treantclass):
def test_tags_setting(self, tmpdir, treantclass):
pass

def test_tags_fuzzy(self, tmpdir, treant):
pass

class TestCategories:
"""Test treant categories"""

Expand Down

0 comments on commit 560dc5b

Please sign in to comment.