Java tutorial
/** * This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>. */ package edu.dfci.cccb.mev.domain; import static edu.dfci.cccb.mev.domain.AnnotationDimension.COLUMN; import static edu.dfci.cccb.mev.domain.AnnotationDimension.ROW; import static edu.dfci.cccb.mev.domain.MatrixData.EMPTY_MATRIX_DATA; import static java.lang.Double.NaN; import static java.lang.Math.max; import static java.lang.Math.min; import static java.util.Arrays.asList; import static java.util.UUID.randomUUID; import static org.supercsv.prefs.CsvPreference.TAB_PREFERENCE; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.Closeable; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectOutput; import java.io.OutputStream; import java.util.AbstractList; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import javax.sql.DataSource; import lombok.Getter; import lombok.Setter; import lombok.SneakyThrows; import lombok.Synchronized; import lombok.ToString; import lombok.experimental.Accessors; import lombok.extern.log4j.Log4j; import org.apache.commons.math3.exception.NotStrictlyPositiveException; import org.apache.commons.math3.exception.OutOfRangeException; import org.apache.commons.math3.linear.AbstractRealMatrix; import org.apache.commons.math3.linear.RealMatrix; import org.apache.commons.math3.linear.RealMatrixPreservingVisitor; import org.javatuples.Pair; import org.javatuples.Triplet; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.multipart.MultipartFile; import org.supercsv.cellprocessor.ConvertNullTo; import org.supercsv.cellprocessor.ParseDouble; import org.supercsv.cellprocessor.ift.CellProcessor; import org.supercsv.io.CsvListReader; import us.levk.math.linear.EucledianDistanceClusterer; import us.levk.math.linear.EucledianDistanceClusterer.Cluster; import us.levk.math.linear.HugeRealMatrix; import us.levk.util.io.implementation.Provisional; import us.levk.util.io.support.Provisionals; import ch.lambdaj.Lambda; import ch.lambdaj.function.convert.Converter; import com.fasterxml.jackson.annotation.JsonView; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.cache.RemovalListener; import com.google.common.cache.RemovalNotification; import edu.dfci.cccb.mev.analysis.Limma; /** * @author levk * */ @ToString(exclude = { "rowAnnotations", "columnAnnotations", "rowSelections", "columnSelections" }) @Log4j public class Heatmap implements Closeable { private final UUID universalId = randomUUID(); private Builder builder; private RealMatrix data; private @Getter MatrixSummary summary; private Annotations rowAnnotations; private Annotations columnAnnotations; private List<Map<String, Map<String, String>>> rowSelections = new SelectionHolderList(); private List<Map<String, Map<String, String>>> columnSelections = new SelectionHolderList(); private @Getter JsonCluster rowClusters = null; private @Getter JsonCluster columnClusters = null; private final LoadingCache<Pair<String, String>, Triplet<Provisional, Provisional, Provisional>> limmaRows = CacheBuilder .newBuilder().maximumSize(100).expireAfterWrite(20, TimeUnit.MINUTES).removalListener( new RemovalListener<Pair<String, String>, Triplet<Provisional, Provisional, Provisional>>() { @Override public void onRemoval( RemovalNotification<Pair<String, String>, Triplet<Provisional, Provisional, Provisional>> arg0) { log.debug("Ejecting limma analysis " + Heatmap.this + "(" + arg0.getKey() + ") from cache"); Provisional p = null; for (Iterator<Object> iterator = arg0.getValue().iterator(); iterator.hasNext();) try { p = (Provisional) iterator.next(); p.close(); } catch (RuntimeException | Error | IOException e) { log.warn("Unable to close provisional " + p + " for " + Heatmap.this, e); } } }) .build(new CacheLoader<Pair<String, String>, Triplet<Provisional, Provisional, Provisional>>() { @Override public Triplet<Provisional, Provisional, Provisional> load(Pair<String, String> key) throws Exception { Provisional output = null, significant = null, rnk = null; try { output = Provisionals.file(); significant = Provisionals.file(); rnk = Provisionals.file(); Limma.execute(Heatmap.this, key.getValue0(), key.getValue1(), output, significant, rnk, "row"); return new Triplet<Provisional, Provisional, Provisional>(output, significant, rnk); } catch (RuntimeException | Error | IOException e) { if (output != null) output.close(); if (significant != null) significant.close(); if (rnk != null) rnk.close(); throw e; } } }); private final LoadingCache<Pair<String, String>, Triplet<Provisional, Provisional, Provisional>> limmaColumns = CacheBuilder .newBuilder().maximumSize(100).expireAfterWrite(20, TimeUnit.MINUTES).removalListener( new RemovalListener<Pair<String, String>, Triplet<Provisional, Provisional, Provisional>>() { @Override public void onRemoval( RemovalNotification<Pair<String, String>, Triplet<Provisional, Provisional, Provisional>> arg0) { log.debug("Ejecting limma analysis " + Heatmap.this + "(" + arg0.getKey() + ") from cache"); Provisional p = null; for (Iterator<Object> iterator = arg0.getValue().iterator(); iterator.hasNext();) try { p = (Provisional) iterator.next(); p.close(); } catch (RuntimeException | Error | IOException e) { log.warn("Unable to close provisional " + p + " for " + Heatmap.this, e); } } }) .build(new CacheLoader<Pair<String, String>, Triplet<Provisional, Provisional, Provisional>>() { @Override public Triplet<Provisional, Provisional, Provisional> load(Pair<String, String> key) throws Exception { Provisional output = null, significant = null, rnk = null; try { output = Provisionals.file(); significant = Provisionals.file(); rnk = Provisionals.file(); Limma.execute(Heatmap.this, key.getValue0(), key.getValue1(), output, significant, rnk, "column"); return new Triplet<Provisional, Provisional, Provisional>(output, significant, rnk); } catch (RuntimeException | Error | IOException e) { if (output != null) output.close(); if (significant != null) significant.close(); if (rnk != null) rnk.close(); throw e; } } }); public Heatmap exportColumnSelections(final String... selections) throws IOException { List<Integer> remap = new ArrayList<Integer>(new HashSet<Integer>() { private static final long serialVersionUID = 1L; { for (String selection : selections) addAll(getColumnSelection(selection, 0, data.getColumnDimension()).getIndices()); } }); Heatmap result = builder.reorderColumns(this, remap); result.summary = new MatrixSummary(result.data.getRowDimension(), summary.columns(), data.walkInOptimizedOrder(new RealMatrixPreservingVisitor() { private double max; @Override public void visit(int row, int column, double value) { if (max < value) max = value; } @Override public void start(int rows, int columns, int startRow, int endRow, int startColumn, int endColumn) { max = -Double.MAX_VALUE; } @Override public double end() { return max; } }), data.walkInOptimizedOrder(new RealMatrixPreservingVisitor() { private double min; @Override public void visit(int row, int column, double value) { if (min > value) min = value; } @Override public void start(int rows, int columns, int startRow, int endRow, int startColumn, int endColumn) { min = Double.MAX_VALUE; } @Override public double end() { return min; } }), false, false); return result; } public Heatmap exportRowSelections(final String... selections) throws IOException { List<Integer> remap = new ArrayList<Integer>(new HashSet<Integer>() { private static final long serialVersionUID = 1L; { for (String selection : selections) addAll(getRowSelection(selection, 0, data.getRowDimension()).getIndices()); } }); Heatmap result = builder.reorderRows(this, remap); result.summary = new MatrixSummary(result.data.getRowDimension(), summary.columns(), data.walkInOptimizedOrder(new RealMatrixPreservingVisitor() { private double max; @Override public void visit(int row, int column, double value) { if (max < value) max = value; } @Override public void start(int rows, int columns, int startRow, int endRow, int startColumn, int endColumn) { max = -Double.MAX_VALUE; } @Override public double end() { return max; } }), data.walkInOptimizedOrder(new RealMatrixPreservingVisitor() { private double min; @Override public void visit(int row, int column, double value) { if (min > value) min = value; } @Override public void start(int rows, int columns, int startRow, int endRow, int startColumn, int endColumn) { min = Double.MAX_VALUE; } @Override public double end() { return min; } }), false, false); return result; } /** * Constructs empty heatmap; this is not very useful as the Heatmap object is * immutable */ protected Heatmap() { } /** * Gets subset of the data * * @param startRow * @param endRow * @param startColumn * @param endColumn * @return */ public MatrixData getData(int startRow, int endRow, int startColumn, int endColumn) { if (startRow >= data.getRowDimension() || startColumn >= data.getColumnDimension()) return EMPTY_MATRIX_DATA; startRow = max(startRow, 0); startRow = min(startRow, data.getRowDimension() - 1); endRow = max(endRow, startRow); endRow = min(endRow, data.getRowDimension() - 1); startColumn = max(startColumn, 0); startColumn = min(startColumn, data.getColumnDimension() - 1); endColumn = max(endColumn, startColumn); endColumn = min(endColumn, data.getColumnDimension() - 1); return new MatrixData(data.getSubMatrix(startRow, endRow, startColumn, endColumn)); } /** * Get available row annotation types * * @return */ public Collection<String> getRowAnnotationTypes() { return rowAnnotations.getAttributes(); } /** * Get available column annotation types * * @return */ public Collection<String> getColumnAnnotationTypes() { return columnAnnotations.getAttributes(); } /** * Get subset of row annotations * * @param start * @param end * @param type * @return */ public List<MatrixAnnotation<?>> getRowAnnotation(int startIndex, int endIndex, String attribute) throws AnnotationNotFoundException { return rowAnnotations.getByIndex(startIndex, endIndex, attribute); } public List<MatrixAnnotation<?>> getRowAnnotation(int index) throws AnnotationNotFoundException { List<MatrixAnnotation<?>> result = new ArrayList<MatrixAnnotation<?>>(); for (String attribute : getRowAnnotationTypes()) result.addAll(rowAnnotations.getByIndex(index, index, attribute)); log.debug("Returning " + result.size() + " annotation objects for row " + index + " in " + this); return result; } public void setRowAnnotations(InputStream tsv) throws IOException { rowAnnotations.setAnnotations(tsv); } /** * Get subset of column annotations * * @param start * @param end * @param type * @return */ public List<MatrixAnnotation<?>> getColumnAnnotation(int startIndex, int endIndex, String attribute) throws AnnotationNotFoundException { return columnAnnotations.getByIndex(startIndex, endIndex, attribute); } public List<MatrixAnnotation<?>> getColumnAnnotation(int index) throws AnnotationNotFoundException { List<MatrixAnnotation<?>> result = new ArrayList<MatrixAnnotation<?>>(); for (String attribute : getColumnAnnotationTypes()) result.addAll(columnAnnotations.getByIndex(index, index, attribute)); log.debug("Returning " + result.size() + " annotation objects for column " + index + " in " + this); return result; } public void setColumnAnnotations(InputStream tsv) throws IOException { columnAnnotations.setAnnotations(tsv); } /** * Get all row selection ids * * @return */ public Collection<String> getRowSelectionIds() { return getSelectionIds(rowSelections); } /** * Get all column selection ids * * @return */ public Collection<String> getColumnSelectionIds() { return getSelectionIds(columnSelections); } /** * Get row selection indecis * * @param id * @param start * @param end * @return */ public MatrixSelection getRowSelection(String id, int start, int end) { return getSelection(rowSelections, start, end, id); } /** * Get column selection indecis * * @param id * @param start * @param end * @return */ public MatrixSelection getColumnSelection(String id, int start, int end) { return getSelection(columnSelections, start, end, id); } /** * Set row selection * * @param id * @param selection * @throws IndexOutOfBoundsException */ public void setRowSelection(String id, MatrixSelection selection) throws IndexOutOfBoundsException { setSelection(rowSelections, id, selection); } /** * Set columns selection * * @param id * @param selection * @throws IndexOutOfBoundsException */ public void setColumnSelection(String id, MatrixSelection selection) throws IndexOutOfBoundsException { setSelection(columnSelections, id, selection); } /** * Delete row selection * * @param id */ public void deleteRowSelection(String id) { deleteSelection(rowSelections, id); } /** * Delete column selection * * @param id */ public void deleteColumnSelections(String id) { deleteSelection(columnSelections, id); } public enum ClusteringAlgorhythm { EUCLEDIAN } public Heatmap clusterColumns(ClusteringAlgorhythm algorhythm) throws IOException { if (columnClusters == null) { RealMatrix data = transpose(this.data); Cluster root = cluster(data, algorhythm); Heatmap result = builder.reorderColumns(this, reorderedIndices(root)); result.summary = new MatrixSummary(summary.rows(), summary.columns(), summary.max(), summary.min(), summary.rowClustered(), true); result.columnClusters = JsonCluster.from(root); return result; } else return this; } public Heatmap clusterRows(ClusteringAlgorhythm algorhythm) throws IOException { if (rowClusters == null) { Cluster root = cluster(data, algorhythm); Heatmap result = builder.reorderRows(this, reorderedIndices(root)); result.summary = new MatrixSummary(summary.rows(), summary.columns(), summary.max(), summary.min(), true, summary.columnClustered()); rowClusters = JsonCluster.from(root); return result; } else return this; } public enum LimmaOutput { FULL, SIGNIFICANT } public List<LimmaResult> limmaRowsData(String experiment, String control, LimmaOutput type) throws IOException { try (BufferedReader reader = new BufferedReader(new FileReader(limmaRows(experiment, control, type)))) { reader.readLine(); // skip header List<LimmaResult> result = new ArrayList<>(); for (String line; (line = reader.readLine()) != null;) { String[] entry = line.split("\t"); result.add(new LimmaResult(entry[0], entry[1], entry[2], entry[3], entry[4])); } return result; } } public List<LimmaResult> limmaColumnsData(String experiment, String control, LimmaOutput type) throws IOException { try (BufferedReader reader = new BufferedReader(new FileReader(limmaColumns(experiment, control, type)))) { reader.readLine(); // skip header List<LimmaResult> result = new ArrayList<>(); for (String line; (line = reader.readLine()) != null;) { String[] entry = line.split("\t"); result.add(new LimmaResult(entry[0], entry[1], entry[2], entry[3], entry[4])); } return result; } } public File limmaRows(String experiment, String control, LimmaOutput type) throws IOException { try { return (File) limmaRows.get(new Pair<String, String>(experiment, control)).getValue(type.ordinal()); } catch (ExecutionException e) { throw new RuntimeException(e.getCause()); } } public File limmaColumns(String experiment, String control, LimmaOutput type) { try { return (File) limmaColumns.get(new Pair<String, String>(experiment, control)).getValue(type.ordinal()); } catch (ExecutionException e) { throw new RuntimeException(e.getCause()); } } public Collection<LimmaParameter> limmaCalculatedRows() { return Lambda.convert(limmaRows.asMap().keySet(), new Converter<Pair<String, String>, LimmaParameter>() { @Override public LimmaParameter convert(Pair<String, String> from) { return new LimmaParameter(from.getValue0(), from.getValue1()); } }); } public Collection<LimmaParameter> limmaCalculatedColumns() { return Lambda.convert(limmaColumns.asMap().keySet(), new Converter<Pair<String, String>, LimmaParameter>() { @Override public LimmaParameter convert(Pair<String, String> from) { return new LimmaParameter(from.getValue0(), from.getValue1()); } }); } public List<Integer> findByRow(AnnotationSearchTerm[] terms) { return rowAnnotations.find(terms); } public List<Integer> findByColumn(AnnotationSearchTerm[] terms) { return columnAnnotations.find(terms); } /* (non-Javadoc) * @see java.io.Closeable#close() */ @Override public void close() throws IOException { log.debug("Closing " + this); limmaRows.invalidateAll(); limmaRows.cleanUp(); limmaColumns.invalidateAll(); limmaColumns.cleanUp(); for (Closeable resource : new ArrayList<Closeable>() { private static final long serialVersionUID = 1L; { add(rowAnnotations); add(columnAnnotations); if (data instanceof Closeable) add((Closeable) data); } }) try { resource.close(); } catch (Throwable e) { log.warn("Unable to close " + resource + " for " + this, e); } } @Accessors(fluent = true, chain = true) public static class Builder { private @Getter @Setter boolean allowComments = false; private @Getter @Setter boolean allowEmptyLines = false; private @Getter @Setter boolean assumeSingleColumnAnnotation = true; private @Getter @Setter CellProcessor valueProcessor = new ConvertNullTo(NaN, new ParseDouble()); private @Getter @Setter CellProcessor annotationProcessor = null; private @Getter @Setter String delimiterRegex = "\t"; private @Getter @Setter List<String> columnAnnotationTypes = asList("column"); private @Autowired DataSource restDataSource; private @Getter @Setter List<String> rowAnnotationTypes = new AbstractList<String>() { @Override public String get(int index) { return "annotation-" + index; } @Override public int size() { return Integer.MAX_VALUE; } }; Heatmap reorderColumns(final Heatmap other, final List<Integer> newOrder) throws IOException { log.debug("Reordering columns: " + newOrder); Heatmap result = new Heatmap(); result.data = new HugeRealMatrix(new Iterator<Double>() { private final int rows = other.data.getRowDimension(); private final int columns = newOrder.size(); private final int entries = rows * columns; private int index = 0; private int row(int index) { return index / columns; } private int column(int index) { return index % columns; } @Override public boolean hasNext() { return index < entries; } @Override public Double next() { double result = other.data.getEntry(row(index), newOrder.get(column(index))); index++; return result; } @Override public void remove() { } }, newOrder.size()); result.rowAnnotations = other.rowAnnotations; result.columnAnnotations = new Annotations(result.universalId, AnnotationDimension.COLUMN, restDataSource); result.columnAnnotations.setAnnotations(new AbstractList<Map<String, ?>>() { @Override public Map<String, ?> get(final int index) { return new HashMap<String, Object>() { private static final long serialVersionUID = 1L; { try { for (MatrixAnnotation<?> annotation : other.getColumnAnnotation(index)) put(annotation.attribute(), annotation.value()); } catch (AnnotationNotFoundException e) { } } }; } @Override public int size() { return newOrder.size(); } }); result.builder = this; return result; } Heatmap reorderRows(final Heatmap other, final List<Integer> newOrder) throws IOException { Heatmap result = new Heatmap(); result.data = new HugeRealMatrix(new Iterator<Double>() { private final int rows = other.data.getRowDimension(); private final int columns = other.data.getColumnDimension(); private final int entries = rows * columns; private int index = 0; private int row(int index) { return index / columns; } private int column(int index) { return index % columns; } @Override public boolean hasNext() { return index < entries; } @Override public Double next() { double result = other.data.getEntry(newOrder.get(row(index)), column(index)); index++; return result; } @Override public void remove() { } }, newOrder.size()); result.columnAnnotations = other.columnAnnotations; result.rowAnnotations = new Annotations(result.universalId, AnnotationDimension.COLUMN, restDataSource); result.rowAnnotations.setAnnotations(new AbstractList<Map<String, ?>>() { @Override public Map<String, ?> get(final int index) { return new HashMap<String, Object>() { private static final long serialVersionUID = 1L; { try { for (MatrixAnnotation<?> annotation : other.getRowAnnotation(index)) put(annotation.attribute(), annotation.value()); } catch (AnnotationNotFoundException e) { } } }; } @Override public int size() { return newOrder.size(); } }); result.builder = this; result.summary = new MatrixSummary(other.summary.rows(), other.summary.columns(), other.summary.max(), other.summary.min(), true, other.summary.columnClustered()); return result; } public Heatmap build(final MultipartFile file) throws IOException { return build(file.getInputStream(), file.getSize(), file.getOriginalFilename()); } public Heatmap build(final InputStream input, final long size, final String name) throws IOException { log.debug("Building heatmap from " + size + " bytes of uploaded data"); BufferedReader reader = new BufferedReader(new InputStreamReader(new InputStream() { private final InputStream in = new BufferedInputStream(input); private final List<Integer> logUpdateThresholds = new ArrayList<Integer>( asList(10, 20, 30, 40, 50, 60, 70, 80, 90)); private boolean complete = false; private long count = 0; /* (non-Javadoc) * @see java.io.InputStream#read() */ @Override public int read() throws IOException { int result = in.read(); if (result < 0) { if (!complete) { complete = true; log.debug("Processing uploaded file " + name + " is complete"); } } else { count++; if (logUpdateThresholds.size() > 0) if (((double) count) * 100 / size > logUpdateThresholds.get(0)) { log.debug("Processing uploaded file " + name + " is " + logUpdateThresholds.get(0) + "% complete"); logUpdateThresholds.remove(0); } } return result; } })); String[] fields = reader.readLine().split(delimiterRegex); if (log.isDebugEnabled()) log.debug("Parsing matrix with header: " + Arrays.toString(fields)); final CellProcessor[] processors = new CellProcessor[fields.length]; int index = 0; for (; index < fields.length && "".equals(fields[index]); index++) processors[index] = annotationProcessor; final int lastRowAnnotationIndex = index; final List<Map<String, ?>> columnAnnotations = new ArrayList<Map<String, ?>>(); final List<Map<String, ?>> rowAnnotations = new ArrayList<Map<String, ?>>(); for (; index < fields.length; index++) { processors[index] = valueProcessor; columnAnnotations.add(new HashMap<String, String>() { private static final long serialVersionUID = 1L; private Map<String, ?> initialize(String annotation) { put(columnAnnotationTypes.get(0), annotation); return this; } }.initialize(fields[index])); } HugeRealMatrix data = null; try (final CsvListReader csvReader = new CsvListReader(reader, TAB_PREFERENCE)) { data = new HugeRealMatrix(new Iterator<Double>() { private Iterator<Object> current = null; @Override public void remove() { throw new UnsupportedOperationException(); } @Override public Double next() { if (!hasNext()) throw new NoSuchElementException(); else return (Double) current.next(); } @Override @SneakyThrows(IOException.class) public boolean hasNext() { if (current != null && current.hasNext()) return true; final List<Object> row = csvReader.read(processors); if (row == null) return false; rowAnnotations.add(new HashMap<String, String>() { private static final long serialVersionUID = 1L; { for (int index = 0; index < lastRowAnnotationIndex; index++) put(rowAnnotationTypes.get(index), row.get(index).toString()); } }); current = row.subList(lastRowAnnotationIndex, row.size()).iterator(); return true; } }, index - lastRowAnnotationIndex); Heatmap result = new Heatmap(); result.data = data; result.summary = new MatrixSummary(data.getRowDimension(), data.getColumnDimension(), data.walkInOptimizedOrder(new RealMatrixPreservingVisitor() { private double max; @Override public void visit(int row, int column, double value) { if (max < value) max = value; } @Override public void start(int rows, int columns, int startRow, int endRow, int startColumn, int endColumn) { max = -Double.MAX_VALUE; } @Override public double end() { return max; } }), data.walkInOptimizedOrder(new RealMatrixPreservingVisitor() { private double min; @Override public void visit(int row, int column, double value) { if (min > value) min = value; } @Override public void start(int rows, int columns, int startRow, int endRow, int startColumn, int endColumn) { min = Double.MAX_VALUE; } @Override public double end() { return min; } }), false, false); result.rowAnnotations = new Annotations(result.universalId, ROW, restDataSource); result.columnAnnotations = new Annotations(result.universalId, COLUMN, restDataSource); result.rowAnnotations.setAnnotations(rowAnnotations); result.columnAnnotations.setAnnotations(columnAnnotations); result.builder = this; return result; } catch (RuntimeException | Error e) { if (data != null) data.close(); throw e; } } } private Collection<String> getSelectionIds(List<Map<String, Map<String, String>>> dimension) { Set<String> result = new HashSet<>(); for (Map<String, Map<String, String>> index : dimension) if (index != null) result.addAll(index.keySet()); return result; } private MatrixSelection getSelection(List<Map<String, Map<String, String>>> dimension, int start, int end, String id) { List<Integer> indecis = new ArrayList<Integer>(); Map<String, String> attributes = null; for (int index = end; --index >= start;) if ((attributes = dimension.get(index).get(id)) != null) indecis.add(index); return new MatrixSelection(attributes, indecis); } private void setSelection(List<Map<String, Map<String, String>>> dimension, String id, MatrixSelection selection) { log.debug("Setting selection " + selection + " for heatmap " + this); for (int index : selection.getIndices()) dimension.get(index).put(id, selection.getAttributes()); } private void deleteSelection(List<Map<String, Map<String, String>>> dimension, String id) { for (Map<String, Map<String, String>> selections : dimension) selections.remove(id); } private static class SelectionHolderList extends ArrayList<Map<String, Map<String, String>>> { private static final long serialVersionUID = 1L; @Override @Synchronized public Map<String, Map<String, String>> get(int index) { for (; size() <= index; add(null)) ; Map<String, Map<String, String>> result = super.get(index); if (result == null) set(index, result = new HashMap<String, Map<String, String>>()); return result; } } private Cluster cluster(RealMatrix data, ClusteringAlgorhythm algorhythm) throws IOException { switch (algorhythm) { case EUCLEDIAN: return new EucledianDistanceClusterer().eucledian(data); default: throw new IllegalArgumentException(); } } public static class JsonCluster { public @Getter @Setter @JsonView int id; public @Getter @Setter @JsonView double d; public @Getter @Setter @JsonView JsonCluster[] children; public static JsonCluster from(Cluster cluster) { JsonCluster result = new JsonCluster(); result.setId(cluster.id()); result.setD(cluster.d()); result.setChildren(cluster.children() == null ? null : new JsonCluster[] { from(cluster.children()[0]), from(cluster.children()[1]) }); return result; } } public void toStream(final Object rowSeparator, final Object columnSeparator, final ObjectOutput out) throws IOException { data.walkInRowOrder(new RealMatrixPreservingVisitor() { @Override @SneakyThrows(IOException.class) public void visit(int row, int column, double value) { if (column == 0 && row != 0) out.writeObject(columnSeparator); else if (row != 0) out.writeObject(rowSeparator); out.writeObject(value); } @Override public void start(int rows, int columns, int startRow, int endRow, int startColumn, int endColumn) { } @Override public double end() { return 0; } }); } public void toStream(OutputStream out) throws IOException, AnnotationNotFoundException { // out.write ("id".getBytes ()); // for (int column = 0; column < getSummary ().columns (); column++) // out.write (("\t" + getColumnAnnotation (column).get (0).value // ()).getBytes ()); for (int row = 0; row < getSummary().rows(); row++) { out.write(("" + getRowAnnotation(row).get(0).value()).getBytes()); for (int column = 0; column < getSummary().columns(); column++) out.write(("\t" + data.getEntry(row, column)).getBytes()); out.write('\n'); } } private List<Integer> reorderedIndices(final Cluster cluster) { return new ArrayList<Integer>() { private static final long serialVersionUID = 1L; { visit(cluster); } private void visit(Cluster cluster) { if (cluster.children() != null) { visit(cluster.children()[0]); visit(cluster.children()[1]); } else add(cluster.contains().get(0)); } }; } private RealMatrix transpose(final RealMatrix original) { return new AbstractRealMatrix() { @Override public void setEntry(int row, int column, double value) throws OutOfRangeException { original.setEntry(column, row, value); } @Override public int getRowDimension() { return original.getColumnDimension(); } @Override public double getEntry(int row, int column) throws OutOfRangeException { return original.getEntry(column, row); } @Override public int getColumnDimension() { return original.getRowDimension(); } @Override public RealMatrix createMatrix(int rowDimension, int columnDimension) throws NotStrictlyPositiveException { return original.createMatrix(rowDimension, columnDimension); } @Override public RealMatrix copy() { final RealMatrix result = createMatrix(getRowDimension(), getColumnDimension()); walkInOptimizedOrder(new RealMatrixPreservingVisitor() { @Override public void visit(int row, int column, double value) { result.setEntry(row, column, value); } @Override public void start(int rows, int columns, int startRow, int endRow, int startColumn, int endColumn) { } @Override public double end() { return NaN; } }); return result; } }; } }