Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plotting of AbstractTrees; close #8 #15

Merged
merged 11 commits into from
Jan 3, 2019

Conversation

mkborregaard
Copy link
Member

@mkborregaard mkborregaard commented Sep 11, 2018

Do not merge yet

This PR awaits some improvements on the text formatting side in Plots, which may be a little while. However, I just wanted to show the progress and ideas, and harvest feedback. Feedback on anything - the interface, commands, defaults, line widths, font sizes, spacing etc very welcome. This is a crucial part of getting it right.

I've put some general considerations in #14, as there was things that was difficult for me to learn and adapt to.

The PR adds plotting functionality for AbstractTrees. I've essentially ignored the whole concept of branches (I think of them as property of their subtending nodes) and done everything on nodes. Everything is designed to fit in with the existing syntax of Plots.

One improvement I still want to do is to allow passing things like line_z (to color the lines) as Dict{Node, T} and have the recipe transform it into Vectors, so the user will not have to worry about ordering things the right way.

I've also made a ladderize function for better plotting to throw in, but I can't get the thing to work. I think it's something to do with the traversal order.

# Read the tree and load the plotting ocde
using Phylo
using Plots
include("/Users/michael/.julia/dev/Phylo/src/plot.jl")
gr(fmt = :png, lc = :black)

There are two basic layouts:
:dendrogram (the default)

passerines = open(t->parsenewick(t, NamedPolytomousTree), "delete/passerines.tree")
plot(passerines, size = (400, 1200), showtips = false)

skaermbillede 2018-09-11 kl 14 35 28

and :fan

hummers = open(t->parsenewick(t, NamedPolytomousTree), "delete/hummingbirds.tree")
plot(hummers, treetype = :fan)

skaermbillede 2018-09-11 kl 14 48 39

You can color the branches after a variable. So let us evolve a trait on the
hummingbird tree according to Brownian motion. I use a depth-first recursive mapping
function I've also put in the code

evolve(tree) = map_depthfirst((val, node) -> val + randn(), 0., tree, Float64)
trait = evolve(hummers)
plot(hummers, treetype = :fan, line_z = trait, lw = 5, showtips = false, c = :RdYlBu)

skaermbillede 2018-09-11 kl 14 51 48

Or let's highlight the oscines in the passerine tree

osc_color = map_depthfirst((val, node) -> node == "Oscines" ? :orange : val, :black, passerines)
plot(passerines, linecolor = osc_color, showtips = false, lw = 2, size = (400, 1200))

skaermbillede 2018-09-11 kl 15 17 13

It's also got support for markers (that can have different colors and layout as well), and basic support for branch annotations (though this isn't as nice as it should be).

plot(hummers, lc = :orange, lw = 5, size = (400, 1100), markersize = 4, series_annotations = text.(1:count(!isleaf, nodeiter(tree)), 6, :right, :bottom), msc = :white)

skaermbillede 2018-09-11 kl 15 10 02

Finally, it's already somewhat efficient (but there is plenty of room for avoiding allocations and making it faster still)

qian = open(t->parsenewick(t, NamedPolytomousTree), "delete/Qian2016.tree") # this takes several minutes
# PolytomousTree{DataFrames.DataFrame,Dict{String,Any}} with 31389 tips, 62776 nodes and 62775 branches.
# Leaf names are Blasia_pusilla, Lunularia_cruciata, Marchantia_polymorpha, Riccia_fluitans, Reboulia_hemisphaerica, ... [31383 omitted] ... and Salvia_aethiopis

@time display(plot(qian, showtips = false, treetype = :fan))
# 1.012017 seconds (3.81 M allocations: 169.401 MiB, 25.17% gc time)

skaermbillede 2018-09-11 kl 15 05 20

@mkborregaard mkborregaard changed the title WIP: plotting of AbstractTrees WIP: plotting of AbstractTrees; close #8 Sep 11, 2018
src/plot.jl Outdated
@@ -0,0 +1,222 @@
using RecipesBase
using Phylo
using Plots # now necessary for the labels :-(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using Plots will have to go before merging! But it's waiting for some functionality becoming available in RecipesBase.

@codecov
Copy link

codecov bot commented Sep 11, 2018

Codecov Report

Merging #15 into master will decrease coverage by 7.74%.
The diff coverage is 0%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #15      +/-   ##
==========================================
- Coverage   74.22%   66.47%   -7.75%     
==========================================
  Files          14       15       +1     
  Lines        1253     1402     +149     
==========================================
+ Hits          930      932       +2     
- Misses        323      470     +147
Impacted Files Coverage Δ
src/Phylo.jl 40% <ø> (ø) ⬆️
src/plot.jl 0% <0%> (ø)
src/Tree.jl 76.66% <0%> (+0.66%) ⬆️
src/trim.jl 93.75% <0%> (+3.12%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 4a140d6...54310ad. Read the comment docs.

@mkborregaard
Copy link
Member Author

I'll write tests before merging of course.

@coveralls
Copy link

coveralls commented Sep 11, 2018

Coverage Status

Coverage decreased (-7.6%) to 66.619% when pulling 0ddb2c5 on mkborregaard:plotting into 4a140d6 on richardreeve:master.

@richardreeve
Copy link
Member

It all looks great so far - I'll have a look in more detail tomorrow. In the meanwhile, if you want an easily accessible tree that you don't have to delete to work with for plotting that we can play with too, then in the repo test/H1N1.trees has two copies of the flu tree in a nexus file with a lot of metadata in it:

julia> tree = open(parsenexus, "test/H1N1.trees")["TREE1"]
[ Info: Created a tree called 'TREE1'
[ Info: Created a tree called 'TREE2'
BinaryTree{DataFrames.DataFrame,Dict{String,Any}} with 507 tips, 1013 nodes and 1012 branches.
Leaf names are H1N1_A_MIYAGI_3_2000, H1N1_A_PARMA_6_2008, H1N1_A_AKITA_86_2002, H1N1_A_DAKAR_14_1997, H1N1_A_EGYPT_84_2001, ... [501 omitted] ... and H1N1_A_HONGKONG_2070_1999

julia> getnoderecord(tree, "H1N1_A_PARMA_6_2008")
Dict{String,Any} with 12 entries:
  "rate_95%_HPD"   => [0.000800258, 0.00565223]
  "length_median"  => 1.0634
  "rate"           => 0.00295453
  "length_range"   => [0.272117, 2.10698]
  "height_range"   => [1.0, 1.0]
  "length"         => 1.06811
  "height"         => 1.0
  "rate_median"    => 0.00270733
  "rate_range"     => [0.00036974, 0.0152626]
  "length_95%_HPD" => [0.629248, 1.47885]
  "height_95%_HPD" => [1.0, 1.0]
  "height_median"  => 1.0

@mkborregaard mkborregaard changed the title WIP: plotting of AbstractTrees; close #8 plotting of AbstractTrees; close #8 Jan 2, 2019
@mkborregaard
Copy link
Member Author

I've added the fix to Plots in v0.22.2 (tagged) and updated this, so now this works and can be merged.

@richardreeve
Copy link
Member

I've merged this, but I haven't put out a release yet, because I have finally fixed the problem I had with Julia 1.0 crashing. If I don't manage to get everything working by the end of today I'll release this as is, but I'm hoping to at least revise the whole API and Interface so that all future changes will be fairly seamless...

@richardreeve richardreeve merged commit 42ef737 into EcoJulia:master Jan 3, 2019
@mkborregaard
Copy link
Member Author

oh sounds sweet!

@richardreeve
Copy link
Member

Yeah, well... time will tell!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants