Java tutorial
/* * Copyright (c) 2010 The Jackson Laboratory * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software. If not, see <http://www.gnu.org/licenses/>. */ package org.jax.maanova.test.gui; import java.awt.BorderLayout; import java.awt.Cursor; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionAdapter; import java.awt.event.MouseMotionListener; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.AbstractAction; import javax.swing.ButtonGroup; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JCheckBoxMenuItem; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JToolTip; import org.jax.maanova.Maanova; import org.jax.maanova.madata.MicroarrayExperiment; import org.jax.maanova.madata.ProbesetRow; import org.jax.maanova.madata.gui.AddGeneListDialog; import org.jax.maanova.plot.AreaSelectionListener; import org.jax.maanova.plot.MaanovaChartPanel; import org.jax.maanova.plot.PlotUtil; import org.jax.maanova.plot.SaveChartAction; import org.jax.maanova.plot.SimpleChartConfigurationDialog; import org.jax.maanova.test.MaanovaTestResult; import org.jax.maanova.test.MaanovaTestStatisticSubtype; import org.jax.maanova.test.MaanovaTestStatisticType; import org.jax.maanova.test.MaanovaTestStatistics; import org.jax.r.RUtilities; import org.jax.util.datastructure.SequenceUtilities; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartRenderingInfo; import org.jfree.chart.JFreeChart; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.data.xy.DefaultXYDataset; import org.jfree.data.xy.XYDataset; /** * Panel used to render a volcano plot * @author <A HREF="mailto:keith.sheppard@jax.org">Keith Sheppard</A> */ public class VolcanoPlotPanel extends JPanel { /** * every {@link java.io.Serializable} is supposed to have one of these */ private static final long serialVersionUID = 2887360960196852317L; private static final double MIN_PVALUE_THRESHOLD = 1e-9; /** * logger for this class */ private static final Logger LOG = Logger.getLogger(VolcanoPlotPanel.class.getName()); private static final int CURSOR_Y_OFFSET = 16; private final MaanovaTestResult maanovaTestResult; private final JMenuItem saveSelectedPointsMenuItem; private final DisplayTestResultsAction displayTestResultsAction; private final JPanel controlPanel; private final JToolTip toolTip; private final MaanovaChartPanel chartPanel; private final JComboBox statisticTypeComboBox; private final JComboBox statisticSubtypeComboBox; private final JComboBox testNumberComboBox; private XYProbeData cachedXYData = null; private volatile int[] selectedIndices = new int[0]; private final SimpleChartConfigurationDialog chartConfigurationDialog; private final MouseMotionListener myMouseMotionListener = new MouseMotionAdapter() { /** * {@inheritDoc} */ @Override public void mouseMoved(MouseEvent e) { VolcanoPlotPanel.this.mouseMoved(e); } }; private final MouseListener chartMouseListener = new MouseAdapter() { /** * {@inheritDoc} */ @Override public void mousePressed(MouseEvent e) { VolcanoPlotPanel.this.clearProbePopup(); } /** * {@inheritDoc} */ @Override public void mouseExited(MouseEvent e) { VolcanoPlotPanel.this.clearProbePopup(); } }; private final AreaSelectionListener areaSelectionListener = new AreaSelectionListener() { /** * {@inheritDoc} */ public void areaSelected(Rectangle2D area) { VolcanoPlotPanel.this.areaSelected(area); } }; private final ComponentListener chartComponentListener = new ComponentAdapter() { /** * {@inheritDoc} */ @Override public void componentResized(ComponentEvent e) { VolcanoPlotPanel.this.saveGraphImageAction.setSize(e.getComponent().getSize()); } }; private final SaveChartAction saveGraphImageAction = new SaveChartAction(); private volatile Rectangle2D viewArea = null; private volatile boolean dragToSelect; private volatile boolean dragToZoom; private volatile boolean showTooltip; private final TestStatisticItem[] availableTestStatistics; /** * Constructor * @param parent the parent frame * @param maanovaTestResult * the test result to plot */ public VolcanoPlotPanel(JFrame parent, MaanovaTestResult maanovaTestResult) { this(parent, maanovaTestResult, 0, new int[0]); } /** * Constructor * @param parent the parent frame * @param maanovaTestResult * the test result to plot * @param initialTestIndex * the initial test index to use * @param selectedIndices * the initially selected indices */ public VolcanoPlotPanel(JFrame parent, MaanovaTestResult maanovaTestResult, int initialTestIndex, int[] selectedIndices) { selectedIndices = SequenceUtilities.uniqueInts(selectedIndices); this.chartConfigurationDialog = new SimpleChartConfigurationDialog(parent); this.chartConfigurationDialog.addOkActionListener(new ActionListener() { /** * {@inheritDoc} */ public void actionPerformed(ActionEvent e) { VolcanoPlotPanel.this.updateDataPoints(); } }); this.maanovaTestResult = maanovaTestResult; this.saveSelectedPointsMenuItem = new JMenuItem("Save Selected Points to Gene List"); this.saveSelectedPointsMenuItem.setEnabled(selectedIndices != null && selectedIndices.length >= 1); this.saveSelectedPointsMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { VolcanoPlotPanel.this.saveSelectedPoints(); } }); this.displayTestResultsAction = new DisplayTestResultsAction("Show Results Table", maanovaTestResult); this.selectedIndices = selectedIndices; this.setLayout(new BorderLayout()); JPanel chartAndControlPanel = new JPanel(new BorderLayout()); this.add(chartAndControlPanel, BorderLayout.CENTER); this.chartPanel = new MaanovaChartPanel(); this.chartPanel.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); this.chartPanel.addComponentListener(this.chartComponentListener); this.chartPanel.addAreaSelectionListener(this.areaSelectionListener); this.chartPanel.addMouseListener(this.chartMouseListener); chartAndControlPanel.add(this.chartPanel, BorderLayout.CENTER); this.controlPanel = new JPanel(new FlowLayout()); ItemListener updateDataItemListener = new ItemListener() { /** * {@inheritDoc} */ public void itemStateChanged(ItemEvent e) { VolcanoPlotPanel.this.forgetGraphState(); VolcanoPlotPanel.this.updateDataPoints(); } }; this.statisticTypeComboBox = new JComboBox(); for (MaanovaTestStatisticType testStatType : MaanovaTestStatisticType.values()) { this.statisticTypeComboBox.addItem(testStatType); } this.statisticTypeComboBox.addItemListener(updateDataItemListener); this.controlPanel.add(this.statisticTypeComboBox); List<MaanovaTestStatisticSubtype> availableTestSubtypes = new ArrayList<MaanovaTestStatisticSubtype>(); this.statisticSubtypeComboBox = new JComboBox(); MaanovaTestStatistics fStat = this.maanovaTestResult.getStatistics(MaanovaTestStatisticType.F_STAT); for (MaanovaTestStatisticSubtype statSubtype : MaanovaTestStatisticSubtype.values()) { if (fStat.hasTestStatistic(statSubtype)) { availableTestSubtypes.add(statSubtype); if (statSubtype != MaanovaTestStatisticSubtype.F_OBSERVED) { this.statisticSubtypeComboBox.addItem(statSubtype); } } } this.statisticSubtypeComboBox.addItemListener(updateDataItemListener); this.controlPanel.add(this.statisticSubtypeComboBox); int testStatCount = availableTestSubtypes.size() * MaanovaTestStatisticType.values().length; this.availableTestStatistics = new TestStatisticItem[testStatCount]; for (int i = 0; i < MaanovaTestStatisticType.values().length; i++) { for (int j = 0; j < availableTestSubtypes.size(); j++) { int flatIndex = i * availableTestSubtypes.size() + j; this.availableTestStatistics[flatIndex] = new TestStatisticItem( MaanovaTestStatisticType.values()[i], availableTestSubtypes.get(j)); } } int testCount = fStat.getContrastCount(); if (testCount == 1) { this.testNumberComboBox = null; } else { this.testNumberComboBox = new JComboBox(); for (int i = 1; i <= testCount; i++) { this.testNumberComboBox.addItem("Test Number " + i); } this.testNumberComboBox.setSelectedIndex(initialTestIndex); this.testNumberComboBox.addItemListener(updateDataItemListener); this.controlPanel.add(this.testNumberComboBox); } chartAndControlPanel.add(this.controlPanel, BorderLayout.NORTH); JMenuBar menu = this.createMenu(); this.add(menu, BorderLayout.NORTH); this.forgetGraphState(); this.updateDataPoints(); this.chartPanel.addMouseMotionListener(this.myMouseMotionListener); this.chartPanel.setLayout(null); this.toolTip = new JToolTip(); } private void saveSelectedPoints() { int[] currSelectedIndices = this.selectedIndices; String[] probeIds = this.maanovaTestResult.getParentExperiment().getProbesetIds(); List<String> selectedGenes = new ArrayList<String>(currSelectedIndices.length); for (int i : currSelectedIndices) { selectedGenes.add(probeIds[i]); } AddGeneListDialog dialog = new AddGeneListDialog( (JFrame) org.jax.util.gui.SwingUtilities.getContainingWindow(this), this.maanovaTestResult.getParentExperiment(), selectedGenes); dialog.pack(); dialog.setVisible(true); } /** * Forget about the axis labeling and the zoom level */ private void forgetGraphState() { this.chartConfigurationDialog.setChartTitle("Volcano Plot for " + this.maanovaTestResult.toString()); this.chartConfigurationDialog.setXAxisLabel("Fold Change"); this.chartConfigurationDialog .setYAxisLabel("-log10(" + this.getSelectedStatisticSubtype().toString() + ")"); this.viewArea = null; } private void autoRangeChart() { this.viewArea = null; this.updateDataPoints(); } @SuppressWarnings("serial") private JMenuBar createMenu() { JMenuBar menuBar = new JMenuBar(); // the file menu JMenu fileMenu = new JMenu("File"); fileMenu.add(this.saveGraphImageAction); menuBar.add(fileMenu); // the tools menu JMenu toolsMenu = new JMenu("Tools"); JMenuItem configureGraphItem = new JMenuItem("Configure Graph..."); configureGraphItem.addActionListener(new ActionListener() { /** * {@inheritDoc} */ public void actionPerformed(ActionEvent e) { VolcanoPlotPanel.this.chartConfigurationDialog.setVisible(true); } }); toolsMenu.add(configureGraphItem); toolsMenu.addSeparator(); toolsMenu.add(new AbstractAction("Clear Selections") { /** * {@inheritDoc} */ public void actionPerformed(ActionEvent e) { VolcanoPlotPanel.this.setSelectedIndices(new int[0]); } }); toolsMenu.addSeparator(); ButtonGroup dragButtonGroup = new ButtonGroup(); JCheckBoxMenuItem selectModeCheckBox = new JCheckBoxMenuItem("Drag Cursor to Select"); selectModeCheckBox.setSelected(true); this.dragToSelect = true; selectModeCheckBox.addItemListener(new ItemListener() { /** * {@inheritDoc} */ public void itemStateChanged(ItemEvent e) { VolcanoPlotPanel.this.dragToSelect = e.getStateChange() == ItemEvent.SELECTED; } }); dragButtonGroup.add(selectModeCheckBox); toolsMenu.add(selectModeCheckBox); JCheckBoxMenuItem zoomModeCheckBox = new JCheckBoxMenuItem("Drag Cursor to Zoom"); zoomModeCheckBox.addItemListener(new ItemListener() { /** * {@inheritDoc} */ public void itemStateChanged(ItemEvent e) { VolcanoPlotPanel.this.dragToZoom = e.getStateChange() == ItemEvent.SELECTED; } }); dragButtonGroup.add(zoomModeCheckBox); toolsMenu.add(zoomModeCheckBox); toolsMenu.addSeparator(); toolsMenu.add(new AbstractAction("Zoom Out") { /** * {@inheritDoc} */ public void actionPerformed(ActionEvent e) { VolcanoPlotPanel.this.autoRangeChart(); } }); toolsMenu.addSeparator(); JCheckBoxMenuItem showTooltipCheckbox = new JCheckBoxMenuItem("Show Info Popup for Nearest Point"); showTooltipCheckbox.setSelected(true); this.showTooltip = true; showTooltipCheckbox.addItemListener(new ItemListener() { /** * {@inheritDoc} */ public void itemStateChanged(ItemEvent e) { VolcanoPlotPanel.this.showTooltip = e.getStateChange() == ItemEvent.SELECTED; VolcanoPlotPanel.this.clearProbePopup(); } }); toolsMenu.add(showTooltipCheckbox); toolsMenu.addSeparator(); toolsMenu.add(this.displayTestResultsAction); toolsMenu.addSeparator(); toolsMenu.add(this.saveSelectedPointsMenuItem); JMenu selectPointsFromLisMenu = new JMenu("Select Points From Gene List"); List<String> geneListNames = this.maanovaTestResult.getParentExperiment().getGeneListNames(); if (geneListNames.isEmpty()) { JMenuItem noListsMenuItem = new JMenuItem("No Gene Lists Available"); noListsMenuItem.setEnabled(false); selectPointsFromLisMenu.add(noListsMenuItem); } else { for (final String geneListName : geneListNames) { JMenuItem currGeneListMenuItem = new JMenuItem( RUtilities.fromRIdentifierToReadableName(geneListName)); currGeneListMenuItem.addActionListener(new ActionListener() { /** * {@inheritDoc} */ public void actionPerformed(ActionEvent e) { VolcanoPlotPanel.this.selectedIndicesFromGeneList(geneListName); } }); selectPointsFromLisMenu.add(currGeneListMenuItem); } } toolsMenu.add(selectPointsFromLisMenu); menuBar.add(toolsMenu); // the help menu JMenu helpMenu = new JMenu("Help"); JMenuItem helpMenuItem = new JMenuItem("Help..."); Icon helpIcon = new ImageIcon(VolcanoPlotPanel.class.getResource("/images/action/help-16x16.png")); helpMenuItem.setIcon(helpIcon); helpMenuItem.addActionListener(new ActionListener() { /** * {@inheritDoc} */ public void actionPerformed(ActionEvent e) { Maanova.getInstance().showHelp("volcano-plot", VolcanoPlotPanel.this); } }); helpMenu.add(helpMenuItem); menuBar.add(helpMenu); return menuBar; } private void selectedIndicesFromGeneList(String geneListName) { MicroarrayExperiment experiment = this.maanovaTestResult.getParentExperiment(); this.setSelectedIndices(experiment.getIndicesForGeneListNamed(geneListName)); } private void areaSelected(Rectangle2D area) { Rectangle2D chartArea = this.chartPanel.toChartRectangle(area); if (this.dragToSelect) { this.setSelectedIndices(this.getIndicesInArea(chartArea)); } else if (this.dragToZoom) { this.viewArea = chartArea; this.updateDataPoints(); } } private void setSelectedIndices(int[] selectedIndices) { selectedIndices = SequenceUtilities.uniqueInts(selectedIndices); if (!Arrays.equals(selectedIndices, this.selectedIndices)) { this.selectedIndices = selectedIndices; this.saveSelectedPointsMenuItem.setEnabled(selectedIndices != null && selectedIndices.length >= 1); this.updateDataPoints(); } } /** * Getter for the indices that fall in the given area * @param area the area (using chart coordinates) * @return the indices */ private int[] getIndicesInArea(Rectangle2D area) { XYProbeData xyData = this.getXYData(); double[] xData = xyData.getXData(); double[] yData = xyData.getYData(); int[] probeIndices = xyData.getProbeIndices(); int selectedCount = 0; int[] mySelectedIndices = new int[xData.length]; for (int i = 0; i < xData.length; i++) { if (area.contains(xData[i], yData[i])) { mySelectedIndices[selectedCount] = probeIndices[i]; selectedCount++; } } // trim the array to size if (selectedCount == mySelectedIndices.length) { return mySelectedIndices; } else { int[] trimmedArray = new int[selectedCount]; for (int i = 0; i < trimmedArray.length; i++) { trimmedArray[i] = mySelectedIndices[i]; } return trimmedArray; } } private void mouseMoved(MouseEvent e) { if (this.showTooltip) { Point2D chartPoint = this.chartPanel.toChartPoint(e.getPoint()); // find the nearest probe XYProbeData xyProbeData = this.getXYData(); double[][] xyData = new double[][] { xyProbeData.getXData(), xyProbeData.getYData() }; int nearestDotIndex = PlotUtil.getNearestDataIndex(xyData, chartPoint.getX(), chartPoint.getY()); if (nearestDotIndex == -1) { this.clearProbePopup(); } else { Point2D probeJava2DCoord = this.getJava2DCoordinates(xyData, nearestDotIndex); double java2DDist = probeJava2DCoord.distance(e.getX(), e.getY()); // is the probe close enough to be worth showing (in pixel distance) if (java2DDist <= PlotUtil.SCATTER_PLOT_DOT_SIZE_PIXELS * 2) { this.showProbePopup(xyProbeData.getProbeIndices()[nearestDotIndex], e.getX(), e.getY()); } else { this.clearProbePopup(); } } } } private void clearProbePopup() { if (this.toolTip.getParent() != null) { this.chartPanel.remove(this.toolTip); this.chartPanel.repaint(); } } private void showProbePopup(int nearestProbeIndex, int pixelX, int pixelY) { if (this.toolTip.getParent() == null) { this.chartPanel.add(this.toolTip); } ProbesetRow nearestProbeset = this.maanovaTestResult.getProbesetRow(nearestProbeIndex, this.getSelectedTestNumber(), this.availableTestStatistics); if (nearestProbeset == null) { LOG.severe("Failed to lookup probeset data for index: " + nearestProbeIndex); } else { final String rowStart = "<tr><td>"; final String rowStop = "</td></tr>"; final String cellDelimiter = "</td><td>"; StringBuilder tableRowsString = new StringBuilder("<html><table>"); tableRowsString.append(rowStart); tableRowsString.append("ID:"); tableRowsString.append(cellDelimiter); tableRowsString.append(nearestProbeset.getId()); tableRowsString.append(rowStop); for (int i = 0; i < nearestProbeset.getValues().length; i++) { tableRowsString.append(rowStart); tableRowsString.append(this.availableTestStatistics[i].toString()); tableRowsString.append(':'); tableRowsString.append(cellDelimiter); tableRowsString.append(nearestProbeset.getValues()[i]); } tableRowsString.append("</table></html>"); this.toolTip.setTipText(tableRowsString.toString()); // if the tool tip goes off the right edge of the screen, move it to the // left side of the cursor final int tooltipX; if (pixelX + this.toolTip.getPreferredSize().width > this.chartPanel.getWidth()) { tooltipX = pixelX - this.toolTip.getPreferredSize().width; } else { tooltipX = pixelX; } final int tooltipY; if (pixelY + this.toolTip.getPreferredSize().height + CURSOR_Y_OFFSET > this.chartPanel.getHeight()) { tooltipY = (pixelY - this.toolTip.getPreferredSize().height) - CURSOR_Y_OFFSET; } else { tooltipY = pixelY + CURSOR_Y_OFFSET; } this.toolTip.setLocation(tooltipX, tooltipY); this.toolTip.setSize(this.toolTip.getPreferredSize()); } } private Point2D getJava2DCoordinates(double[][] xyData, int dotIndex) { final double graphX = xyData[0][dotIndex]; final double graphY = xyData[1][dotIndex]; final XYPlot plot = (XYPlot) this.chartPanel.getChart().getPlot(); final ChartRenderingInfo renderingInfo = this.chartPanel.getChartRenderingInfo(); return PlotUtil.toJava2DCoordinates(plot, renderingInfo, graphX, graphY); } /** * Getter for the selected test statistics * @return * the selected test statistics */ private MaanovaTestStatistics getSelectedTestStatistics() { return this.maanovaTestResult.getStatistics(this.getSelectedStatisticType()); } private MaanovaTestStatisticType getSelectedStatisticType() { return (MaanovaTestStatisticType) this.statisticTypeComboBox.getSelectedItem(); } private MaanovaTestStatisticSubtype getSelectedStatisticSubtype() { return (MaanovaTestStatisticSubtype) this.statisticSubtypeComboBox.getSelectedItem(); } private int getSelectedTestNumber() { if (this.testNumberComboBox == null) { return 0; } else { return this.testNumberComboBox.getSelectedIndex(); } } private void updateDataPoints() { this.cachedXYData = null; XYProbeData xyData = this.getXYData(); DefaultXYDataset xyDataSet = new DefaultXYDataset(); int[] selectedIndices = this.selectedIndices; if (selectedIndices.length == 0) { xyDataSet.addSeries("data", new double[][] { xyData.getXData(), xyData.getYData() }); } else { double[] xData = xyData.getXData(); double[] yData = xyData.getYData(); int[] dataIndices = xyData.getProbeIndices(); double[] normalXData = new double[xData.length - selectedIndices.length]; double[] normalYData = new double[xData.length - selectedIndices.length]; double[] selectedXData = new double[selectedIndices.length]; double[] selectedYData = new double[selectedIndices.length]; int selectionIndex = 0; for (int i = 0; i < xData.length; i++) { if (selectionIndex < selectedIndices.length && dataIndices[i] == selectedIndices[selectionIndex]) { // this is one of the selected points selectedXData[selectionIndex] = xData[i]; selectedYData[selectionIndex] = yData[i]; selectionIndex++; } else { // this is not a selected point normalXData[i - selectionIndex] = xData[i]; normalYData[i - selectionIndex] = yData[i]; } } xyDataSet.addSeries("data", new double[][] { normalXData, normalYData }); xyDataSet.addSeries("selected data", new double[][] { selectedXData, selectedYData }); } JFreeChart scatterPlot = ChartFactory.createScatterPlot(this.chartConfigurationDialog.getChartTitle(), this.chartConfigurationDialog.getXAxisLabel(), this.chartConfigurationDialog.getYAxisLabel(), xyDataSet, PlotOrientation.VERTICAL, false, false, false); XYPlot xyPlot = (XYPlot) scatterPlot.getPlot(); xyPlot.setRenderer(PlotUtil.createSimpleScatterPlotRenderer()); if (this.viewArea != null) { PlotUtil.rescaleXYPlot(this.viewArea, xyPlot); } this.saveGraphImageAction.setChart(scatterPlot); this.chartPanel.setChart(scatterPlot); } private synchronized XYProbeData getXYData() { if (this.cachedXYData == null) { this.cachedXYData = this.createXYData(this.getSelectedTestNumber(), this.getSelectedTestStatistics(), this.getSelectedStatisticSubtype()); } return this.cachedXYData; } /** * Creates volcano plot data points from a JFreeChart {@link XYDataset} * from the given test statistics. * @param plotIndex * the index of test that we want data points for * @param testStatistics * the test statistics to extract from * @param testStatisticSubtype * the subtype to extract from * @return * the XY points for the volcano plot */ private XYProbeData createXYData(int plotIndex, MaanovaTestStatistics testStatistics, MaanovaTestStatisticSubtype testStatisticSubtype) { Double[] objXValues = this.maanovaTestResult.getFoldChangeValues(plotIndex); Double[] objYValues = testStatistics.getValues(testStatisticSubtype, plotIndex); // check the array lengths which should be the same if everything is OK if (objXValues.length != objYValues.length) { throw new IllegalArgumentException("There is a missmatch between the number of X (" + objXValues.length + ") and Y (" + objYValues.length + ") values"); } // first count all non-null pairings int nonNullCount = 0; for (int i = 0; i < objXValues.length; i++) { if (objXValues[i] != null && objYValues[i] != null) { nonNullCount++; } } if (nonNullCount != objXValues.length && LOG.isLoggable(Level.WARNING)) { LOG.warning( "Found " + (objXValues.length - nonNullCount) + " NaN data points in the volcano plot data"); } // OK, now convert to primitive arrays double[] primXValues = new double[nonNullCount]; double[] primYValues = new double[nonNullCount]; int[] probeIndices = new int[nonNullCount]; int primitiveArraysIndex = 0; for (int objArraysIndex = 0; objArraysIndex < objXValues.length; objArraysIndex++) { if (objXValues[objArraysIndex] != null && objYValues[objArraysIndex] != null) { double yVal = objYValues[objArraysIndex]; if (yVal < MIN_PVALUE_THRESHOLD) { yVal = MIN_PVALUE_THRESHOLD; } primXValues[primitiveArraysIndex] = objXValues[objArraysIndex]; primYValues[primitiveArraysIndex] = -Math.log10(yVal); probeIndices[primitiveArraysIndex] = objArraysIndex; primitiveArraysIndex++; } } return new XYProbeData(primXValues, primYValues, probeIndices); } private class XYProbeData { private final double[] xData; private final double[] yData; private final int[] probeIndices; /** * Constructor * @param xData the x axis data * @param yData the y axis data * @param probeIndices the indices for the corresponding probes */ public XYProbeData(double[] xData, double[] yData, int[] probeIndices) { assert xData.length == yData.length; assert yData.length == probeIndices.length; this.xData = xData; this.yData = yData; this.probeIndices = probeIndices; } /** * Getter for the probe indices * @return the probeIndices */ public int[] getProbeIndices() { return this.probeIndices; } /** * Getter for the X data * @return the xData */ public double[] getXData() { return this.xData; } /** * Getter for the Y data * @return the yData */ public double[] getYData() { return this.yData; } } }