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

Study view km plot optimization #4520

Merged
merged 2 commits into from
Apr 27, 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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/pages/groupComparison/Survival.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,7 @@ export default class Survival extends React.Component<ISurvivalProps, {}> {
this.selectedSurvivalPlotPrefix
]
}
compactMode={false}
/>
</div>
</div>
Expand Down
156 changes: 102 additions & 54 deletions src/pages/resultsView/survival/SurvivalChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
filterScatterData,
SurvivalPlotFilters,
SurvivalSummary,
SURVIVAL_COMPACT_MODE_THRESHOLD,
} from './SurvivalUtil';
import { toConditionalPrecision } from 'shared/lib/NumberUtils';
import { getPatientViewUrl } from '../../../shared/api/urls';
Expand Down Expand Up @@ -88,10 +89,13 @@ export interface ISurvivalChartProps {
legendLabelComponent?: any;
yAxisTickCount?: number;
xAxisTickCount?: number;
// Compact mode will hide censoring dots in the chart and do binning based on configuration
compactMode?: boolean;
}

const MIN_GROUP_SIZE_FOR_LOGRANK = 10;
// Start to down sampling when there are more than 1000 dots in the plot.
// TODO: 1000 samples is our current setting, but we should make this configurable
const SURVIVAL_DOWN_SAMPLING_THRESHOLD = 1000;

@observer
Expand Down Expand Up @@ -269,15 +273,30 @@ export default class SurvivalChart
// The filter is only available when user zooms in the plot.
@computed
get scatterData(): GroupedScatterData {
return filterScatterData(
this.unfilteredScatterData,
this.scatterFilter,
{
xDenominator: this.downSamplingDenominators.x,
yDenominator: this.downSamplingDenominators.y,
threshold: SURVIVAL_DOWN_SAMPLING_THRESHOLD,
}
);
if (this.props.compactMode) {
return filterScatterData(
this.unfilteredScatterData,
this.scatterFilter,
{
xDenominator: this.downSamplingDenominators.x,
yDenominator: this.downSamplingDenominators.y,
threshold: SURVIVAL_DOWN_SAMPLING_THRESHOLD,
enableCensoringCross: false,
floorTimeToMonth: true,
}
);
} else {
return filterScatterData(
this.unfilteredScatterData,
this.scatterFilter,
{
xDenominator: this.downSamplingDenominators.x,
yDenominator: this.downSamplingDenominators.y,
threshold: SURVIVAL_DOWN_SAMPLING_THRESHOLD,
enableCensoringCross: true,
}
);
}
}

public static defaultProps: Partial<ISurvivalChartProps> = {
Expand Down Expand Up @@ -790,6 +809,77 @@ export default class SurvivalChart
));
}

@computed get tooltipContent() {
return (
<div>
Patient ID:{' '}
<a
href={getPatientViewUrl(
this.tooltipModel.datum.studyId,
this.tooltipModel.datum.patientId
)}
target="_blank"
>
{this.tooltipModel.datum.patientId}
</a>
<br />
{!!this.props.showCurveInTooltip && [
`Curve: ${this.tooltipModel.datum.group}`,
<br />,
]}
{this.props.yLabelTooltip}:{' '}
{this.tooltipModel.datum.y.toFixed(2)}%<br />
{this.tooltipModel.datum.status
? this.props.xLabelWithEventTooltip
: this.props.xLabelWithoutEventTooltip}
: {this.tooltipModel.datum.x.toFixed(2)} months{' '}
{this.tooltipModel.datum.status ? '' : '(censored)'}
<br />
{this.props.analysisClinicalAttribute && (
<span>
{this.props.analysisClinicalAttribute.displayName}:{' '}
{
this.props.patientToAnalysisGroups[
this.tooltipModel.datum.uniquePatientKey
]
}
</span>
)}
<br />
Number of patients at risk: {this.tooltipModel.datum.atRisk}
</div>
);
}

@computed get compactTooltipContent() {
return (
<div>
Events during [{this.tooltipModel.datum.x},
{this.tooltipModel.datum.x + 1}) months
<br />
{this.tooltipModel.datum.numberOfEvents !== undefined && (
<>
Patients with an event:{' '}
{this.tooltipModel.datum.numberOfEvents}
</>
)}
<br />
{this.tooltipModel.datum.numberOfCensored !== undefined && (
<>
Censored patients:{' '}
{this.tooltipModel.datum.numberOfCensored}
</>
)}
<br />
<br />% event free at interval end:{' '}
{this.tooltipModel.datum.y.toFixed(2)}%
<br />
Patients at risk at interval end:{' '}
{this.tooltipModel.datum.atRisk}
</div>
);
}

public render() {
if (
_.flatten(_.values(this.props.sortedGroupedSurvivals)).length === 0
Expand Down Expand Up @@ -829,51 +919,9 @@ export default class SurvivalChart
onMouseEnter={this.tooltipMouseEnter}
onMouseLeave={this.tooltipMouseLeave}
>
<div>
Patient ID:{' '}
<a
href={getPatientViewUrl(
this.tooltipModel.datum.studyId,
this.tooltipModel.datum.patientId
)}
target="_blank"
>
{this.tooltipModel.datum.patientId}
</a>
<br />
{!!this.props.showCurveInTooltip && [
`Curve: ${this.tooltipModel.datum.group}`,
<br />,
]}
{this.props.yLabelTooltip}:{' '}
{this.tooltipModel.datum.y.toFixed(2)}%<br />
{this.tooltipModel.datum.status
? this.props.xLabelWithEventTooltip
: this.props.xLabelWithoutEventTooltip}
: {this.tooltipModel.datum.x.toFixed(2)} months{' '}
{this.tooltipModel.datum.status
? ''
: '(censored)'}
<br />
{this.props.analysisClinicalAttribute && (
<span>
{
this.props.analysisClinicalAttribute
.displayName
}
:{' '}
{
this.props.patientToAnalysisGroups[
this.tooltipModel.datum
.uniquePatientKey
]
}
</span>
)}
<br />
Number of patients at risk:{' '}
{this.tooltipModel.datum.atRisk}
</div>
{this.props.compactMode
? this.compactTooltipContent
: this.tooltipContent}
</Popover>
)}
{this.props.showTable && (
Expand Down
Loading