
<h2>Analysis on the dependency heaviness of R packages</h2> <p>R packages under analysis were retrieved from CRAN/Biocoductor on 2021-10-28. There are
<b><%=n_cran%></b> packages from CRAN and <b><%=n_bioc%></b> packages from
Bioconductor (<a href='https://bioconductor.org/packages/3.14/BiocViews.html#___Software' target='_blank'>bioc version 3.14</a>).</p>

<div id="tabs">
  <ul>
    <li><a href="#table">Heaviness table</a></li>
    <li><a href="#analysis">Heaviness analysis</a></li>
      </ul>

<div id="table"> <details><summary>Measures in the table</summary>
<br>
For a package denoted as <i>P</i>, its direct
dependency packages are listed in the <code>Depends</code>,
<code>Imports</code>, <code>LinkingTo</code>, <code>Suggestes</code> and
<code>Enhances</code> fields in its <code>DESCRIPTION</code> file. We define the following dependency categories
for package <i>P</i>:</p>

<ul>
	<li><b>Parent packages</b>: the packages listed in the <code>Depends</code>, <code>Imports</code>and <code>LinkingTo</code> fields
(packages in the red box in the following diagram). They are also called the
<b>strong direct</b> dependency packages of <i>P</i>. Parent packages are enforced to be installed when installing package <i>P</i>.</li>
<li><b>Strong dependency packages</b>: the total packages by recursively looking for parent
packages (package category <b>A</b> and <b>B</b>, plus packages in the red
box). They are also called <b>upstream packages</b>. Note strong dependency
packages contain parent packages. Strong dependency packages are enforced to be installed when installing package <i>P</i>.</li>
<li><b>All dependency packages</b>: the
total packages by recursively looking for parent packages, but on the level of
package <i>P</i>, parents for packages in <code>Suggests</code> and
<code>Enhances</code> are also included (package category <b>A</b>, <b>B</b>,
<b>C</b> and <b>D</b>, plus all packages listed in the box of package <i>P</i>). It
simulates when moving all <code>Suggests</code> and <code>Enhances</code> packages to
<code>Depends</code>/<code>Imports</code> of <i>P</i>, the number of strong dependency
packages.</li>
<li><b>Child packages</b>: the packages whose parent packages
include package <i>P</i> (package category <b>E</b>).</li>
<li><b>Downstream
packages</b>: the total packages by recursively looking for child packages
(package category <b>E</b> and <b>F</b>). Note downstream packages include
child packages.</li>
<li><b>Indirect downstream packages:</b> downstream packages excluding child packages (package category <b>F</b>).</li>
</ul>

<div style="padding:20px 206px;">
<%= paste(readLines(system.file("website", "dependency_diagram.svg", package = "pkgndep")), collapse = "\n") %>
</div>

<p>Next various measures for the heaviness are defined as follows:</p>

<ul> 
	<li><b>Heaviness from a parent</b>: If package <i>A</i> is a
parent of package <i>P</i> (i.e. package <i>P</i> strongly and directly depends on <i>A</i>),
<b>the heaviness of <i>A</i> on <i>P</i></b> is calculated as $n_1 - n_2$ where
$n_1$ is the number of parents of <i>P</i> and $n_2$ is
the number of parents of <i>P</i> <i>if moving <i>A</i> to <code style="font-style: normal;">Suggests</code> of <i>P</i></i>. In
other words, <b>the heaviness measures the number of additionally uniquely required
packages that <i>A</i> brings to <i>P</i></b>.</li>

<li>Since a package may have multiple parents, <b>max heaviness from parents</b> or <b>total heaviness from parents</b>
are used to measure how heavy parents are.</li>

<li><b>Heaviness of a package on all its child packages</b>: For package <i>P</i>,
assuming it has $K_c$ child packages and the $k^{th}$ child is denoted
as $A_k$. Denote $n_{1k}$ as the number of strong dependencies of
$A_k$ and $n_{2k}$ as the number of strong dependencies of $A_k$ <i>if moving <i>P</i>
to its</i> <code>Suggests</code>, the heaviness of <i>P</i> on its child packages is
calculated as $\frac{1}{K_c} \sum_k^{K_c}(n_{1k} - n_{2k})$. So here the heaviness measures
the average number of additional packages <i>P</i> brings to its child packages.</li>

<li><b>Heaviness of a package on all its downstream packages</b>: The
definition is similar to the heaviness of a package on all its child packages.
For package <i>P</i>,
assuming it has $K_d$ downstream packages and the $k^{th}$ downstream package is denoted
as $B_k$. Denote $n_{1k}$ as the number of strong dependencies of package
$B_k$. Since <i>P</i> can affect its downstream in an indirect manner, 
we recalculate the global dependency relations for all packages after moving 
<i>P</i> to all its child packages' <code>Suggests</code>. Then we denote 
$n_{2k}$ as the number of strong dependencies of $B_k$ in the modified dependency graph.
The heaviness of <i>P</i> on its downstream packages is
calculated as $\frac{1}{K_d} \sum_k^{K_d}(n_{1k} - n_{2k})$.</li>

<li><b>Heaviness of a package on all its indirect downstream packages</b>: The calculation is
the same as "heaviness on downstream packages" except now the child packages are excluded from
downstream packages. </li>
</ul>

<p>Here the measure of <b>Heaviness of a package on all its child packages</b> is more important to developers,
since it tells how many additional depedency packages are expected to be imported when they add a new parent package to their packages.</p>

<p>All these measures have a trend that small $K$ (i.e. number of parents, children or downstream packages) leads to high heaviness values.
Packages with small $K$ are in general of less interests. What is more important is to see, e.g. which package heavily affects a lot of 
children or downstream packages (i.e. with large $K$). Thus, the original definition of heaviness is <b>adjusted</b> correspondingly to decrease the heaviness more for smaller $K$.
A detailed explanation of the adjusted heaviness can be found in the tab <b>"Heaviness analysis"</b>.</p>

<p>The previous definition of heaviness only measures the effect of a single package. Here we define another measure called <b>"co-heaviness from parent package"</b> that measures the number of additional dependency packages simultaneously imported by two parents.
Denote <i>P</i>'s two strong parent packages as <i>A</i> and <i>B</i>, 
denote $S_A$ as the set of reduced dependency packages when only moving <i>A</i> to
<code>Suggests</code> of <i>P</i>, denote $S_B$ as the set of reduced dependency packages when
only moving <i>B</i> to <code>Suggests</code> of <i>P</i>, and denote $S_{AB}$ as the set of reduced dependency
packages when moving <i>A</i> and <i>B</i> together to <code>Suggests</code> of <i>P</i>, the co-heaviness of
<i>A</i>, <i>B</i> on <i>P</i> is calculatd as $ \left | S_{AB} \setminus  \cup (S_A, S_B) \right | $ where $|A|$
is the number of elements in set <i>A</i> and $A \setminus B$ is the set of elements in <i>A</i> but not in <i>B</i>.
</p>

</details>

<style>
details summary {
    cursor: pointer;
}
summary {
    display: list-item;
}
details > summary:first-of-type {
    display: list-item;
    counter-increment: list-item 0;
    list-style: inside disclosure-closed;
}
details[open] > summary:first-of-type {
    list-style-type: disclosure-open;
}
</style>

<br>

<div class="form-box">
<form method="get" class="form-inline">

<div>
	<label> <b>Order table rows by: </b> <a id="order-by-note" class="fake-link" title="Note: adjusted heaviness values are not listed in the table because the absolute values of them are meaningless. They are only used for reordering the table." style="font-size: 0.8em;">Where are these columns?</a><br>
		<input type="radio" name="order_by" value="adjusted_max_heaviness_from_parents" <%=ifelse(order_by == 'adjusted_max_heaviness_from_parents', 'checked=1', '')%> onchange="this.form.submit()"> Adjusted max heaviness from parent packages,<br>
      <input type="radio" name="order_by" value="adjusted_heaviness_on_children" <%=ifelse(order_by == 'adjusted_heaviness_on_children', 'checked=1', '')%> onchange="this.form.submit()"> Adjusted heaviness on child packages,<br>
      <% if(exclude_children) { %>
      <input type="radio" name="order_by" value="adjusted_heaviness_on_indirect_downstream" <%=ifelse(order_by == 'adjusted_heaviness_on_indirect_downstream', 'checked=1', '')%> onchange="this.form.submit()"> Adjusted heaviness on downstream packages (excluding child packages)
      <% } else { %>
      <input type="radio" name="order_by" value="adjusted_heaviness_on_downstream" <%=ifelse(order_by == 'adjusted_heaviness_on_downstream', 'checked=1', '')%>onchange="this.form.submit()"> Adjusted heaviness on downstream packages.
      <% } %>
        </label>
</div>
<br>

<div>
  <label> <b>Other settings: </b><br>
    <input id="input-reducible" type="checkbox" name="reducible" <%=ifelse(only_reducible, 'checked=1', '')%> onchange="this.form.submit()"> Only show packages whose parent's heaviness could be reduced, i.e. only a limited number of functions are imported from parent.
  </label>
</div>

<div>
<label>
	<input id="input-exclude-children" type="checkbox" name="exclude_children" <%=ifelse(exclude_children, 'checked=1', '')%> onchange="this.form.submit()"> Exclude children from downstream packages or only list indirect downsteam packages. A package's downstream packages also include its child packages, so a package's heaviness on downstream packages would be very similar to the heaviness on its child packages if the dependency depth to downstream is small (if the max dependency depth is 2, then all downstream packages are child packages). Thus, excluding children from downstream packages helps to discover the heaviness that indirectly affects downstream packages.
</label>
</div>


<script>
$(function() {
  $("#order-by-note").tooltip();
});
</script>

 </form>
<p><b>Legends:</b></p>
<p><span class="heaviness-high"><a>High heaviness</a></span> Packages with adjusted heaviness on child packages higher than <%=CUTOFF$adjusted_heaviness_on_children[2]%>.</p>
<p><span class="heaviness-median"><a>Median heaviness</a></span> Packages with adjusted heaviness on child packages between <%=CUTOFF$adjusted_heaviness_on_children[1]%> and <%=CUTOFF$adjusted_heaviness_on_children[2]%>.</p>
<p><span class="reducible" style="font-size:1em;">reducible</span> Packages whose parent's heaviness could be reduced, i.e. only a limited number of functions are imported from parent.</p>
<p><b>Columns:</b>
 <span style="width:20px;background-color:#FFFF10;padding:0px 4px;">&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;Heaviness from parent packages
 <span style="width:20px;background-color:#10FFFF;padding:0px 4px;">&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;Heaviness on child/downstream packages
 </p>
 <br>
<p>The full table of dependency heaviness analysis can be obtained by <code>df = pkgndep::all_pkg_stat_snapshot()</code>.</p>
</div>


<% reducible_str = ifelse(only_reducible, 'on', '') 
	 exclude_children_str = ifelse(exclude_children, 'on', '') 
if(exclude_children) {
	col.names = c(qq("<a href='?order_by=package&reducible=@{reducible_str}'>Package</a>"), 
			             "Repository",
				          qq("<a href='?order_by=n_by_strong&reducible=@{reducible_str}&exclude_children=@{exclude_children_str}'>Number of strong dependency packages</a>"), 
				          qq("<a href='?order_by=n_by_all&reducible=@{reducible_str}&exclude_children=@{exclude_children_str}'>Number of all dependency packages</a>"), 
				          qq("<a href='?order_by=n_parents&reducible=@{reducible_str}&exclude_children=@{exclude_children_str}'>Number of parent packages</a>"), 
				          qq("<a href='?order_by=max_heaviness_from_parents&reducible=@{reducible_str}&exclude_children=@{exclude_children_str}'>Max heaviness from parent packages</a>"), 
				          qq("<a href='?order_by=max_co_heaviness_from_parents&reducible=@{reducible_str}&exclude_children=@{exclude_children_str}'>Max co-heaviness from parent packages</a>"), 
				          qq("<a href='?order_by=heaviness_on_children&reducible=@{reducible_str}&exclude_children=@{exclude_children_str}'>Heaviness on child packages</a>"), 
				          qq("<a href='?order_by=n_children&reducible=@{reducible_str}&exclude_children=@{exclude_children_str}'>Number of child packages</a>"), 
				          qq("<a href='?order_by=adjusted_heaviness_on_indirect_downstream&reducible=@{reducible_str}&exclude_children=@{exclude_children_str}'>Heaviness on indirect downstream packages (excluding children)</a>"), 
				          qq("<a href='?order_by=n_indierct_downstream&reducible=@{reducible_str}&exclude_children=@{exclude_children_str}'>Number of indirect downstream packages (excluding children)</a>"))
} else {
	col.names = c(qq("<a href='?order_by=package&reducible=@{reducible_str}'>Package</a>"), 
			             "Repository",
				          qq("<a href='?order_by=n_by_strong&reducible=@{reducible_str}&exclude_children=@{exclude_children_str}'>Number of strong dependency packages</a>"), 
				          qq("<a href='?order_by=n_by_all&reducible=@{reducible_str}&exclude_children=@{exclude_children_str}'>Number of all dependency packages</a>"), 
				          qq("<a href='?order_by=n_parents&reducible=@{reducible_str}&exclude_children=@{exclude_children_str}'>Number of parent packages</a>"), 
				          qq("<a href='?order_by=max_heaviness_from_parents&reducible=@{reducible_str}&exclude_children=@{exclude_children_str}'>Max heaviness from parent packages</a>"), 
				          qq("<a href='?order_by=max_co_heaviness_from_parents&reducible=@{reducible_str}&exclude_children=@{exclude_children_str}'>Max co-heaviness from parent packages</a>"), 
				          qq("<a href='?order_by=heaviness_on_children&reducible=@{reducible_str}&exclude_children=@{exclude_children_str}'>Heaviness on child packages</a>"), 
				          qq("<a href='?order_by=n_children&reducible=@{reducible_str}&exclude_children=@{exclude_children_str}'>Number of child packages</a>"), 
				          qq("<a href='?order_by=heaviness_on_downstream&reducible=@{reducible_str}&exclude_children=@{exclude_children_str}'>Heaviness on downstream packages</a>"), 
				          qq("<a href='?order_by=n_downstream&reducible=@{reducible_str}&exclude_children=@{exclude_children_str}'>Number of downstream packages</a>"))
}
%>

<%= as.character(knitr::kable(df2, format = "html", row.names = FALSE, escape = FALSE, table.attr = "id='dependency-table' class='table table-striped'",
			col.names = col.names,
			align = c("l", rep("r", ncol(df2) - 1)))) %>
<style>
.heaviness-high {
	padding: 2px 4px;
	background-color: red;
	border-radius:  4px;
}

.heaviness-high a {
	color: white;
}

.heaviness-median {
	padding: 2px 4px;
	background-color: orange;
	border-radius:  4px;
}

.heaviness-median a {
	color: white;
}

.reducible {
	padding: 2px 4px;
	background-color: purple;
	border-radius:  4px;
	color:  white;
	font-size: 10px;
	vertical-align: middle;
}

.reducible a {
	color: white;
}

</style>
<script>
$(function() {
	$("#dependency-table thead th").filter( ":nth-child(3)" ).css("background-color", "#FFFF10");
	$("#dependency-table thead th").filter( ":nth-child(4)" ).css("background-color", "#FFFF10");
	$("#dependency-table thead th").filter( ":nth-child(5)" ).css("background-color", "#FFFF10");
	$("#dependency-table thead th").filter( ":nth-child(6)" ).css("background-color", "#FFFF10");
	$("#dependency-table thead th").filter( ":nth-child(7)" ).css("background-color", "#FFFF10");

	$("#dependency-table thead th").filter( ":nth-child(8)" ).css("background-color", "#10FFFF");
	$("#dependency-table thead th").filter( ":nth-child(9)" ).css("background-color", "#10FFFF");
	$("#dependency-table thead th").filter( ":nth-child(10)" ).css("background-color", "#10FFFF");
	$("#dependency-table thead th").filter( ":nth-child(11)" ).css("background-color", "#10FFFF");

	$("#dependency-table a").tooltip();
})
</script>

<% if(package == "") { %>

<% if(order_by == "adjusted_heaviness_on_children") order_by = "" %>

<div>
<form method="get" class="form-inline">
<select id="records_per_page" class="form-control" aria-label="Records per page" name="records_per_page">
  <option value="20" <%= ifelse(records_per_page == 20, 'selected', '') %>>20</option>
  <option value="50" <%= ifelse(records_per_page == 50, 'selected', '') %>>50</option>
  <option value="100" <%= ifelse(records_per_page == 100, 'selected', '') %>>100</option>
</select> records per page, showing <%=ind[1]%> to <%=ind[length(ind)]%> of <%=nrow(df)%> pacakges.
<input type="hidden" name="order_by" value="<%=order_by%>" />
<input type="hidden" name="reducible" value="<%=reducible_str%>" />
<input type="hidden" name="exclude_children" value="<%=exclude_children_str%>" />
</form>

 <script type="text/javascript">

  $(function() {
    $('#records_per_page').change(function() {
        this.form.submit();
    });
});
</script>

</div>



	<% nr = nrow(df)
	if(nr > records_per_page) { %>
		<%= page_select(page, ceiling(nr/records_per_page), qq("order_by=@{order_by}&reducible=@{reducible_str}&exclude_children=@{exclude_children_str}")) %>
	<% } %>

<% } %>

</div>

<div id="analysis">

<div id="analysis-content">
<p>Loading content...</p>
<script>
$.ajax({
  url: "global_heaviness_analysis"
}).done(function(html) {
  $("#analysis-content").html(html);
});
</script>

</div>
</div>
</div>

<script>
 $( function() {
    $( "#tabs" ).tabs();
  } );
  </script>

