Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature 469 volcano infinity #497

Merged
merged 6 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 73 additions & 22 deletions packages/libs/components/src/plots/VolcanoPlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,16 @@ export interface VolcanoPlotProps {
showSpinner?: boolean;
/** used to determine truncation logic */
rawDataMinMaxValues: RawDataMinMaxValues;
/** The maximum possible y axis value. Points with pValue=0 will get plotted at -log10(minPValueCap). */
minPValueCap?: number;
}

const EmptyVolcanoPlotData: VolcanoPlotData = [
{ log2foldChange: '0', pValue: '1' },
];

const MARGIN_DEFAULT = 50;

interface TruncationRectangleProps {
x1: number;
x2: number;
Expand Down Expand Up @@ -134,6 +138,7 @@ function VolcanoPlot(props: VolcanoPlotProps, ref: Ref<HTMLDivElement>) {
truncationBarFill,
showSpinner = false,
rawDataMinMaxValues,
minPValueCap = 2e-300,
} = props;

// Use ref forwarding to enable screenshotting of the plot for thumbnail versions.
Expand All @@ -155,42 +160,33 @@ function VolcanoPlot(props: VolcanoPlotProps, ref: Ref<HTMLDivElement>) {
const { min: dataYMin, max: dataYMax } = rawDataMinMaxValues.y;

// Set mins, maxes of axes in the plot using axis range props
// The y axis max should not be allowed to exceed -log10(minPValueCap)
const xAxisMin = independentAxisRange?.min ?? 0;
const xAxisMax = independentAxisRange?.max ?? 0;
const yAxisMin = dependentAxisRange?.min ?? 0;
const yAxisMax = dependentAxisRange?.max ?? 0;
const yAxisMax = dependentAxisRange?.max
? dependentAxisRange.max > -Math.log10(minPValueCap)
? -Math.log10(minPValueCap)
: dependentAxisRange.max
: 0;

/**
* Accessors - tell visx which value of the data point we should use and where.
*/

// For the actual volcano plot data
const dataAccessors = {
xAccessor: (d: VolcanoPlotDataPoint) => Number(d?.log2foldChange),
yAccessor: (d: VolcanoPlotDataPoint) => -Math.log10(Number(d?.pValue)),
};

// For all other situations where we need to access point values. For example
// threshold lines and annotations.
const xyAccessors = {
xAccessor: (d: VisxPoint) => {
return d?.x;
},
yAccessor: (d: VisxPoint) => {
return d?.y;
},
};
// Do we need to show the special annotation for the case when the y axis is maxxed out?
const showCappedDataAnnotation = yAxisMax === -Math.log10(minPValueCap);

// Truncation indicators
// If we have truncation indicators, we'll need to expand the plot range just a tad to
// ensure the truncation bars appear. The folowing showTruncationBar variables will
// be either 0 (do not show bar) or 1 (show bar).
// The y axis has special logic because it gets capped at -log10(minPValueCap)
const showXMinTruncationBar = Number(dataXMin < xAxisMin);
const showXMaxTruncationBar = Number(dataXMax > xAxisMax);
const xTruncationBarWidth = 0.02 * (xAxisMax - xAxisMin);

const showYMinTruncationBar = Number(-Math.log10(dataYMax) < yAxisMin);
const showYMaxTruncationBar = Number(-Math.log10(dataYMin) > yAxisMax);
const showYMaxTruncationBar =
dataYMin === 0
? Number(-Math.log10(minPValueCap) > yAxisMax)
: Number(-Math.log10(dataYMin) > yAxisMax);
const yTruncationBarHeight = 0.02 * (yAxisMax - yAxisMin);

/**
Expand All @@ -205,6 +201,30 @@ function VolcanoPlot(props: VolcanoPlotProps, ref: Ref<HTMLDivElement>) {
-Math.log10(Number(significanceThreshold)) > yAxisMin &&
-Math.log10(Number(significanceThreshold)) < yAxisMax;

/**
* Accessors - tell visx which value of the data point we should use and where.
*/

// For the actual volcano plot data. Y axis points are capped at -Math.log10(minPValueCap)
const dataAccessors = {
xAccessor: (d: VolcanoPlotDataPoint) => Number(d?.log2foldChange),
yAccessor: (d: VolcanoPlotDataPoint) =>
d.pValue === '0'
? -Math.log10(minPValueCap)
: -Math.log10(Number(d?.pValue)),
};

// For all other situations where we need to access point values. For example
// threshold lines and annotations.
const xyAccessors = {
xAccessor: (d: VisxPoint) => {
return d?.x;
},
yAccessor: (d: VisxPoint) => {
return d?.y;
},
};

return (
// Relative positioning so that tooltips are positioned correctly (tooltips are positioned absolutely)
<div
Expand Down Expand Up @@ -238,6 +258,12 @@ function VolcanoPlot(props: VolcanoPlotProps, ref: Ref<HTMLDivElement>) {
zero: false,
}}
findNearestDatumOverride={findNearestDatumXY}
margin={{
top: MARGIN_DEFAULT,
right: showCappedDataAnnotation ? 150 : MARGIN_DEFAULT,
left: MARGIN_DEFAULT,
bottom: MARGIN_DEFAULT,
}}
>
{/* Set up the axes and grid lines. XYChart magically lays them out correctly */}
<Grid numTicks={6} lineStyle={gridStyles} />
Expand Down Expand Up @@ -318,6 +344,31 @@ function VolcanoPlot(props: VolcanoPlotProps, ref: Ref<HTMLDivElement>) {
</>
)}

{/* infinity y data annotation line */}
{showCappedDataAnnotation && (
<Annotation
datum={{
x: xAxisMax,
y: yAxisMax + showYMaxTruncationBar * yTruncationBarHeight,
}}
{...xyAccessors}
>
<AnnotationLineSubject
{...thresholdLineStyles}
orientation="horizontal"
/>
<AnnotationLabel
title={'Values above this line are capped'}
titleFontWeight={200}
titleFontSize={12}
horizontalAnchor="start"
verticalAnchor="middle"
showAnchorLine={false}
showBackground={false}
/>
</Annotation>
)}

{/* The data itself */}
{/* Wrapping in a group in order to change the opacity of points. The GlyphSeries is somehow
a bunch of glyphs which are <circles> so there should be a way to pass opacity
Expand Down
29 changes: 26 additions & 3 deletions packages/libs/components/src/stories/plots/VolcanoPlot.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ const dataSetVolcano: VEuPathDBVolcanoPlotData = {
'-8',
'-4',
'-3',
'-8.2',
'7',
],
pValue: [
'0.001',
Expand All @@ -63,9 +65,26 @@ const dataSetVolcano: VEuPathDBVolcanoPlotData = {
'0.001',
'0.0001',
'0.002',
'0',
'0',
],
adjustedPValue: ['0.01', '0.001', '0.01', '0.001', '0.02', '0', '0'],
pointID: [
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'buzz',
'lightyear',
],
adjustedPValue: ['0.01', '0.001', '0.01', '0.001', '0.02'],
pointID: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'],
},
};

Expand Down Expand Up @@ -112,7 +131,7 @@ const Template: Story<TemplateProps> = (args) => {
})
.map((d) => ({
...d,
pointID: d.pointID ? [d.pointID] : undefined,
pointIDs: d.pointID ? [d.pointID] : undefined,
significanceColor: assignSignificanceColor(
Number(d.log2foldChange),
Number(d.pValue),
Expand Down Expand Up @@ -196,6 +215,10 @@ ManyPoints.args = {
significanceThreshold: 0.01,
independentAxisRange: { min: -9, max: 9 },
dependentAxisRange: { min: 0, max: 9 },
comparisonLabels: [
'up in super long group name',
'up in other long group name',
],
};

// Test truncation indicators
Expand Down
Loading