Skip to content

Commit

Permalink
visualization - new GUI features (color mode), dot output
Browse files Browse the repository at this point in the history
  • Loading branch information
viq854 committed May 26, 2016
1 parent d0eeaf7 commit 91e2cfb
Show file tree
Hide file tree
Showing 14 changed files with 983 additions and 283 deletions.
Binary file added LICHeE/lib/VectorGraphics2D-0.10.jar
Binary file not shown.
Binary file modified LICHeE/release/lichee.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion LICHeE/src/lineage/AAFClusterer.java
Expand Up @@ -370,7 +370,7 @@ public Instances convertMatrixToWeka(double[][] data, int numObs, int numFeature
* Cluster of observation points
* Each cluster has an associated centroid point and a list of members
*/
protected class Cluster implements Serializable {
public class Cluster implements Serializable {

private static final long serialVersionUID = 1L;

Expand Down
56 changes: 47 additions & 9 deletions LICHeE/src/lineage/LineageEngine.java
Expand Up @@ -29,6 +29,7 @@

package lineage;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
Expand All @@ -48,6 +49,8 @@
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

import util.LineageDisplayConfig;
import util.Visualizer;
import lineage.AAFClusterer.Cluster;
import lineage.AAFClusterer.ClusteringAlgorithms;
import lineage.Parameters.Format;
Expand Down Expand Up @@ -135,22 +138,26 @@ public static void buildLineage(Args args) {
constrNetwork.displayNetwork();
}
if(spanningTrees.size() > 0) {
for(int i = 0; i < args.numShow; i++) {
if(spanningTrees.size() > i) {
constrNetwork.displayTree(spanningTrees.get(i), db.getSampleNames(), null, null);
} else {
break;
for(int i = 0; i < spanningTrees.size(); i++) {
if(i >= args.numShow && i > 0) break;
LineageDisplayConfig display = new LineageDisplayConfig(constrNetwork, spanningTrees.get(i), db.getSampleNames(), args.color);
if(i < args.numShow) {
Visualizer.showLineageTree(display);
}
if(i == 0 && args.outputDOTFileName != null) { // top tree only for simplicity
String parentDir = new File(args.outputDOTFileName).getParent();
writeTreeToDOTFile(display.toDOT(parentDir), db.getSampleNames(), args);
}
}
// 8. persistent storage
// 8. persistent storage
if(args.numSave > 0) {
writeTreesToTxtFile(constrNetwork, spanningTrees, db.getSampleNames(), args);
}
}
}

///// I/O /////

private static void writeTreesToTxtFile(PHYNetwork net, ArrayList<PHYTree> trees, ArrayList<String> sampleNames, Args args) {
String treeFileName = args.outputFileName;
try {
Expand Down Expand Up @@ -181,6 +188,19 @@ private static void writeTreesToTxtFile(PHYNetwork net, ArrayList<PHYTree> trees
}
}

private static void writeTreeToDOTFile(String dotTree, ArrayList<String> sampleNames, Args args) {
String treeFileName = args.outputDOTFileName;
try {
FileWriter fw = new FileWriter(treeFileName);
fw.write(dotTree);
fw.close();
} catch (IOException e) {
e.printStackTrace();
System.err.println("Failed to write to the file: " + treeFileName);
System.exit(-1);
}
}

// ---- LAUNCH ----

private static final String TREES_TXT_FILE_EXTENSION = ".trees.txt";
Expand All @@ -199,6 +219,9 @@ public static void main(String[] args) {
options.addOption("s", "save", true, "Maximum number of output trees to save (default: 1)");
options.addOption("showNetwork", "net", false, "Display the constraint network");
options.addOption("showTree", "tree", true, "Number of top-ranking trees to display (default: 0)");
options.addOption("color", false, "Enable lineage tree visualization in color mode");
options.addOption("dot", false, "Enable DOT file export of the top-scoring tree for Graphviz visualization (saved by default to: input file with suffix .dot)");
options.addOption("dotFile", true, "DOT file path");

// SSNV filtering / calling
options.addOption("maxVAFAbsent", "absent", true, "Maximum VAF to robustly consider an SSNV as absent from a sample [required without -sampleProfile]");
Expand Down Expand Up @@ -231,6 +254,9 @@ public static void main(String[] args) {
optionsList.add(options.getOption("s"));
optionsList.add(options.getOption("net"));
optionsList.add(options.getOption("tree"));
optionsList.add(options.getOption("color"));
optionsList.add(options.getOption("dot"));
optionsList.add(options.getOption("dotFile"));
optionsList.add(options.getOption("maxVAFAbsent"));
optionsList.add(options.getOption("minVAFPresent"));
optionsList.add(options.getOption("maxVAFValid"));
Expand Down Expand Up @@ -271,6 +297,13 @@ public static void main(String[] args) {
} else {
params.outputFileName = params.inputFileName + TREES_TXT_FILE_EXTENSION;
}
if(cmdLine.hasOption("dot")) {
params.outputDOTFileName = params.inputFileName + ".dot";
}
if(cmdLine.hasOption("dotFile")) {
params.outputDOTFileName = cmdLine.getOptionValue("dotFile");
}

if(cmdLine.hasOption("clustersFile")) {
params.clustersFileName = cmdLine.getOptionValue("clustersFile");
}
Expand All @@ -294,6 +327,9 @@ public static void main(String[] args) {
if(cmdLine.hasOption("s")) {
params.numSave = Integer.parseInt(cmdLine.getOptionValue("s"));
}
if(cmdLine.hasOption("color")) {
params.color = true;
}

if(cmdLine.hasOption("maxVAFAbsent")) {
Parameters.MAX_VAF_ABSENT = Double.parseDouble(cmdLine.getOptionValue("maxVAFAbsent"));
Expand Down Expand Up @@ -368,8 +404,9 @@ public static void main(String[] args) {
protected static class Args {
// --- 'build' command ---
String inputFileName;
String outputFileName;
String clustersFileName;
String outputFileName;
String outputDOTFileName;
String clustersFileName;
int normalSampleId = 0;
String cnvFileName;
String annFileName;
Expand All @@ -384,6 +421,7 @@ protected static class Args {
// flags
boolean showNetwork = false;
boolean verbose = false;
boolean color = false;
}

protected static class LogFormatter extends Formatter {
Expand Down
89 changes: 6 additions & 83 deletions LICHeE/src/lineage/PHYNetwork.java
Expand Up @@ -66,23 +66,23 @@ public class PHYNetwork implements Serializable {
private static final long serialVersionUID = 1L;

/** Nodes in the graph divided by levels (number of samples node SNVs occurred in) */
protected HashMap<Integer, ArrayList<PHYNode>> nodes;
public HashMap<Integer, ArrayList<PHYNode>> nodes;

/** Nodes in the graph indexed by their unique ID */
protected transient HashMap<Integer, PHYNode> nodesById;
public transient HashMap<Integer, PHYNode> nodesById;

/** Adjacency map of nodes to the their neighbors/children */
protected transient HashMap<PHYNode, ArrayList<PHYNode>> edges;
public transient HashMap<PHYNode, ArrayList<PHYNode>> edges;

/** Total number of nodes in the graph.
* During construction: used as a counter to assign unique IDs to nodes */
protected int numNodes;
public int numNodes;

/** Total number of edges in the graph */
protected int numEdges;
public int numEdges;

/** Total number of tissue samples */
protected int numSamples;
public int numSamples;

private static Logger logger = LineageEngine.logger;

Expand Down Expand Up @@ -780,83 +780,6 @@ public void displayNetwork() {
Visualizer.showNetwork(g, nodeLabels);
}

/** Displays a spanning tree of the network */
public void displayTree(PHYTree t, ArrayList<String> sampleNames, HashMap<String, ArrayList<SNVEntry>> snvsByTag, String fileOutputName) {
DirectedGraph<Integer, Integer> g = new DirectedSparseGraph<Integer, Integer>();
HashMap<Integer, String> nodeLabels = new HashMap<Integer, String>();
HashMap<Integer, PHYNode> nodeObj = new HashMap<Integer, PHYNode>();

int edgeId = 0;
for (PHYNode n : t.treeEdges.keySet()) {
g.addVertex(n.getNodeId());
nodeLabels.put(n.getNodeId(), n.getLabel());
nodeObj.put(n.getNodeId(), n);
for(PHYNode n2 : t.treeEdges.get(n)) {
if(!g.containsVertex(n2.getNodeId())) {
g.addVertex(n2.getNodeId());
nodeLabels.put(n2.getNodeId(), n2.getLabel());
nodeObj.put(n2.getNodeId(), n2);
}
g.addEdge(edgeId, n.getNodeId(), n2.getNodeId(), EdgeType.DIRECTED);
edgeId++;
}
}

// add sample leaves
for(int i = 0; i < numSamples; i++) {
PHYNode n = new PHYNode(0, i, numNodes + i);
g.addVertex(-n.getNodeId());
nodeLabels.put(-n.getNodeId(), sampleNames.get(i));
nodeObj.put(-n.getNodeId(), n);

// find a parent in the closest higher level
boolean found = false;
ArrayList<PHYNode> parents = new ArrayList<PHYNode>();
ArrayList<PHYNode> sameLevelParents = new ArrayList<PHYNode>();
for(int j = n.getLevel() + 1; j <= numSamples; j++) {
ArrayList<PHYNode> fromLevelNodes = nodes.get(j);
if(fromLevelNodes == null) continue;
for(PHYNode n2 : fromLevelNodes) {
if(n2.getAAF(i) > 0) {
boolean addEdge = true;
for(PHYNode p : parents) {
if(t.isDescendent(n2, p)) {
addEdge = false;
break;
}
}
if(addEdge) {
sameLevelParents.add(n2);
parents.add(n2);
found = true;
}
}
}
// remove nodes that are in same level that are connected
ArrayList<PHYNode> toRemove = new ArrayList<PHYNode>();
for(PHYNode n1 : sameLevelParents) {
for(PHYNode n2 : sameLevelParents) {
if(t.isDescendent(n1, n2)) {
toRemove.add(n1);
}
}
}
sameLevelParents.removeAll(toRemove);

for(PHYNode n2 : sameLevelParents) {
g.addEdge(edgeId, n2.getNodeId(), -n.getNodeId());
edgeId++;
}
sameLevelParents.clear();
}
if(!found) {
g.addEdge(edgeId, 0, -n.getNodeId());
edgeId++;
}
}
Visualizer.showLineageTree(g, nodeLabels, snvsByTag, fileOutputName, nodeObj, t, this, sampleNames);
}


/**
* Returns a string representation of the graph
Expand Down
42 changes: 36 additions & 6 deletions LICHeE/src/lineage/PHYTree.java
Expand Up @@ -44,8 +44,8 @@
public class PHYTree implements Comparable<PHYTree>, Serializable {
private static final long serialVersionUID = 1L;

protected ArrayList<PHYNode> treeNodes;
protected HashMap<PHYNode, ArrayList<PHYNode>> treeEdges;
public ArrayList<PHYNode> treeNodes;
public HashMap<PHYNode, ArrayList<PHYNode>> treeEdges;
protected double errorScore = -1;

public PHYTree() {
Expand Down Expand Up @@ -114,6 +114,10 @@ public boolean containsEdge(PHYNode from, PHYNode to) {
return false;
}

public PHYNode getRoot() {
return treeNodes.get(0);
}

/**
* Returns a copy of the tree
*/
Expand Down Expand Up @@ -237,21 +241,23 @@ public int compareTo(PHYTree t) {
*/
public String getLineage(int sampleId, String sampleName) {
StringBuilder lineage = new StringBuilder();
String indent = "";
lineage.append(sampleName + ":\n");
lineage.append("\tSample lineage decomposition: ");
lineage.append(sampleName + "\n");
lineage.append("GL\n");

// traverse the tree starting from the root in DFS order
String indent = "";
for(PHYNode n : treeEdges.get(treeNodes.get(0))) {
getLineageHelper(lineage, indent, n, sampleId);
}
return lineage.toString();
}

private void getLineageHelper(StringBuilder lineage, String indent, PHYNode n, int sampleId) {
indent += " ";
//indent += " ";
indent += ".....";

DecimalFormat df = new DecimalFormat("#.##");
DecimalFormat df = new DecimalFormat("#.###");
if(n.getSNVGroup().containsSample(sampleId)) {
lineage.append(indent + n.getSNVGroup().getTag() + ": " + df.format(n.getAAF(sampleId)) + " [" + df.format(n.getStdDev(sampleId)) + "]\n");
}
Expand All @@ -261,4 +267,28 @@ private void getLineageHelper(StringBuilder lineage, String indent, PHYNode n, i
}
}
}

public void getLineageClusters(ArrayList<PHYNode> path, ArrayList<ArrayList<PHYNode>> clones, PHYNode n, int sampleId) {
if(n.getSNVGroup() != null && n.getSNVGroup().containsSample(sampleId)) {
path.add(n);
} else if(n.getSNVGroup() != null && !n.getSNVGroup().containsSample(sampleId)) {
return;
}
if(treeEdges.get(n) != null) {
for(PHYNode nbr : treeEdges.get(n)) {
int size1 = clones.size();
ArrayList<PHYNode> clone = new ArrayList<PHYNode>(path);
getLineageClusters(new ArrayList<PHYNode>(path), clones, nbr, sampleId);
int size2 = clones.size();
if(size1 == size2) {
if(nbr.getSNVGroup() != null && nbr.getSNVGroup().containsSample(sampleId)) {
clone.add(nbr);
clones.add(clone);
}
}
}
} else {
clones.add(new ArrayList<PHYNode>(path));
}
}
}
4 changes: 4 additions & 0 deletions LICHeE/src/lineage/SNVGroup.java
Expand Up @@ -143,6 +143,10 @@ public int getNumSamplesTotal() {
return tag.length();
}

public int[] getSampleIds() {
return sampleIndex;
}

public int getNumSNVs() {
return snvs.size();
}
Expand Down

0 comments on commit 91e2cfb

Please sign in to comment.