use of qupath.lib.measurements.MeasurementList in project qupath by qupath.
the class DelaunayTriangulation method addClusterMeasurements.
/**
* Compute mean measurements from clustering all connected objects.
*/
public void addClusterMeasurements() {
if (nodeMap == null || nodeMap.isEmpty())
return;
List<Set<PathObject>> clusters = getConnectedClusters();
String key = "Cluster ";
List<String> measurementNames = new ArrayList<>();
for (String s : PathClassifierTools.getAvailableFeatures(nodeMap.keySet())) {
if (!s.startsWith(key))
measurementNames.add(s);
}
RunningStatistics[] averagedMeasurements = new RunningStatistics[measurementNames.size()];
Set<String> missing = new LinkedHashSet<>();
for (Set<PathObject> cluster : clusters) {
for (int i = 0; i < averagedMeasurements.length; i++) averagedMeasurements[i] = new RunningStatistics();
// Arrays.fill(averagedMeasurements, 0);
int n = cluster.size();
for (PathObject pathObject : cluster) {
MeasurementList ml = pathObject.getMeasurementList();
for (int i = 0; i < measurementNames.size(); i++) {
String name = measurementNames.get(i);
double val = ml.getMeasurementValue(name);
if (Double.isFinite(val)) {
averagedMeasurements[i].addValue(val);
} else
missing.add(name);
}
}
for (PathObject pathObject : cluster) {
MeasurementList ml = pathObject.getMeasurementList();
for (int i = 0; i < measurementNames.size(); i++) {
ml.putMeasurement(key + "mean: " + measurementNames.get(i), averagedMeasurements[i].getMean());
}
ml.putMeasurement(key + "size", n);
ml.close();
}
}
if (!missing.isEmpty()) {
logger.warn("Some objects have missing measurements! Statistics will calculated only for objects with measurements available.");
logger.warn("Missing measurements: {}", missing);
// System.err.println("Missing measurements will be ignored! " + nMissing);
}
}
use of qupath.lib.measurements.MeasurementList in project qupath by qupath.
the class TMASummaryViewer method updateSurvivalCurves.
private void updateSurvivalCurves() {
String colID = null;
String colScore = null;
colCensored = null;
for (String nameOrig : model.getAllNames()) {
if (nameOrig.equals(TMACoreObject.KEY_UNIQUE_ID))
colID = nameOrig;
else // else if (nameOrig.equals(TMACoreObject.KEY_CENSORED))
// colCensored = nameOrig;
// else if (!Number.class.isAssignableFrom())
// continue;
{
if (nameOrig.trim().length() == 0 || !model.getMeasurementNames().contains(nameOrig))
continue;
String name = nameOrig.toLowerCase();
if (name.equals("h-score"))
colScore = nameOrig;
else if (name.equals("positive %") && colScore == null)
colScore = nameOrig;
}
}
// Check for a column with the exact requested name
String colCensoredRequested = null;
String colSurvival = getSurvivalColumn();
if (colSurvival != null) {
colCensoredRequested = getRequestedSurvivalCensoredColumn(colSurvival);
if (model.getAllNames().contains(colCensoredRequested))
colCensored = colCensoredRequested;
else // Check for a general 'censored' column... less secure since it doesn't specify OS or RFS (but helps with backwards-compatibility)
if (model.getAllNames().contains("Censored")) {
logger.warn("Correct censored column for \"{}\" unavailable - should be \"{}\", but using \"Censored\" column instead", colSurvival, colCensoredRequested);
colCensored = "Censored";
}
}
if (colCensored == null && colSurvival != null) {
logger.warn("Unable to find censored column - survival data will be uncensored");
} else
logger.info("Survival column: {}, Censored column: {}", colSurvival, colCensored);
colScore = comboMainMeasurement.getSelectionModel().getSelectedItem();
if (colID == null || colSurvival == null || colCensored == null) {
// Adjust priority depending on whether we have any data at all..
if (!model.getItems().isEmpty())
logger.warn("No survival data found!");
else
logger.trace("No entries or survival data available");
return;
}
// Generate a pseudo TMA core hierarchy
Map<String, List<TMAEntry>> scoreMap = createScoresMap(model.getItems(), colScore, colID);
// System.err.println("Score map size: " + scoreMap.size() + "\tEntries: " + model.getEntries().size());
List<TMACoreObject> cores = new ArrayList<>(scoreMap.size());
double[] scores = new double[15];
for (Entry<String, List<TMAEntry>> entry : scoreMap.entrySet()) {
TMACoreObject core = new TMACoreObject();
core.setName("ID: " + entry.getKey());
MeasurementList ml = core.getMeasurementList();
Arrays.fill(scores, Double.POSITIVE_INFINITY);
List<TMAEntry> list = entry.getValue();
// Increase array size, if needed
if (list.size() > scores.length)
scores = new double[list.size()];
for (int i = 0; i < list.size(); i++) {
scores[i] = model.getNumericValue(list.get(i), colScore);
// scores[i] = list.get(i).getMeasurement(colScore).doubleValue();
}
Arrays.sort(scores);
int n = list.size();
double score;
if (n % 2 == 1)
score = scores[n / 2];
else
score = (scores[n / 2 - 1] + scores[n / 2]) / 2;
core.putMetadataValue(TMACoreObject.KEY_UNIQUE_ID, entry.getKey());
// System.err.println("Putting: " + list.get(0).getMeasurement(colSurvival).doubleValue() + " LIST: " + list.size());
ml.putMeasurement(colSurvival, list.get(0).getMeasurementAsDouble(colSurvival));
ml.putMeasurement(colCensoredRequested, list.get(0).getMeasurementAsDouble(colCensored));
if (colScore != null)
ml.putMeasurement(colScore, score);
cores.add(core);
// logger.info(entry.getKey() + "\t" + score);
}
TMAGrid grid = DefaultTMAGrid.create(cores, 1);
PathObjectHierarchy hierarchy = new PathObjectHierarchy();
hierarchy.setTMAGrid(grid);
kmDisplay.setHierarchy(hierarchy, colSurvival, colCensoredRequested);
kmDisplay.setScoreColumn(comboMainMeasurement.getSelectionModel().getSelectedItem());
// new KaplanMeierPlotTMA.KaplanMeierDisplay(hierarchy, colScore).show(frame, colScore);
}
use of qupath.lib.measurements.MeasurementList in project qupath by qupath.
the class KaplanMeierDisplay method generatePlot.
@SuppressWarnings("unchecked")
private void generatePlot() {
KaplanMeierDisplay.ScoreData newScoreData = scoreData;
// If we have a hierarchy, update the scores with the most recent data
if (hierarchy != null) {
List<TMACoreObject> cores = PathObjectTools.getTMACoreObjects(hierarchy, false);
double[] survival = new double[cores.size()];
boolean[] censored = new boolean[cores.size()];
double[] scores = new double[cores.size()];
// scoreColumn = "RoughScore";
for (int i = 0; i < cores.size(); i++) {
TMACoreObject core = cores.get(i);
MeasurementList ml = core.getMeasurementList();
survival[i] = core.getMeasurementList().getMeasurementValue(survivalColumn);
double censoredValue = core.getMeasurementList().getMeasurementValue(censoredColumn);
boolean hasCensoredValue = !Double.isNaN(censoredValue) && (censoredValue == 0 || censoredValue == 1);
censored[i] = censoredValue != 0;
if (!hasCensoredValue) {
// If we don't have a censored value, ensure we mask out everything else
scores[i] = Double.NaN;
survival[i] = Double.NaN;
} else if (ml.containsNamedMeasurement(scoreColumn))
// Get the score if we can
scores[i] = ml.getMeasurementValue(scoreColumn);
else {
// // Try to compute score if we need to
// Map<String, Number> map = ROIMeaningfulMeasurements.getPathClassSummaryMeasurements(core.getChildObjects(), true);
// Number value = map.get(scoreColumn);
// if (value == null)
scores[i] = Double.NaN;
// else
// scores[i] = value.doubleValue();
}
}
// Mask out any scores that don't have associated survival data
for (int i = 0; i < survival.length; i++) {
if (Double.isNaN(survival[i]))
scores[i] = Double.NaN;
}
newScoreData = new ScoreData(scores, survival, censored);
}
if (newScoreData == null || newScoreData.scores.length == 0)
return;
// KaplanMeier kmHigh = new KaplanMeier("Above threshold");
// KaplanMeier kmLow = new KaplanMeier("Below threshold");
double[] quartiles = StatisticsHelper.getQuartiles(newScoreData.scores);
double q1 = quartiles[0];
double median = quartiles[1];
double q3 = quartiles[2];
double[] thresholds;
if (params != null) {
Object thresholdMethod = params.getChoiceParameterValue("scoreThresholdMethod");
if (thresholdMethod.equals("Median")) {
// panelParams.setNumericParameterValue("scoreThreshold", median);
// ((DoubleParameter)params.getParameters().get("scoreThreshold")).setValue(median); // TODO: UPDATE DIALOG!
thresholds = new double[] { median };
} else if (thresholdMethod.equals("Tertiles")) {
// ((DoubleParameter)params.getParameters().get("scoreThreshold")).setValue(median); // TODO: UPDATE DIALOG!
thresholds = StatisticsHelper.getTertiles(newScoreData.scores);
} else if (thresholdMethod.equals("Quartiles")) {
// ((DoubleParameter)params.getParameters().get("scoreThreshold")).setValue(median); // TODO: UPDATE DIALOG!
thresholds = new double[] { q1, median, q3 };
} else if (thresholdMethod.equals("Manual (1)")) {
thresholds = new double[] { params.getDoubleParameterValue("threshold1") };
} else if (thresholdMethod.equals("Manual (2)")) {
thresholds = new double[] { params.getDoubleParameterValue("threshold1"), params.getDoubleParameterValue("threshold2") };
} else
// if (thresholdMethod.equals("Manual (3)")) {
thresholds = new double[] { params.getDoubleParameterValue("threshold1"), params.getDoubleParameterValue("threshold2"), params.getDoubleParameterValue("threshold3") };
} else
thresholds = new double[] { median };
double minVal = Double.POSITIVE_INFINITY;
double maxVal = Double.NEGATIVE_INFINITY;
int numNonNaN = 0;
for (double d : newScoreData.scores) {
if (Double.isNaN(d))
continue;
if (d < minVal)
minVal = d;
if (d > maxVal)
maxVal = d;
numNonNaN++;
}
// If not this, we don't have valid scores that we can work with
boolean scoresValid = maxVal > minVal;
double maxTimePoint = 0;
for (double d : newScoreData.survival) {
if (Double.isNaN(d))
continue;
if (d > maxTimePoint)
maxTimePoint = d;
}
if (panelParams != null && maxTimePoint > ((IntParameter) params.getParameters().get("censorTimePoints")).getUpperBound()) {
panelParams.setNumericParameterValueRange("censorTimePoints", 0, Math.ceil(maxTimePoint));
}
// Optionally censor at specified time
double censorThreshold = params == null ? maxTimePoint : params.getIntParameterValue("censorTimePoints");
// Compute log-rank p-values for *all* possible thresholds
// Simultaneously determine the threshold that yields the lowest p-value,
// resolving ties in favour of a more even split between high/low numbers of events
boolean pValuesChanged = false;
if (calculateAllPValues) {
if (!(pValues != null && pValueThresholds != null && newScoreData.equals(scoreData) && censorThreshold == lastPValueCensorThreshold)) {
Map<Double, Double> mapLogRank = new TreeMap<>();
Set<Double> setObserved = new HashSet<>();
for (int i = 0; i < newScoreData.scores.length; i++) {
Double d = newScoreData.scores[i];
boolean observed = !newScoreData.censored[i] && newScoreData.survival[i] < censorThreshold;
if (observed)
setObserved.add(d);
if (mapLogRank.containsKey(d))
continue;
List<KaplanMeierData> kmsTemp = splitByThresholds(newScoreData, new double[] { d }, censorThreshold, false);
// if (kmsTemp.get(1).nObserved() == 0 || kmsTemp.get(1).nObserved() == 0)
// continue;
LogRankResult test = LogRankTest.computeLogRankTest(kmsTemp.get(0), kmsTemp.get(1));
double pValue = test.getPValue();
// double pValue = test.hazardRatio < 1 ? test.hazardRatio : 1.0/test.hazardRatio; // Checking usefulness of Hazard ratios...
if (!Double.isFinite(pValue))
continue;
// if (!Double.isFinite(test.getHazardRatio())) {
// // continue;
// pValue = Double.NaN;
// }
mapLogRank.put(d, pValue);
}
pValueThresholds = new double[mapLogRank.size()];
pValues = new double[mapLogRank.size()];
pValueThresholdsObserved = new boolean[mapLogRank.size()];
int count = 0;
for (Entry<Double, Double> entry : mapLogRank.entrySet()) {
pValueThresholds[count] = entry.getKey();
pValues[count] = entry.getValue();
if (setObserved.contains(entry.getKey()))
pValueThresholdsObserved[count] = true;
count++;
}
// Find the longest 'significant' stretch
int maxSigCount = 0;
int maxSigInd = -1;
int sigCurrent = 0;
int[] sigCount = new int[pValues.length];
for (int i = 0; i < pValues.length; i++) {
if (pValues[i] < 0.05) {
sigCurrent++;
sigCount[i] = sigCurrent;
if (sigCurrent > maxSigCount) {
maxSigCount = sigCurrent;
maxSigInd = i;
}
} else
sigCurrent = 0;
}
if (maxSigCount == 0) {
logger.info("No p-values < 0.05");
} else {
double minThresh = maxSigInd - maxSigCount < 0 ? pValueThresholds[0] - 0.0000001 : pValueThresholds[maxSigInd - maxSigCount];
double maxThresh = pValueThresholds[maxSigInd];
int nBetween = 0;
int nBetweenObserved = 0;
for (int i = 0; i < newScoreData.scores.length; i++) {
if (newScoreData.scores[i] > minThresh && newScoreData.scores[i] <= maxThresh) {
nBetween++;
if (newScoreData.survival[i] < censorThreshold && !newScoreData.censored[i])
nBetweenObserved++;
}
}
logger.info("Longest stretch of p-values < 0.05: {} - {} ({} entries, {} observed)", minThresh, maxThresh, nBetween, nBetweenObserved);
}
pValuesSmoothed = new double[pValues.length];
Arrays.fill(pValuesSmoothed, Double.NaN);
int n = (pValues.length / 20) * 2 + 1;
logger.info("Smoothing log-rank test p-values by " + n);
for (int i = n / 2; i < pValues.length - n / 2; i++) {
double sum = 0;
for (int k = i - n / 2; k < i - n / 2 + n; k++) {
sum += pValues[k];
}
pValuesSmoothed[i] = sum / n;
}
// for (int i = 0; i < pValues.length; i++) {
// double sum = 0;
// for (int k = Math.max(0, i-n/2); k < Math.min(pValues.length, i-n/2+n); k++) {
// sum += pValues[k];
// }
// pValuesSmoothed[i] = sum/n;
// }
// pValues = pValuesSmoothed;
lastPValueCensorThreshold = censorThreshold;
pValuesChanged = true;
}
} else {
lastPValueCensorThreshold = Double.NaN;
pValueThresholds = null;
pValues = null;
}
// if (params != null && !Double.isNaN(bestThreshold) && (params.getChoiceParameterValue("scoreThresholdMethod").equals("Lowest p-value")))
if (params != null && (params.getChoiceParameterValue("scoreThresholdMethod").equals("Lowest p-value"))) {
int bestIdx = -1;
double bestPValue = Double.POSITIVE_INFINITY;
for (int i = pValueThresholds.length / 10; i < pValueThresholds.length * 9 / 10; i++) {
if (pValues[i] < bestPValue) {
bestIdx = i;
bestPValue = pValues[i];
}
}
thresholds = bestIdx >= 0 ? new double[] { pValueThresholds[bestIdx] } : new double[0];
} else if (params != null && (params.getChoiceParameterValue("scoreThresholdMethod").equals("Lowest smoothed p-value"))) {
int bestIdx = -1;
double bestPValue = Double.POSITIVE_INFINITY;
for (int i = pValueThresholds.length / 10; i < pValueThresholds.length * 9 / 10; i++) {
if (pValuesSmoothed[i] < bestPValue) {
bestIdx = i;
bestPValue = pValuesSmoothed[i];
}
}
thresholds = bestIdx >= 0 ? new double[] { pValueThresholds[bestIdx] } : new double[0];
}
// Split into different curves using the provided thresholds
List<KaplanMeierData> kms = splitByThresholds(newScoreData, thresholds, censorThreshold, params != null && "Quartiles".equals(params.getChoiceParameterValue("scoreThresholdMethod")));
if (plotter == null) {
plotter = new KaplanMeierChartWrapper(survivalColumn + " time");
// plotter.setBorder(BorderFactory.createTitledBorder("Survival plot"));
// plotter.getCanvas().setWidth(300);
// plotter.getCanvas().setHeight(300);
}
KaplanMeierData[] kmArray = new KaplanMeierData[kms.size()];
plotter.setKaplanMeierCurves(survivalColumn + " time", kms.toArray(kmArray));
tableModel.setSurvivalCurves(thresholds, params != null && params.getChoiceParameterValue("scoreThresholdMethod").equals("Lowest p-value"), kmArray);
// Bar width determined using 'Freedman and Diaconis' rule' (but overridden if this gives < 16 bins...)
double barWidth = (2 * q3 - q1) * Math.pow(numNonNaN, -1.0 / 3.0);
int nBins = 100;
if (!Double.isNaN(barWidth))
barWidth = (int) Math.max(16, Math.ceil((maxVal - minVal) / barWidth));
Histogram histogram = scoresValid ? new Histogram(newScoreData.scores, nBins) : null;
if (histogramPanel == null) {
GridPane paneHistogram = new GridPane();
histogramPanel = new HistogramPanelFX();
histogramPanel.getChart().setAnimated(false);
histogramWrapper = new ThresholdedChartWrapper(histogramPanel.getChart());
for (ObservableNumberValue val : threshProperties) histogramWrapper.addThreshold(val, ColorToolsFX.getCachedColor(240, 0, 0, 128));
histogramWrapper.getPane().setPrefHeight(150);
paneHistogram.add(histogramWrapper.getPane(), 0, 0);
Tooltip.install(histogramPanel.getChart(), new Tooltip("Distribution of scores"));
GridPane.setHgrow(histogramWrapper.getPane(), Priority.ALWAYS);
GridPane.setVgrow(histogramWrapper.getPane(), Priority.ALWAYS);
NumberAxis xAxis = new NumberAxis();
xAxis.setLabel("Score threshold");
NumberAxis yAxis = new NumberAxis();
yAxis.setLowerBound(0);
yAxis.setUpperBound(1);
yAxis.setTickUnit(0.1);
yAxis.setAutoRanging(false);
yAxis.setLabel("P-value");
chartPValues = new LineChart<>(xAxis, yAxis);
chartPValues.setAnimated(false);
chartPValues.setLegendVisible(false);
// Make chart so it can be navigated
ChartTools.makeChartInteractive(chartPValues, xAxis, yAxis);
pValuesChanged = true;
Tooltip.install(chartPValues, new Tooltip("Distribution of p-values (log-rank test) comparing low vs. high for all possible score thresholds"));
// chartPValues.getYAxis().setAutoRanging(false);
pValuesWrapper = new ThresholdedChartWrapper(chartPValues);
for (ObservableNumberValue val : threshProperties) pValuesWrapper.addThreshold(val, ColorToolsFX.getCachedColor(240, 0, 0, 128));
pValuesWrapper.getPane().setPrefHeight(150);
paneHistogram.add(pValuesWrapper.getPane(), 0, 1);
GridPane.setHgrow(pValuesWrapper.getPane(), Priority.ALWAYS);
GridPane.setVgrow(pValuesWrapper.getPane(), Priority.ALWAYS);
ContextMenu popup = new ContextMenu();
ChartTools.addChartExportMenu(chartPValues, popup);
RadioMenuItem miZoomY1 = new RadioMenuItem("0-1");
miZoomY1.setOnAction(e -> {
yAxis.setAutoRanging(false);
yAxis.setUpperBound(1);
yAxis.setTickUnit(0.2);
});
RadioMenuItem miZoomY05 = new RadioMenuItem("0-0.5");
miZoomY05.setOnAction(e -> {
yAxis.setAutoRanging(false);
yAxis.setUpperBound(0.5);
yAxis.setTickUnit(0.1);
});
RadioMenuItem miZoomY02 = new RadioMenuItem("0-0.2");
miZoomY02.setOnAction(e -> {
yAxis.setAutoRanging(false);
yAxis.setUpperBound(0.2);
yAxis.setTickUnit(0.05);
});
RadioMenuItem miZoomY01 = new RadioMenuItem("0-0.1");
miZoomY01.setOnAction(e -> {
yAxis.setAutoRanging(false);
yAxis.setUpperBound(0.1);
yAxis.setTickUnit(0.05);
});
RadioMenuItem miZoomY005 = new RadioMenuItem("0-0.05");
miZoomY005.setOnAction(e -> {
yAxis.setAutoRanging(false);
yAxis.setUpperBound(0.05);
yAxis.setTickUnit(0.01);
});
RadioMenuItem miZoomY001 = new RadioMenuItem("0-0.01");
miZoomY001.setOnAction(e -> {
yAxis.setAutoRanging(false);
yAxis.setUpperBound(0.01);
yAxis.setTickUnit(0.005);
});
ToggleGroup tgZoom = new ToggleGroup();
miZoomY1.setToggleGroup(tgZoom);
miZoomY05.setToggleGroup(tgZoom);
miZoomY02.setToggleGroup(tgZoom);
miZoomY01.setToggleGroup(tgZoom);
miZoomY005.setToggleGroup(tgZoom);
miZoomY001.setToggleGroup(tgZoom);
Menu menuZoomY = new Menu("Set y-axis range");
menuZoomY.getItems().addAll(miZoomY1, miZoomY05, miZoomY02, miZoomY01, miZoomY005, miZoomY001);
MenuItem miCopyData = new MenuItem("Copy chart data");
miCopyData.setOnAction(e -> {
String dataString = ChartTools.getChartDataAsString(chartPValues);
ClipboardContent content = new ClipboardContent();
content.putString(dataString);
Clipboard.getSystemClipboard().setContent(content);
});
popup.getItems().addAll(miCopyData, menuZoomY);
chartPValues.setOnContextMenuRequested(e -> {
popup.show(chartPValues, e.getScreenX(), e.getScreenY());
});
for (int col = 0; col < tableModel.getColumnCount(); col++) {
TableColumn<Integer, String> column = new TableColumn<>(tableModel.getColumnName(col));
int colNumber = col;
column.setCellValueFactory(new Callback<CellDataFeatures<Integer, String>, ObservableValue<String>>() {
@Override
public ObservableValue<String> call(CellDataFeatures<Integer, String> p) {
return new SimpleStringProperty((String) tableModel.getValueAt(p.getValue(), colNumber));
}
});
column.setCellFactory(new Callback<TableColumn<Integer, String>, TableCell<Integer, String>>() {
@Override
public TableCell<Integer, String> call(TableColumn<Integer, String> param) {
TableCell<Integer, String> cell = new TableCell<Integer, String>() {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(item);
setTooltip(new Tooltip(item));
}
};
return cell;
}
});
table.getColumns().add(column);
}
table.setPrefHeight(250);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
table.maxHeightProperty().bind(table.prefHeightProperty());
params = new ParameterList();
// maxTimePoint = 0;
// for (TMACoreObject core : hierarchy.getTMAGrid().getTMACoreList()) {
// double os = core.getMeasurementList().getMeasurementValue(TMACoreObject.KEY_OVERALL_SURVIVAL);
// double rfs = core.getMeasurementList().getMeasurementValue(TMACoreObject.KEY_RECURRENCE_FREE_SURVIVAL);
// if (os > maxTimePoint)
// maxTimePoint = os;
// if (rfs > maxTimePoint)
// maxTimePoint = rfs;
// }
params.addIntParameter("censorTimePoints", "Max censored time", (int) (censorThreshold + 0.5), null, 0, (int) Math.ceil(maxTimePoint), "Latest time point beyond which data will be censored");
// params.addChoiceParameter("scoreThresholdMethod", "Threshold method", "Manual", Arrays.asList("Manual", "Median", "Log-rank test"));
if (calculateAllPValues)
// Don't include "Lowest smoothed p-value" - it's not an established method and open to misinterpretation...
params.addChoiceParameter("scoreThresholdMethod", "Threshold method", "Median", Arrays.asList("Manual (1)", "Manual (2)", "Manual (3)", "Median", "Tertiles", "Quartiles", "Lowest p-value"));
else
// params.addChoiceParameter("scoreThresholdMethod", "Threshold method", "Median", Arrays.asList("Manual (1)", "Manual (2)", "Manual (3)", "Median", "Tertiles", "Quartiles", "Lowest p-value", "Lowest smoothed p-value"));
params.addChoiceParameter("scoreThresholdMethod", "Threshold method", "Median", Arrays.asList("Manual (1)", "Manual (2)", "Manual (3)", "Median", "Tertiles", "Quartiles"));
params.addDoubleParameter("threshold1", "Threshold 1", thresholds.length > 0 ? thresholds[0] : (minVal + maxVal) / 2, null, "Threshold to distinguish between patient groups");
params.addDoubleParameter("threshold2", "Threshold 2", thresholds.length > 1 ? thresholds[1] : (minVal + maxVal) / 2, null, "Threshold to distinguish between patient groups");
params.addDoubleParameter("threshold3", "Threshold 3", thresholds.length > 2 ? thresholds[2] : (minVal + maxVal) / 2, null, "Threshold to distinguish between patient groups");
params.addBooleanParameter("showAtRisk", "Show at risk", plotter.getShowAtRisk(), "Show number of patients at risk below the plot");
params.addBooleanParameter("showTicks", "Show censored ticks", plotter.getShowCensoredTicks(), "Show ticks to indicate censored data");
params.addBooleanParameter("showKey", "Show key", plotter.getShowKey(), "Show key indicating display of each curve");
// Hide threshold parameters if threshold can't be used
if (!scoresValid) {
// params.setHiddenParameters(true, "scoreThresholdMethod", "scoreThreshold");
histogramPanel.getChart().setVisible(false);
}
panelParams = new ParameterPanelFX(params);
panelParams.addParameterChangeListener(this);
updateThresholdsEnabled();
for (int i = 0; i < threshProperties.length; i++) {
String p = "threshold" + (i + 1);
threshProperties[i].addListener((v, o, n) -> {
if (interactiveThresholds()) {
// Need to do a decent double check with tolerance to text field value changing while typing
if (!GeneralTools.almostTheSame(params.getDoubleParameterValue(p), n.doubleValue(), 0.0001))
panelParams.setNumericParameterValue(p, n);
}
});
}
BorderPane paneBottom = new BorderPane();
TitledPane paneOptions = new TitledPane("Options", panelParams.getPane());
// paneOptions.setCollapsible(false);
Pane paneCanvas = new StackPane();
paneCanvas.getChildren().add(plotter.getCanvas());
GridPane paneLeft = new GridPane();
paneLeft.add(paneOptions, 0, 0);
paneLeft.add(table, 0, 1);
GridPane.setHgrow(paneOptions, Priority.ALWAYS);
GridPane.setHgrow(table, Priority.ALWAYS);
paneBottom.setLeft(paneLeft);
paneBottom.setCenter(paneHistogram);
paneMain.setCenter(paneCanvas);
paneMain.setBottom(paneBottom);
paneMain.setPadding(new Insets(10, 10, 10, 10));
} else if (thresholds.length > 0) {
// Ensure the sliders/text fields are set sensibly
if (!GeneralTools.almostTheSame(thresholds[0], params.getDoubleParameterValue("threshold1"), 0.0001)) {
panelParams.setNumericParameterValue("threshold1", thresholds[0]);
}
if (thresholds.length > 1 && !GeneralTools.almostTheSame(thresholds[1], params.getDoubleParameterValue("threshold2"), 0.0001)) {
panelParams.setNumericParameterValue("threshold2", thresholds[1]);
}
if (thresholds.length > 2 && !GeneralTools.almostTheSame(thresholds[2], params.getDoubleParameterValue("threshold3"), 0.0001)) {
panelParams.setNumericParameterValue("threshold3", thresholds[2]);
}
}
if (histogram != null) {
histogramPanel.getHistogramData().setAll(HistogramPanelFX.createHistogramData(histogram, false, (Color) null));
histogramPanel.getChart().getXAxis().setLabel(scoreColumn);
histogramPanel.getChart().getYAxis().setLabel("Count");
ChartTools.addChartExportMenu(histogramPanel.getChart(), null);
// histogramWrapper.setVerticalLines(thresholds, ColorToolsFX.getCachedColor(240, 0, 0, 128));
// Deal with threshold adjustment
// histogramWrapper.getThresholds().addListener((Observable o) -> generatePlot());
}
if (pValues != null) {
// TODO: Raise earlier where p-value calculation is
if (pValuesChanged) {
ObservableList<XYChart.Data<Number, Number>> data = FXCollections.observableArrayList();
for (int i = 0; i < pValueThresholds.length; i++) {
double pValue = pValues[i];
if (Double.isNaN(pValue))
continue;
data.add(new XYChart.Data<>(pValueThresholds[i], pValue, pValueThresholdsObserved[i]));
}
ObservableList<XYChart.Data<Number, Number>> dataSmoothed = null;
if (pValuesSmoothed != null) {
dataSmoothed = FXCollections.observableArrayList();
for (int i = 0; i < pValueThresholds.length; i++) {
double pValueSmoothed = pValuesSmoothed[i];
if (Double.isNaN(pValueSmoothed))
continue;
dataSmoothed.add(new XYChart.Data<>(pValueThresholds[i], pValueSmoothed));
}
}
// Don't bother showing the smoothed data... it tends to get in the way...
// if (dataSmoothed != null)
// chartPValues.getData().setAll(new XYChart.Series<>("P-values", data), new XYChart.Series<>("Smoothed P-values", dataSmoothed));
// else
chartPValues.getData().setAll(new XYChart.Series<>("P-values", data));
// Add line to show 0.05 significance threshold
if (pValueThresholds.length > 1) {
Data<Number, Number> sigData1 = new Data<>(pValueThresholds[0], 0.05);
Data<Number, Number> sigData2 = new Data<>(pValueThresholds[pValueThresholds.length - 1], 0.05);
XYChart.Series<Number, Number> dataSignificant = new XYChart.Series<>("Significance 0.05", FXCollections.observableArrayList(sigData1, sigData2));
chartPValues.getData().add(dataSignificant);
sigData1.getNode().setVisible(false);
sigData2.getNode().setVisible(false);
}
// pValuesWrapper.clearThresholds();
for (XYChart.Data<Number, Number> dataPoint : data) {
if (!Boolean.TRUE.equals(dataPoint.getExtraValue()))
dataPoint.getNode().setVisible(false);
}
// if (dataSmoothed != null) {
// for (XYChart.Data<Number, Number> dataPoint : dataSmoothed) {
// dataPoint.getNode().setVisible(false);
// }
// chartPValues.getData().get(1).getNode().setOpacity(0.5);
// }
// int count = 0;
// for (int i = 0; i < pValueThresholds.length; i++) {
// double pValue = pValues[i];
// if (Double.isNaN(pValue))
// continue;
// boolean observed = pValueThresholdsObserved[i];
// // if (observed)
// // pValuesWrapper.addThreshold(new ReadOnlyDoubleWrapper(pValueThresholds[i]), Color.rgb(0, 0, 0, 0.05));
//
// if (!observed) {
// // StackPane pane = (StackPane)data.get(count).getNode();
// // pane.setEffect(new DropShadow());
// data.get(count).getNode().setVisible(false);
// }
// count++;
// }
}
for (int i = 0; i < threshProperties.length; i++) {
if (i < thresholds.length)
threshProperties[i].set(thresholds[i]);
else
threshProperties[i].set(Double.NaN);
}
boolean isInteractive = interactiveThresholds();
histogramWrapper.setIsInteractive(isInteractive);
pValuesWrapper.setIsInteractive(isInteractive);
chartPValues.setVisible(true);
}
// else
// chartPValues.setVisible(false);
// Store values for next time
scoreData = newScoreData;
}
use of qupath.lib.measurements.MeasurementList in project qupath by qupath.
the class PathObjectIOTest method test_IOObjectsGeoJSONImpl.
private void test_IOObjectsGeoJSONImpl(boolean keepMeasurements, GeoJsonExportOptions... options) throws IOException {
ROI roiDetection = ROIs.createRectangleROI(0, 0, 10, 10, ImagePlane.getDefaultPlane());
ROI roiAnnotation = ROIs.createRectangleROI(100, 100, 10, 10, ImagePlane.getDefaultPlane());
ROI roiCell1 = ROIs.createRectangleROI(25, 25, 25, 25, ImagePlane.getDefaultPlane());
ROI roiCell2 = ROIs.createRectangleROI(12, 12, 5, 5, ImagePlane.getDefaultPlane());
ROI roiTile = ROIs.createRectangleROI(100, 100, 10, 10, ImagePlane.getDefaultPlane());
MeasurementList mlDetection = MeasurementListFactory.createMeasurementList(16, MeasurementList.MeasurementListType.GENERAL);
MeasurementList mlCell = MeasurementListFactory.createMeasurementList(16, MeasurementList.MeasurementListType.GENERAL);
PathObject myPDO = PathObjects.createDetectionObject(roiDetection, PathClassFactory.getPathClass("PathClassTest1", ColorTools.BLACK), mlDetection);
PathObject myPAO = PathObjects.createAnnotationObject(roiAnnotation, PathClassFactory.getPathClass("PathClassTest1", ColorTools.BLACK));
PathObject myPCO = PathObjects.createCellObject(roiCell1, roiCell2, PathClassFactory.getPathClass("PathClassTest2", ColorTools.GREEN), mlCell);
PathObject myPTO = PathObjects.createTileObject(roiTile, PathClassFactory.getPathClass("PathClassTest2", ColorTools.GREEN), null);
PathObject myTMA = PathObjects.createTMACoreObject(25, 25, 25, false);
Collection<PathObject> objs = Arrays.asList(myPDO, myPCO, myPAO, myPTO, myTMA);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// Add measurements
mlDetection.addMeasurement("TestMeasurement1", 5.0);
mlDetection.addMeasurement("TestMeasurement2", 10.0);
mlCell.addMeasurement("TestMeasurement3", 15.0);
mlCell.addMeasurement("TestMeasurement4", 20.0);
// Export to GeoJSON
PathIO.exportObjectsAsGeoJSON(bos, objs, options);
// Import from GeoJSON
List<PathObject> objsBack = new ArrayList<>(PathIO.readObjectsFromGeoJSON(new ByteArrayInputStream(bos.toByteArray())));
assertEquals(objs.size(), objsBack.size());
// Array to count number of each PathObject type
int[] countCheck = new int[] { 0, 0, 0, 0, 0 };
for (PathObject po : objsBack) {
if (po == null)
continue;
// Test whether po has a ROI
assertTrue(po.hasROI());
if (po.isTile()) {
assertEquals(po.getPathClass(), PathClassFactory.getPathClass("PathClassTest2", ColorTools.GREEN));
assertSameROIs(po.getROI(), roiTile);
assertFalse(po.hasMeasurements());
countCheck[0]++;
} else if (po.isCell()) {
assertEquals(po.getPathClass(), PathClassFactory.getPathClass("PathClassTest2", ColorTools.GREEN));
assertSameROIs(po.getROI(), roiCell1);
assertSameROIs(((PathCellObject) po).getNucleusROI(), roiCell2);
if (keepMeasurements) {
assertTrue(po.hasMeasurements());
assertSameMeasurements(po.getMeasurementList(), myPCO.getMeasurementList());
} else
assertFalse(po.hasMeasurements());
countCheck[1]++;
} else if (po.isDetection()) {
assertEquals(po.getPathClass(), PathClassFactory.getPathClass("PathClassTest1", ColorTools.BLACK));
assertSameROIs(po.getROI(), roiDetection);
if (keepMeasurements) {
assertTrue(po.hasMeasurements());
assertSameMeasurements(po.getMeasurementList(), myPDO.getMeasurementList());
} else
assertFalse(po.hasMeasurements());
countCheck[2]++;
} else if (po.isAnnotation()) {
assertEquals(po.getPathClass(), PathClassFactory.getPathClass("PathClassTest1", ColorTools.BLACK));
assertSameROIs(po.getROI(), roiAnnotation);
assertFalse(po.hasMeasurements());
countCheck[3]++;
} else if (po.isTMACore()) {
assertFalse(po.hasMeasurements());
assertSameROIs(po.getROI(), myTMA.getROI());
countCheck[4]++;
}
}
assertArrayEquals(countCheck, new int[] { 1, 1, 1, 1, 1 });
}
use of qupath.lib.measurements.MeasurementList in project qupath by qupath.
the class TestPathAnnotationObject method test_MeasurementList.
@Test
public void test_MeasurementList() {
MeasurementList tML = MeasurementListFactory.createMeasurementList(16, MeasurementList.MeasurementListType.GENERAL);
tML.putMeasurement(MeasurementFactory.createMeasurement(nameML, valueML));
PathAnnotationObject tPO = new PathAnnotationObject(myROI, myPC, tML);
// no measurements
test_hasMeasurements(myPO, Boolean.FALSE);
// no measurements
test_nMeasurements(myPO, 0);
// checks for instanceOf(MeasurementList.class)
test_getMeasurementList(myPO);
test_hasMeasurements(myPO, Boolean.FALSE);
test_nMeasurements(myPO, 0);
// there is 1 measurement
test_hasMeasurements(tPO, Boolean.TRUE);
// there is 1 measurement
test_nMeasurements(tPO, 1);
test_getMeasurementList(tPO, tML);
}
Aggregations