See also:
rank_by. Thesubsetoperator's declarative top-k mode (subset(ranking_key=…, k=…)) is one of the canonical motivating cases for ordering. The newerrank_byoperator generalizes it: any top-k expressed viasubset(ranking_key=…, k=k)can equivalently be expressed asrank_by(...) | filter_keys(k < k_max). Both stay in the codebase —subsetfor the declarative one-shot form,rank_bywhen you want a ranked AF that you can compose further (e.g. for median, percentile lookups). For simple pagination (page N of size k)subset(ranking_key=…, k=k, offset=n*k)is the most direct expression; for multi-step pipelines that reuse the ranked AF, preferrank_by. See rank.md for the FDM-faithful framing.
output: AF = subset(input: AF)
Computes a subset of the items contained in the input AF and returns a new output AF.
In contrast to filter, the subset operator computes a condition based on a global condition.
In other words:
filter operator: uses a predicate phrased against the individual items in the input AF (or just the keys or just the values). Such predicate may be evaluated for each item independently. For instance, a condition like
foo==42may be computed for eachfooindividually without influencing the outcome of other predicates.vs
subset operator: uses a predicate phrased against all items in the input AF. Such predicate cannot be evaluated for each item independently. For instance, a condition like 'k-smallest items with respect to their foo value' cannot be evaluated independently: the outcome depends on the other items present in the input AF.
Provide ranking_key and k (and optionally reverse and offset).
from fdm.attribute_functions import RF, TF
from fql.operators.subsets import subset
users: RF = RF({
1: TF({"name": "Horst", "yob": 1972}),
2: TF({"name": "Tom", "yob": 1983}),
3: TF({"name": "John", "yob": 2003}),
})
# k-smallest: youngest 2 users
top2 = subset(users, ranking_key=lambda i: i.value.yob, k=2).result
# → { 1: Horst(1972), 2: Tom(1983) }
# reverse=True: 2 oldest users
bottom2 = subset(users, ranking_key=lambda i: i.value.yob, k=2, reverse=True).result
# → { 3: John(2003), 2: Tom(1983) }Internally: all items are sorted by ranking_key, then the slice
sorted_items[offset : offset + k] is returned. Original keys are
preserved in the output AF.
| Parameter | Type | Default | Description |
|---|---|---|---|
ranking_key |
Callable[[Item], Any] |
- | Maps each Item to a comparable value used for sorting. |
k |
int |
- | Number of items to keep. Must be ≥ 1. |
offset |
int |
0 |
Number of items to skip from the start of the sorted list. Must be ≥ 0. Only valid in top-k mode. |
reverse |
bool |
False |
If True, sort descending (largest first). |
output_factory |
Callable |
None |
Factory for the output AF. Defaults to type(input_function)(). |
offset lets you retrieve a specific page of the sorted result without
materialising a ranked AF first:
PAGE_SIZE = 2
# page 1: items 0..1 (positions 0-based)
page1 = subset(users, ranking_key=lambda i: i.value.yob, k=PAGE_SIZE, offset=0).result
# page 2: items 2..3
page2 = subset(users, ranking_key=lambda i: i.value.yob, k=PAGE_SIZE, offset=PAGE_SIZE).resultIf offset is at or beyond the length of the AF, the result is an empty
AF (no error is raised - Python slice semantics). If offset + k exceeds
the length, fewer than k items are returned.
The convenience methods AttributeFunction.top() and .bottom() also
expose offset:
page2_top = users.top(k=2, key=lambda i: i.value.yob, offset=2)Provide subset_predicate: a callable that receives the entire input AF
and returns a new AF containing only the qualifying items. This covers
arbitrary global conditions that cannot be decomposed per item.
def above_mean(af: RF) -> RF:
all_yobs = [item.value.yob for item in af]
mean_yob = sum(all_yobs) / len(all_yobs)
return af.where(lambda item: item.value.yob > mean_yob)
result = subset(users, subset_predicate=above_mean).result
# mean of {1972, 1983, 2003} = 1986 → only John (2003) survivesoffset is not available in predicate mode (pass it and a
ValueError is raised immediately).
| Parameter | Type | Default | Description |
|---|---|---|---|
subset_predicate |
Callable[[INPUT_AF], OUTPUT_AF] |
- | Receives the full input AF, returns a subset AF. |
output_factory |
Callable |
None |
Factory for the output AF. Defaults to type(input_function)(). |
Select the attributes to work on based on a global condition.
output: TF = subset(input: TF)
Computes a subset of the items (e.g. key/value-mappings) mapped to in the input TF.
For instance, this could be used to compute a subset of a tuple, i.e. 'give me the k-smallest items present in that tuple w.r.t. the condition specified'.
Select the tuples to work on based on a global condition.
output: RF = subset(input: RF)
Computes a subset of the items (e.g. key/TF-mappings) mapped to in the input RF. Semantically equivalent to a
top-k operator in extended relational algebra or simulating the same thing in SQL using ORDER BY and LIMIT.
Note that for k=1, this operation is equivalent to a classical min or max-aggregation (but not mean, avg, median, count as they compute a new value that does not have to exist in the input RF, see aggregate).
For instance, this could be used to compute the subset of tuples of a given relation, i.e. 'give me the k-smallest tuples based on the condition specified'.
Select the relations to work on based on a global condition.
output: DBF = subset(input: DBF)
Computes a subset of the items (e.g. key/RF-mappings) mapped to in the input DBF.
For instance, this could be used to compute the subset of relations of a given database, i.e. 'give me the k-smallest relations based on the condition specified'.
Select the databases to work on based on a global condition.
output: SDBF = subset(input: SDBF)
Computes a subset of the items (e.g. key/DBF-mappings) mapped to in the input SDBF.
For instance, this could be used to compute the subset of databases of a given set of databases, i.e. 'give me the k-smallest databases based on the condition specified'.