Select, order and track values¶
This guide covers the Collection options beyond plain filtering and
grouping: removing duplicates, sorting, selecting by looked-up values,
putting one element in several groups, recording sources, and writing a
subclass.
Remove duplicates and sort¶
from sercol import Collection
values = Collection([5, 2, 3, 2], distinct=True, sort_key=abs)
print(list(values))
[2, 3, 5]
distinct=True keeps the first occurrence of each element, in input order, so
elements must be hashable. sort_key then sorts what remains; elements with
equal keys keep their input order.
Select by a value lookup¶
scores = {"alpha": 2.0, "beta": 0.5}
names = Collection(["alpha", "beta", "unknown"])
selected = names.filter_above_threshold(
key_fn=lambda name: name,
value_dict=scores,
threshold=1.0,
)
print(list(selected))
['alpha']
The comparison is strictly greater than the threshold. Missing keys receive
default_value, which defaults to zero. Use filter_any_above_threshold()
when each element supplies several lookup keys and any one may qualify.
Put an element in several groups¶
words = Collection(["ab", "bc"])
groups = words.multi_groupby(lambda word: set(word))
print({key: list(groups[key]) for key in sorted(groups)})
{'a': ['ab'], 'b': ['ab', 'bc'], 'c': ['bc']}
multi_groupby() accepts an iterable of keys for each element. Return distinct
keys for each element when it should appear only once in each group.
Keep source metadata¶
values = Collection([1, 2, 3], sources={"results.csv"})
selected = values.filter(lambda value: value > 1)
print(selected.source)
print(selected.filenames)
results.csv
['results.csv']
sources labels where values came from; it does not open those files. Each
collection keeps its own copy of the set, so adding a source to one collection
doesn't change another. source requires exactly one source. filenames
strips directory components and is sorted; filename is its first entry, and
raises ValueError when there is none.
Two collections are equal when they have the same class, the same elements in the same order and the same sources:
print(values == Collection([1, 2, 3], sources={"results.csv"}))
print(values == Collection([1, 2, 3], sources={"other.csv"}))
True
False
Extend the collection¶
filter() and the grouping methods build their results with
clone_with_new_elements(), which passes elements, distinct, sort_key
and sources to the subclass constructor. A constructor may leave out any of
distinct, sort_key and sources, and may give its first argument another
name, as long as that argument is required:
class Scores(Collection):
def __init__(self, scores, sources=None):
Collection.__init__(self, scores, sort_key=abs, sources=sources)
scores = Scores([-3, 1, 2], sources={"results.csv"})
high = scores.filter(lambda score: abs(score) > 1)
print(type(high).__name__, list(high), high.source)
Scores [2, -3] results.csv
A constructor with other required arguments must override
clone_with_new_elements(); otherwise cloning raises a TypeError which says
so.
A subclass whose constructor takes exactly elements, distinct, sort_key
and sources, for example one that inherits Collection.__init__, can be
pickled, copied and saved as JSON as it is. A subclass whose constructor takes other arguments
must implement its own to_dict() returning them; Collection.to_dict()
raises ValueError for it rather than return state its constructor cannot
accept.