@causalmap/filter-engine
v0.1.2
Published
Pure, dependency-free link-filter engine for causal maps: ordered structural filters (tags, label, path-tracing, frequency, combine-opposites, zoom/collapse and more), data in and data out.
Maintainers
Readme
@causalmap/filter-engine
Pure, dependency-free link-filter engine for causal maps. Give it a list of links and an ordered list of filter configs; get the filtered links back. No DOM, no database, no framework. Plain ES modules, Node 18+ or any modern browser.
This is the filtering core extracted from Causal Map. The code under
src/ is a snapshot of the app's canonical engine, so its behaviour tracks the live product.
Licence
Source-available under the Functional Source License 1.1 (FSL-1.1-MIT): free to use, copy, modify and redistribute for any purpose except a competing use, that is, putting it into a product or service that substitutes for the Software or for Causal Map. Internal use, education, research and professional services are all permitted. Each release converts to the MIT licence two years after it is published. This is source-available rather than an OSI open-source licence until that conversion.
Install
npm install @causalmap/filter-engineQuick start
import { applyOrderedLinkFilters } from '@causalmap/filter-engine'
const links = [
{ cause: 'training', effect: 'confidence', source_id: 's1', tags: 'outcome' },
{ cause: 'confidence', effect: 'income', source_id: 's1', tags: 'impact' },
{ cause: 'training', effect: 'income', source_id: 's2', tags: 'impact' },
]
const filters = [
{ type: 'tags', selectedTags: ['impact'] },
{ type: 'exclude-self-loops' },
]
const { rows, unsupported } = applyOrderedLinkFilters(links, filters /*, sources */)
// rows = the surviving links, in order; unsupported = any filter types this build does not handleapplyOrderedLinkFilters(links, filters, sources = []) runs the filters in array order and returns
{ rows, unsupported }. The optional third argument is the sources table (objects with at least
id); it is only needed by filters that read source metadata.
The link object
A link is a plain object. The fields the engine reads:
cause,effect: factor labels (strings). The core of every link.source_id: which source the link came from; used by frequency and path-tracing counts.tags: string (comma-separated) or array, used by the tag filters.- any other column you attach (for example
sentiment, or flattened source-group columns): read by theeverythingandsource-groupsfilters via a resolved field name.
Extra fields you put on a link are preserved through filtering.
Filters
Each filter is { type, ...options }. Set enabled: false to skip one. Supported types:
| type | what it does | key options |
|------|--------------|-------------|
| label | keep links within N steps of factors matching a label | selectedLabels, stepsUp, stepsDown, traceThreads |
| exclude-label | drop links whose cause/effect match labels | selectedLabels, matchMode, excludeAny |
| tags | keep links matching any selected tag | selectedTags, matchMode |
| exclude-tags | drop links matching selected tags | selectedTags, matchMode, excludeAny |
| path-tracing | keep links on paths between two factor sets | see path tracing below |
| link-frequency | keep the most or least frequent cause to effect pairs | which, mode, threshold |
| factor-frequency | keep links whose factors are frequent enough | which, mode, threshold |
| combine-opposites | merge negative variants into their positive label | sets flipped_* flags |
| zoom | trim factor labels to a hierarchy level | level |
| collapse | merge a set of labels into one | selectedLabels |
| replace-brackets | rewrite bracketed label parts | |
| source-groups | membership over a resolved source-group column | selectedField, selectedValues |
| everything | membership over any resolved link column | selectedField, selectedValues |
| exclude-self-loops | drop links where cause equals effect | |
matchMode is 'start' (default), 'anywhere', or 'exact'. Frequency mode is 'top'
(default, keep the top threshold by count) or 'minimum' (keep count at or above threshold);
which is 'sources' to count unique sources, otherwise it counts appearances.
Per-filter functions are also exported if you want to drive them yourself
(applyTagsFilter, applyLinkFrequencyFilter, applyPathTracingFilter, and so on).
Path tracing
import { applyPathTracingFilter } from '@causalmap/filter-engine'Keeps links lying on paths from one factor set to another within a step budget. Highlight helpers
matchPathFactors and tracePaths are exported alongside it.
Reserved output fields
Some filters write fields onto the returned links. Do not strip these if you render downstream:
_recoded:{ cause: { original, recoded }, effect: {...} }, set by zoom/collapse/replace-brackets; the earliest original is preserved. Used to display a renamed factor._recoded_cause/_recoded_effect: booleans, set by collapse and replace-brackets only.flipped_cause/flipped_effect: booleans, set by combine-opposites and read by link-frequency bundling.
Host-resolved inputs
A few filters need a value the host application resolves and supplies:
everything/source-groupsreadlink[filter.selectedField], so setselectedFieldto a real column before running (the app resolves friendly names to columns likesentimentors_region).link-frequencybundling can separate flipped opposite pairs. PassapplyLinkFrequencyFilter(links, filter, { separateFlippedBundles })when driving it directly. It defaults tofalse, so a consumer without combine-opposites needs nothing.
Documentation
These filters are the engine behind the Causal Map app. To see what they do in context:
- Answering questions: individual questions: the analysis questions these filters answer (main outcomes, comparing groups, path tracing, and more).
- How to, in the Causal Map app: step-by-step guides for using the filters in the app.
- The Causal Map app: the product the engine powers.
Provenance
Canonical source: webapp/js/filter-engine in the Causal Map app. src/ here is generated by
sync-from-webapp.mjs and regenerated on every publish. The test suite under tests/ (run with
npm test) includes the app's differential harnesses, which check this engine against a faithful
copy of the app's filter logic across thousands of configurations.
