Dendrograms

Overview

In general, dendrograms provide a hierarchical description of datasets, which may be used to identify clusters of similar objects or variables. This is known as hierarchical clustering. In the case of position-position-velocity (PPV) cubes, a dendrogram is a hierarchical decomposition of the emission in the cube. This decomposition was introduced by Rosolowsky et al. 2008 and Goodman et al. 2009 to calculate the multiscale properties of molecular gas in nearby clouds. The tree structure is comprised of branches and leaves. Branches are the connections, while leaves are the tips of the branches.

Burkhart et al. 2013 introduced two statistics for comparing the dendrograms of two cubes: the relationship between the number of leaves and branches in the tree versus the minimum branch length, and a histogram comparison of the peak intensity in a branch or leaf. The former statistic shows a power-law like turn-off with increasing branch length.

Using

The data in this tutorial are available here.

Requires the optional astrodendro package to be installed. See the documentation

Importing the dendrograms code, along with a few other common packages:

>>> from turbustat.statistics import Dendrogram_Stats
>>> from astropy.io import fits
>>> import astropy.units as u
>>> from astrodendro import Dendrogram  
>>> import matplotlib.pyplot as plt
>>> import numpy as np

And we load in the data:

>>> cube = fits.open("Design4_flatrho_0021_00_radmc.fits")[0]  

Before running the statistics side, we can first compute the dendrogram itself to see what we’re dealing with:

>>> d = Dendrogram.compute(cube.data, min_value=0.005, min_delta=0.1, min_npix=50, verbose=True)  
>>> ax = plt.subplot(111)  
>>> d.plotter().plot_tree(ax)  
>>> plt.ylabel("Intensity (K)")  
../../_images/design4_dendrogram.png

We see a number of leaves of varying height throughout the tree. Their minimum height is set by min_delta. As we increase this value, the tree becomes pruned: more and more structure will be merged, leaving only the brightest regions on the tree.

While this tutorial uses a PPV cube, a 2D image may also be used! The same tutorial code can be used for both, with changes needed for the choice of min_delta.

The statistics are computed through Dendrogram_Stats:

>>> dend_stat = Dendrogram_Stats(cube,
...                              min_deltas=np.logspace(-2, 0, 50),
...                              dendro_params={"min_value": 0.005, "min_npix": 50})  

There are two parameters that will change depending on the given data set: (1) min_deltas sets the minimum branch heights, which are completely dependent on the range of values within the data data, and (2) dendro_params, which is a dictionary setting other dendrogram parameters such as the minimum number of pixels a region must have (min_npix) and the minimum values of the data to consider (min_value). The settings given above are specific for these data and will need to be changed when using other data sets.

To run the statistics, we use run:

>>> dend_stat.run(verbose=True)  
                            OLS Regression Results
==============================================================================
Dep. Variable:                      y   R-squared:                       0.962
Model:                            OLS   Adj. R-squared:                  0.960
Method:                 Least Squares   F-statistic:                     825.6
Date:                Mon, 03 Jul 2017   Prob (F-statistic):           6.25e-25
Time:                        15:04:02   Log-Likelihood:                 34.027
No. Observations:                  35   AIC:                            -64.05
Df Residuals:                      33   BIC:                            -60.94
Df Model:                           1
Covariance Type:            nonrobust
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const          0.4835      0.037     13.152      0.000       0.409       0.558
x1            -1.1105      0.039    -28.733      0.000      -1.189      -1.032
==============================================================================
Omnibus:                        4.273   Durbin-Watson:                   0.287
Prob(Omnibus):                  0.118   Jarque-Bera (JB):                3.794
Skew:                          -0.800   Prob(JB):                        0.150
Kurtosis:                       2.800   Cond. No.                         4.39
==============================================================================
../../_images/design4_dendrogram_stats.png

On the left is the relationship between the value of min_delta and the number of features in the tree. On the right is a stack of histograms, showing the distribution of peak intensities for all values of min_delta. The results of the linear fit are also printed, where x1 is the slope of the power-law tail.

When using simulated data from a periodic box, the boundaries need to be handled across the edges. Setting periodic_bounds=True will treat the spatial dimensions as periodic. The simulated data shown here should have periodic_bounds enabled:

>>> dend_stat.run(verbose=True, periodic_bounds=True)  
                            OLS Regression Results
==============================================================================
Dep. Variable:                      y   R-squared:                       0.962
Model:                            OLS   Adj. R-squared:                  0.961
Method:                 Least Squares   F-statistic:                     808.6
Date:                Thu, 06 Jul 2017   Prob (F-statistic):           2.77e-24
Time:                        13:30:48   Log-Likelihood:                 33.415
No. Observations:                  34   AIC:                            -62.83
Df Residuals:                      32   BIC:                            -59.78
Df Model:                           1
Covariance Type:            nonrobust
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const          0.3758      0.039      9.744      0.000       0.297       0.454
x1            -1.1369      0.040    -28.437      0.000      -1.218      -1.055
==============================================================================
Omnibus:                        4.386   Durbin-Watson:                   0.267
Prob(Omnibus):                  0.112   Jarque-Bera (JB):                4.055
Skew:                          -0.823   Prob(JB):                        0.132
Kurtosis:                       2.611   Cond. No.                         4.60
==============================================================================
../../_images/design4_dendrogram_stats_periodic.png

The results have slightly changed. The left panel shows fewer features at nearly every value of \(\delta\) as regions along the edges are connected across the boundaries.

Creating the initial dendrogram is the most time-consuming step. To check the progress of building the dendrogram, dendro_verbose=True can be set in the previous call to give a progress bar and time-to-completion estimate.

Computing dendrograms can be time-consuming when working with large datasets. We can avoid recomputing a dendrogram by loading from an HDF5 file:

>>> dend_stat = Dendrogram_Stats.load_dendrogram("design4_dendrogram.hdf5",
...                                              min_deltas=np.logspace(-2, 0, 50))  

Saving the dendrogram structure is explained in the astrodendro documentation. The saved dendrogram must have min_delta set to the minimum of the given min_deltas. Otherwise pruning is ineffective.

If the dendrogram is stored in a variable (say you have just run it in the same terminal), you may pass the computed dendrogram into run:

>>> d = Dendrogram.compute(cube, min_value=0.005, min_delta=0.01, min_npix=50, verbose=True)  
>>> dend_stat = Dendrogram_Stats(cube, min_deltas=np.logspace(-2, 0, 50))  
>>> dend_stat.run(verbose=True, dendro_obj=d)  

Once the statistics have been run, the results can be saved as a pickle file:

>>> dend_stat.save_results(output_name="Design4_Dendrogram_Stats.pkl", keep_data=False)  

keep_data=False will avoid saving the entire cube and is the default setting.

Saving can also be enabled with run:

>>> dend_stat.run(save_results=True, output_name="Design4_Dendrogram_Stats.pkl")  

The results may then be reloaded:

>>> dend_stat = Dendrogram_Stats.load_results("Design4_Dendrogram_Stats.pkl")  

Note that the dendrogram and data are NOT saved, and only the statistic outputs will be accessible.