public String toGraphviz() { StringBuilder builder = new StringBuilder(); builder.append("digraph QuantileDigest {\n").append("\tgraph [ordering=\"out\"];"); final List<Node> nodes = new ArrayList<>(); postOrderTraversal( root, new Callback() { @Override public boolean process(Node node) { nodes.add(node); return true; } }); Multimap<Integer, Node> nodesByLevel = Multimaps.index( nodes, new Function<Node, Integer>() { @Override public Integer apply(Node input) { return input.level; } }); for (Map.Entry<Integer, Collection<Node>> entry : nodesByLevel.asMap().entrySet()) { builder.append("\tsubgraph level_" + entry.getKey() + " {\n").append("\t\trank = same;\n"); for (Node node : entry.getValue()) { builder.append( String.format( "\t\t%s [label=\"[%s..%s]@%s\\n%s\", shape=rect, style=filled,color=%s];\n", idFor(node), node.getLowerBound(), node.getUpperBound(), node.level, node.weightedCount, node.weightedCount > 0 ? "salmon2" : "white")); } builder.append("\t}\n"); } for (Node node : nodes) { if (node.left != null) { builder.append(format("\t%s -> %s;\n", idFor(node), idFor(node.left))); } if (node.right != null) { builder.append(format("\t%s -> %s;\n", idFor(node), idFor(node.right))); } } builder.append("}\n"); return builder.toString(); }