Skip to content

Commit

Permalink
Allow filtering of data in second tree
Browse files Browse the repository at this point in the history
Trees define traitNames and traitValues via their `node_attrs` in the
dataset JSON, for instance "traitName=country" and "traitValue=New
Zealand". We populate the filtering dropdowns using these, but had
previously only considered the main (LHS) tree. Behavioural changes:

* traitName & traitValue present on both trees: no change. Filtering
  works as expected.
* traitName on both trees, traitValue only on 2nd tree: functionality
  added here.
* traitName only present on 2nd tree: this now works, but the app
  crashes from an uncaught error in the filter badges shown in the
  header. This will be fixed in the next commit.

In addition, mutations and node ("sample") names unique to the 2nd tree
are added to the available filters.

Closes #1781
  • Loading branch information
jameshadfield committed Jun 12, 2024
1 parent 62b6dd2 commit 3165d95
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 8 deletions.
4 changes: 4 additions & 0 deletions src/actions/recomputeReduxState.js
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,10 @@ function instantiateSecondTree(secondTreeDataset, metadata, genomeMap, secondTre
treeToo.name = secondTreeName;
updateMetadataStateViaSecondTree({...metadata}, secondTreeDataset, genomeMap)

const secondTreeColorings = convertColoringsListToDict(secondTreeDataset.meta?.colorings || []);
const stateCountAttrs = gatherTraitNames(treeToo.nodes, secondTreeColorings);
treeToo.totalStateCounts = countTraitsAcrossTree(treeToo.nodes, stateCountAttrs, false, true);

/* TODO: calc & display num tips in 2nd tree */
// metadata.secondTreeNumTips = calcTotalTipsInTree(treeToo.nodes);

Expand Down
46 changes: 38 additions & 8 deletions src/components/controls/filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const DEBOUNCE_TIME = 200;
totalStateCounts: state.tree.totalStateCounts,
canFilterByGenotype: !!state.entropy.genomeMap,
nodes: state.tree.nodes,
nodesSecondTree: state.treeToo?.nodes,
totalStateCountsSecondTree: state.treeToo?.totalStateCounts,
measurementsFieldsMap: state.measurements.collectionToDisplay.fields,
measurementsFiltersMap: state.measurements.collectionToDisplay.filters,
measurementsFilters: state.controls.measurementsFilters
Expand Down Expand Up @@ -66,16 +68,24 @@ class FilterData extends React.Component {
* colorings). Within each trait, the values are alphabetical
*/
const coloringKeys = Object.keys(this.props.colorings||{});
const unorderedTraitNames = Object.keys(this.props.totalStateCounts);
const unorderedTraitNames = [
...Object.keys(this.props.totalStateCounts),
...Object.keys(this.props.totalStateCountsSecondTree),
]
const traitNames = [
...coloringKeys.filter((name) => unorderedTraitNames.includes(name)),
...unorderedTraitNames.filter((name) => !coloringKeys.includes(name))
]
for (const traitName of traitNames) {
const traitData = this.props.totalStateCounts[traitName];
const traitData = new Set([
...(this.props.totalStateCounts[traitName]?.keys() || []),
...(this.props.totalStateCountsSecondTree?.[traitName]?.keys() || []),
]);

this.props.totalStateCounts[traitName];
const traitTitle = this.getFilterTitle(traitName);
const filterValuesCurrentlyActive = new Set((this.props.activeFilters[traitName] || []).filter((x) => x.active).map((x) => x.value));
for (const traitValue of Array.from(traitData.keys()).sort()) {
for (const traitValue of Array.from(traitData).sort()) {
if (filterValuesCurrentlyActive.has(traitValue)) continue;
options.push({
label: `${traitTitle}${traitValue}`,
Expand All @@ -89,7 +99,12 @@ class FilterData extends React.Component {
* mutations
*/
if (this.props.canFilterByGenotype) {
Array.from(collectGenotypeStates(this.props.nodes))
const observedGenotypes = collectGenotypeStates(this.props.nodes); // set of "nuc:123A", "S:418K", etc
const observedGenotypesSecondTree = this.props.nodesSecondTree ?
collectGenotypeStates(this.props.nodesSecondTree).difference(observedGenotypes) :
new Set();
Array.from(observedGenotypes)
.concat(Array.from(observedGenotypesSecondTree))
.sort()
.forEach((o) => {
options.push({
Expand All @@ -99,14 +114,29 @@ class FilterData extends React.Component {
});
}

this.props.nodes
/**
* Add all (terminal) node names, calling each a "sample"
*/
const sampleNames = this.props.nodes
.filter((n) => !n.hasChildren)
.forEach((n) => {
.map((n) => n.name);
sampleNames.forEach((name) => {
options.push({
label: `sample → ${n.name}`,
value: [strainSymbol, n.name]
label: `sample → ${name}`,
value: [strainSymbol, name]
});
});
if (this.props.nodesSecondTree) {
const seenNames = new Set(sampleNames);
this.props.nodesSecondTree
.filter((n) => !n.hasChildren && !seenNames.has(n.name))
.forEach((n) => {
options.push({
label: `sample → ${n.name}`,
value: [strainSymbol, n.name]
});
});
}

if (this.props.measurementsOn && this.props.measurementsFiltersMap && this.props.measurementsFieldsMap) {
this.props.measurementsFiltersMap.forEach(({values}, filterField) => {
Expand Down

0 comments on commit 3165d95

Please sign in to comment.