Skip to content

Commit

Permalink
Group nuc mutations on-hover
Browse files Browse the repository at this point in the history
Grouping nucleotide mutations (as per previous commits) while hovering
on branches is really helpful for understanding the tree. The penalty
is the use of more space, and so I've chosen to not group AA mutations
here; one can shift+click on the branch to see these grouped if needed.
  • Loading branch information
jameshadfield committed Feb 14, 2022
1 parent 9971471 commit 69b53a0
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 27 deletions.
54 changes: 27 additions & 27 deletions src/components/tree/infoPanels/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { numericToCalendar } from "../../../util/dateHelpers";
import { getTipColorAttribute } from "../../../util/colorHelpers";
import { isColorByGenotype, decodeColorByGenotype } from "../../../util/getGenotype";
import { getTraitFromNode, getDivFromNode, getVaccineFromNode,
getFullAuthorInfoFromNode, getTipChanges } from "../../../util/treeMiscHelpers";
getFullAuthorInfoFromNode, getTipChanges, getBranchMutations } from "../../../util/treeMiscHelpers";
import { isValueValid } from "../../../util/globals";
import { formatDivergence, getIdxOfInViewRootNode } from "../phyloTree/helpers";
import { parseIntervalsOfNsOrGaps } from "./MutationTable";

const InfoLine = ({name, value, padBelow=false}) => {
const renderValues = () => {
Expand Down Expand Up @@ -173,41 +174,39 @@ const TipMutations = ({node, t}) => {
* @param {Object} props
* @param {Object} props.node branch node which is currently highlighted
* @param {Object} props.geneSortFn function to sort a list of genes
* @param {Object} props.observedMutations counts of all observed mutations across the tree
*/
const BranchMutations = ({node, geneSortFn, t}) => {
const BranchMutations = ({node, geneSortFn, observedMutations, t}) => {
if (!node.branch_attrs || !node.branch_attrs.mutations) return null;
const elements = []; // elements to render
const mutations = node.branch_attrs.mutations;

/* --------- NUCLEOTIDE MUTATIONS --------------- */
/* Nt mutations are found at `mutations.nuc` -> Array of strings */
if (mutations.nuc && mutations.nuc.length) {
const nDisplay = 9; // max number of mutations to display

const isMutGap = (mut) => mut.slice(-1) === "-" || mut.slice(0, 1) === "-";
const isMutUnknown = (mut) => mut.slice(-1) === "N" || mut.slice(0, 1) === "N";
const categorisedMutations = getBranchMutations(node, observedMutations);

// gather muts which aren't to/from a gap or a "N"
const nucs = mutations.nuc.filter((mut) => (!isMutGap(mut) && !(isMutUnknown(mut))));
const nucLen = nucs.length; // number of mutations that exist without N/-
const subset = (muts, maxNum) =>
muts.slice(0, Math.min(maxNum, muts.length)).join(", ") +
(muts.length > maxNum ? ` + ${muts.length-maxNum} more` : '');

let m = nucs.slice(0, Math.min(nDisplay, nucLen)).join(", ");
if (nucLen > nDisplay) {
m += " + " + t("{{x}} more", {x: nucLen - nDisplay});
/* --------- NUCLEOTIDE MUTATIONS --------------- */
if (categorisedMutations.nuc) {
const nDisplay = 5; // max number of mutations to display per category
if (categorisedMutations.nuc.unique.length) {
elements.push(<InfoLine name='Unique Nucleotide mutations:' value={subset(categorisedMutations.nuc.unique, nDisplay)} key="nuc_unique"/>);
}

if (nucLen !== 0) {
elements.push(<InfoLine name={t("Nucleotide mutations")+":"} value={m} key="nuc"/>);
if (categorisedMutations.nuc.homoplasies.length) {
elements.push(<InfoLine name='Homoplasic mutations:' value={subset(categorisedMutations.nuc.homoplasies, nDisplay)} key="nuc_homoplasies"/>);
}

const nGapMutations = mutations.nuc.filter((mut) => isMutGap(mut)).length;
if (nGapMutations) {
elements.push(<InfoLine name={`${t("Gaps")} ("-"):`} value={nGapMutations} key="gaps"/>);
if (categorisedMutations.nuc.reversionsToRoot.length) {
elements.push(<InfoLine name='Reversions to Root:' value={categorisedMutations.nuc.reversionsToRoot.length} key="nuc_rtr"/>);
}

const nUnknownMutations = mutations.nuc.filter((mut) => isMutUnknown(mut)).length;
if (nUnknownMutations) {
elements.push(<InfoLine name='Ns:' value={nUnknownMutations} key="Ns"/>);
if (categorisedMutations.nuc.gaps.length) {
const value = `${parseIntervalsOfNsOrGaps(categorisedMutations.nuc.gaps).length} regions, ${categorisedMutations.nuc.gaps.length}bp.`;
elements.push(<InfoLine name='Gaps:' value={value} key="nuc_gaps"/>);
}
if (categorisedMutations.nuc.ns.length) {
const value = `${parseIntervalsOfNsOrGaps(categorisedMutations.nuc.ns).length} regions, ${categorisedMutations.nuc.ns.length}bp.`;
elements.push(<InfoLine name='Ns:' value={value} key="nuc_ns"/>);
}
} else {
elements.push(<InfoLine name={t("No nucleotide mutations")} value="" key="nuc"/>);
Expand Down Expand Up @@ -390,6 +389,7 @@ const HoverInfoPanel = ({
panelDims,
colorings,
geneSortFn,
observedMutations,
t
}) => {
if (selectedNode.event !== "hover") return null;
Expand All @@ -411,7 +411,7 @@ const HoverInfoPanel = ({
) : (
<>
<BranchDescendents node={node} t={t}/>
<BranchMutations node={node} geneSortFn={geneSortFn} t={t}/>
<BranchMutations node={node} geneSortFn={geneSortFn} observedMutations={observedMutations} t={t}/>
<BranchLength node={node} t={t}/>
<ColorBy node={node} colorBy={colorBy} colorByConfidence={colorByConfidence} colorScale={colorScale} colorings={colorings}/>
<Comment>
Expand Down
1 change: 1 addition & 0 deletions src/components/tree/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ class Tree extends React.Component {
colorScale={this.props.colorScale}
colorings={this.props.metadata.colorings}
geneSortFn={this.state.geneSortFn}
observedMutations={this.props.tree.observedMutations}
panelDims={{width: this.props.width, height: this.props.height, spaceBetweenTrees}}
t={t}
/>
Expand Down

0 comments on commit 69b53a0

Please sign in to comment.