lets-plot-kotlin icon indicating copy to clipboard operation
lets-plot-kotlin copied to clipboard

Heatmap with dendrogram

Open PoslavskySV opened this issue 2 years ago • 11 comments

Dear Colleagues,

we are very exited using your library in our projects and thank you for developing it. Our team is quite eager to know whether you have plans to add a dendrogram and heatmap with dendrogram in the upcoming releases? If you don't have such plans and since we need these kind of plots very much, probably you may give us a guide how we can implement these plots, then we will be able to make a proper PR.

Warm regards, Stanislav

PoslavskySV avatar Oct 27 '21 22:10 PoslavskySV

wow, that would be huge contribution! Do I understand correctly you are using lets-plot Kotlin API?

alshan avatar Oct 27 '21 23:10 alshan

yes, correct, we use Kotlin API

PoslavskySV avatar Oct 28 '21 00:10 PoslavskySV

in this event let me transfer you to the LPK project. Disregard scary notifications.

alshan avatar Oct 28 '21 00:10 alshan

We didn't plan to release dendrogram any time soon and if you are willing to put efforts in this task it would be great.

The 1st step would be implement dendrogram as a separate plot similar to the ggdendro package. I.e. using geom_segment() and perhaps geom_text().

Dendrogram API we can put to the "jetbrains.letsPlot.bistro" package next to CorrPlot.

Dont worry about plot orientation - we are releasing coord_flip() pretty soon.

The next step would be the heatmap combo. We don't have 'marginal plot' feature yet so unfortunately you will have to compute sizes and positions of plots manually and use GGBunch to combine heatmap and dendrograms in one figure.

How does it sound?

alshan avatar Oct 28 '21 01:10 alshan

Hi Igor,

thank you very much for a quick response. We looked in the sources of ggdendro and an example of CorrPlot. I think it is pretty straightforward, so we will try to implement dendrogram that way. But I'm still not sure how to use GGBunch to combine dendrogram and heatmap, so we'll investigate this further. Meanwhile, we'll start with dendrogram and come back if any questions arise. Once we will be ready, we open PR. Thank you very much for the help.

With best wishes, Stanislav

PoslavskySV avatar Oct 28 '21 22:10 PoslavskySV

Sounds great!

alshan avatar Oct 29 '21 00:10 alshan

GGBunch example: https://nbviewer.org/github/JetBrains/lets-plot-kotlin/blob/master/docs/examples/jupyter-notebooks/ggbunch.ipynb

alshan avatar Oct 29 '21 01:10 alshan

Hi Stanislav, how is dendrogram going? In v3.3.0 we've added ggmarginal() which could help to add dendrogram on a plot margin. Top/bottom margins should work right away. Left/right margin will require setting orientation="y" to the dendrogram geometry layer.

Doc: ggmarginal.

alshan avatar Jun 28 '22 20:06 alshan

Hi Igor,

I've implemented heatmap and dendrogram, but not in a conventional ggplot way, so I'm not sure how it could be helpful for the lets-plot project; there are fundamentally things which may not be done in a ggplot way (e.g. several different fill scales etc). Our library (just made it public, its in the wip, no docs still; the license will be changed later to apache) combines ggpubr, ggdendro, heatmap2 and other addon packages for ggplot.

I will be happy to discuss with you how we can make it more useful for lets-plot project!

Few examples:

Heatmaps with clustering

https://github.com/milaboratory/miplots/blob/master/src/test/kotlin/com/milaboratory/miplots/heatmap/HeatmapTest.kt

val plt = Heatmap(
    TestData.sampleMatrix(15, 15), "x", "y", "z",
    xOrder = Hierarchical(),
    yOrder = Hierarchical()
)
    .withBorder()
    .withColorKey(
        "xcat", Top,
        sep = 0.1, pallete = Categorical.Triadic9Bright,
        label = "X Cat", labelPos = Left, labelSep = 0.2, labelSize = 2.0
    )
    .withColorKey(
        "ycat", Right,
        sep = 0.1, pallete = Categorical.Triadic9Bright,
        label = "Y Cat", labelPos = Top, labelSep = 0.2, labelSize = 2.0, labelAngle = 90.0
    )
    .withDendrogram(Top)
    .withDendrogram(Right)
    .withLabels(Left, sep = 0.2)
    .withLabels(Bottom, sep = 0.2, angle = 45)
    .withFillLegend(Bottom, title = "Awesome Z label", textSize = 1.5, sizeUnit = "x")
image

Dendro

https://github.com/milaboratory/miplots/blob/master/src/test/kotlin/com/milaboratory/miplots/dendro/GGDendroTest.kt

val tree =
    root {
        repeat(2) {
            node {
                repeat(2) {
                    node {
                        repeat(2) {
                            node {
                                repeat(2) {
                                    node()
                                }
                            }
                        }
                    }
                }
            }
        }
    }

GGDendroPlot(
    tree,
    rpos = Left
) {
    color = label
}
    .withLabels(label)
    .withAlignmentLayer(alignment)
image

Statistics

https://github.com/milaboratory/miplots/blob/master/src/test/kotlin/com/milaboratory/miplots/stat/xdiscrete/statCompareMeansTest.kt

GGBoxPlot(
        toothGrowth,
        x = "dose",
        y = "len"
    ) {
        fill = "dose"
    } + statCompareMeans(
        allComparisons = true,
        method = TestMethod.KruskalWallis,
        multipleGroupsMethod = TestMethod.KruskalWallis
    ) + statCompareMeans()
image

PoslavskySV avatar Jun 29 '22 20:06 PoslavskySV

Hi Stanislav, congratulations, it looks awesome!

Not sure what the second + statCompareMeans() adds in the last (boxplot) test.

alshan avatar Jun 30 '22 17:06 alshan

The last + statCompareMeans() adds the overall p-value layer (between all three groups), and the first one adds pairwise p-values. Not sure that it is a good way to do, but I tried to mimicrate how it done in ggpubr.

PoslavskySV avatar Jun 30 '22 20:06 PoslavskySV