[med-svn] [Git][med-team/q2-feature-table][upstream] New upstream version 2024.5.0+dfsg

Michael R. Crusoe (@crusoe) gitlab at salsa.debian.org
Tue Jun 25 02:34:44 BST 2024



Michael R. Crusoe pushed to branch upstream at Debian Med / q2-feature-table


Commits:
43fd6906 by Michael R. Crusoe at 2024-06-25T02:52:53+02:00
New upstream version 2024.5.0+dfsg
- - - - -


9 changed files:

- ci/recipe/meta.yaml
- q2_feature_table/_summarize/_visualizer.py
- q2_feature_table/_summarize/summarize_assets/feature-frequency-detail.html
- q2_feature_table/_summarize/summarize_assets/index.html
- q2_feature_table/_summarize/summarize_assets/sample-frequency-detail.html
- + q2_feature_table/_summarize/summarize_assets/utils/util.js
- q2_feature_table/_version.py
- q2_feature_table/tests/test_summarize.py
- setup.py


Changes:

=====================================
ci/recipe/meta.yaml
=====================================
@@ -20,10 +20,8 @@ requirements:
     - python {{ python }}
     - scikit-bio {{ scikit_bio }}
     - biom-format {{ biom_format }}
-    - seaborn
-    # pinned here on 10.19.22 due to pkg int build failure on community
-    # due to matplotlib bug. will be resolved once 3.7.0 is released
-    - matplotlib =3.6.0
+    - seaborn {{ seaborn }}
+    - matplotlib {{ matplotlib }}
     - pandas {{ pandas }}
     - numpy
     # `ipywidgets` included to avoid ShimWarning from


=====================================
q2_feature_table/_summarize/_visualizer.py
=====================================
@@ -151,40 +151,42 @@ def summarize(output_dir: str, table: biom.Table,
         feature_frequencies_ax.get_figure().savefig(
             os.path.join(output_dir, 'feature-frequencies.png'))
 
-    sample_summary_table = q2templates.df_to_html(
-        sample_summary.apply('{:,}'.format).to_frame('Frequency'))
-    feature_summary_table = q2templates.df_to_html(
-        feature_summary.apply('{:,}'.format).to_frame('Frequency'))
+    sample_summary_json = pd.DataFrame(
+        sample_summary, columns=['Frequency']).to_json()
+    feature_summary_json = pd.DataFrame(
+        feature_summary, columns=['Frequency']).to_json()
 
     index = os.path.join(TEMPLATES, 'summarize_assets', 'index.html')
     context = {
         'number_of_samples': number_of_samples,
         'number_of_features': number_of_features,
         'total_frequencies': int(np.sum(sample_frequencies)),
-        'sample_summary_table': sample_summary_table,
-        'feature_summary_table': feature_summary_table,
+        'sample_summary_table': sample_summary_json,
+        'feature_summary_table': feature_summary_json,
     }
 
-    feature_qualitative_data = _compute_qualitative_summary(table)
-    sample_frequencies.sort_values(inplace=True, ascending=False)
-
-    sample_frequencies_json = pd.Series(["{:,}".format(int(x)) for x in
-                                         sample_frequencies])
-
-    feature_frequencies.sort_values(inplace=True, ascending=False)
+    # Create a JSON object containing the Sample Frequencies to build the
+    # table in sample-frequency-detail.html
+    #
+    # Cast to DataFrame to standardize with other tables
+    sample_frequencies_json = pd.DataFrame(
+        sample_frequencies, columns=['Frequency']).to_json()
 
-    feature_frequencies = feature_frequencies.astype(int) \
-        .apply('{:,}'.format).to_frame('Frequency')
+    # Create a JSON object containing the Feature Frequencies to build the
+    # table in feature-frequency-detail.html
+    feature_qualitative_data = _compute_qualitative_summary(table)
+    feature_frequencies = feature_frequencies.astype(int).to_frame('Frequency')
     feature_frequencies['# of Samples Observed In'] = \
-        pd.Series(feature_qualitative_data).astype(int).apply('{:,}'.format)
-    feature_frequencies_table = q2templates.df_to_html(feature_frequencies)
+        pd.Series(feature_qualitative_data).astype(int)
+    feature_frequencies_json = feature_frequencies.to_json()
+
     sample_frequency_template = os.path.join(
         TEMPLATES, 'summarize_assets', 'sample-frequency-detail.html')
     feature_frequency_template = os.path.join(
         TEMPLATES, 'summarize_assets', 'feature-frequency-detail.html')
 
     context.update({'max_count': sample_frequencies.max(),
-                    'feature_frequencies_table': feature_frequencies_table,
+                    'feature_frequencies_json': feature_frequencies_json,
                     'feature_qualitative_data': feature_qualitative_data,
                     'tabs': [{'url': 'index.html',
                               'title': 'Overview'},
@@ -193,10 +195,6 @@ def summarize(output_dir: str, table: biom.Table,
                              {'url': 'feature-frequency-detail.html',
                               'title': 'Feature Detail'}]})
 
-    # Create a JSON object containing the Sample Frequencies to build the
-    # table in sample-frequency-detail.html
-    sample_frequencies_json = sample_frequencies_json.to_json()
-
     templates = [index, sample_frequency_template, feature_frequency_template]
     context.update({'frequencies_list':
                     json.dumps(sorted(sample_frequencies.values.tolist()))})
@@ -211,6 +209,10 @@ def summarize(output_dir: str, table: biom.Table,
                                               'summarize_assets',
                                               'vega'),
                                  output_dir)
+    q2templates.util.copy_assets(os.path.join(TEMPLATES,
+                                              'summarize_assets',
+                                              'utils'),
+                                 output_dir)
     q2templates.render(templates, output_dir, context=context)
 
     plt.close('all')


=====================================
q2_feature_table/_summarize/summarize_assets/feature-frequency-detail.html
=====================================
@@ -1,9 +1,37 @@
-{% extends "tabbed.html" %}
-
+{% extends "tabbed.html" %} {% block head %}
+<script src="util.js"></script> {% endblock %}
 {% block tabcontent %}
-  <div class="row">
+<div class="row">
+  <div class="col-lg-12">
     <div class="col-lg-12">
-      {{ feature_frequencies_table }}
+      <table class="table table-striped table-hover" border="0">
+        <thead>
+          <tr style="text-align: right;">
+            <th></th>
+            <th>Frequency</th>
+            <th># of Samples Observed In</th>
+          </tr>
+        </thead>
+        <tbody id="table-body"></tbody>
+      </table>
     </div>
   </div>
+</div>
+
+<script id="table-data" type="application/json">
+{{ feature_frequencies_json }}
+</script>
+
+<script type="text/javascript">
+  const tableBody = document.getElementById("table-body");
+  const tableData = JSON.parse(document.getElementById("table-data").innerHTML);
+  const featureFrequencies = tableData["Frequency"];
+
+  const sortedFeatureIDs = Object.keys(featureFrequencies).sort(function(a, b) {
+    return sortIDs(a, b, featureFrequencies)
+  });
+
+  formatTable(tableBody, tableData, sortedFeatureIDs);
+</script>
+
 {% endblock %}


=====================================
q2_feature_table/_summarize/summarize_assets/index.html
=====================================
@@ -1,5 +1,5 @@
-{% extends "tabbed.html" %}
-
+{% extends "tabbed.html" %} {% block head %}
+<script src="util.js"></script> {% endblock %}
 {% block tabcontent %}
 <div class="row">
   <div class="col-lg-12">
@@ -10,22 +10,22 @@
         <table class="table table-striped table-hover">
           <thead>
             <tr>
-              <th>Metric</th>
-              <th>Sample</th>
+              <th>Summary Statistic</th>
+              <th>Value</th>
             </tr>
           </thead>
           <tbody>
             <tr>
               <th scope="row">Number of samples</th>
-              <td>{{ "{:,}".format(number_of_samples) }}</td>
+              <td id="number-of-samples"></td>
             </tr>
             <tr>
               <th scope="row">Number of features</th>
-              <td>{{ "{:,}".format(number_of_features) }}</td>
+              <td id="number-of-features"></td>
             </tr>
             <tr>
               <th scope="row">Total frequency</th>
-              <td>{{ "{:,}".format(total_frequencies) }}</td>
+              <td id="total-frequency"></td>
             </tr>
           </tbody>
         </table>
@@ -36,7 +36,15 @@
 
     <div class="row">
       <div class="col-lg-6">
-        {{ sample_summary_table }}
+        <table class="table table-striped table-hover">
+          <thead>
+            <tr>
+              <th></th>
+              <th>Frequency</th>
+            </tr>
+          </thead>
+          <tbody id="sample-table-body"></tbody>
+        </table>
       </div>
       {% if number_of_samples > 1 %}
       <div class="col-lg-6">
@@ -51,10 +59,19 @@
       {% endif %}
     </div>
 
+    <h1>Frequency per feature</h1>
+
     <div class="row">
       <div class="col-lg-6">
-        <h1>Frequency per feature</h1>
-        {{ feature_summary_table }}
+        <table class="table table-striped table-hover">
+          <thead>
+            <tr>
+              <th></th>
+              <th>Frequency</th>
+            </tr>
+          </thead>
+          <tbody id="feature-table-body"></tbody>
+        </table>
       </div>
       {% if number_of_features > 1 %}
       <div class="col-lg-6">
@@ -70,4 +87,34 @@
     </div>
   </div>
 </div>
+
+<script id="sample-table-data" type="application/json">
+{{ sample_summary_table }}
+</script>
+
+<script id="feature-table-data" type="application/json">
+{{ feature_summary_table }}
+</script>
+
+<script type="text/javascript">
+  const numberOfSamples = document.getElementById("number-of-samples");
+  numberOfSamples.innerText = formatter.format({{number_of_samples}});
+
+  const numberOfFeatures = document.getElementById("number-of-features");
+  numberOfFeatures.innerText = formatter.format({{number_of_features}});
+
+  const totalFrequency = document.getElementById("total-frequency");
+  totalFrequency.innerText = formatter.format({{total_frequencies}});
+
+  const sampleTableBody = document.getElementById("sample-table-body");
+  const sampleTableData = JSON.parse(document.getElementById("sample-table-data").innerHTML);
+
+  formatTable(sampleTableBody, sampleTableData, Object.keys(sampleTableData["Frequency"]).reverse());
+
+  const featureTableBody = document.getElementById("feature-table-body");
+  const featureTableData = JSON.parse(document.getElementById("feature-table-data").innerHTML);
+
+  formatTable(featureTableBody, featureTableData, Object.keys(featureTableData["Frequency"]).reverse());
+</script>
+
 {% endblock %}


=====================================
q2_feature_table/_summarize/summarize_assets/sample-frequency-detail.html
=====================================
@@ -1,6 +1,7 @@
 {% extends "tabbed.html" %} {% block head %}
 <script src="js/vega.min.js"></script>
 <script src="js/vega-embed.min.js"></script>
+<script src="util.js"></script>
 <link rel="stylesheet" type="text/css" href="css/spinkit.css" /> {% endblock %}
 {% block tabcontent %}
 
@@ -148,7 +149,7 @@
       <thead>
         <tr>
           <th scope="col">Sample ID</th>
-          <th scope="col"> Frequency </th>
+          <th scope="col">Frequency</th>
         </tr>
       </thead>
       <tbody id="table-body"></tbody>
@@ -175,56 +176,45 @@
   // when the viz loads the default description is displayed
   textField.innerHTML = defaultDescription;
 
-  var sampleFrequency = JSON.parse(document.getElementById("table-data").innerHTML);
+  let sampleTable = JSON.parse(document.getElementById("table-data").innerHTML);
+  let sampleFrequencies = sampleTable["Frequency"];
   // get object keys and store them in an ascending order based on the key value
   // this order is used to create the table rows
-  sortedSampleIDs = Object.keys(sampleFrequency).sort(function(a, b) {
-    var temp = sampleFrequency[a] - sampleFrequency[b];
-    // if two samples have the same number of features then we
-    // determine the order using the sample ID alphabetical order
-    if (temp == 0){
-      return b.localeCompare(a);
-    }
-
-  return temp;
-  });
-
-  sortedSampleIDs.forEach(function(element) {
-    var row = tableBody.insertRow(0);
-    var cell1 = row.insertCell(0);
-    var cell2 = row.insertCell(1);
-    cell1.innerHTML = element;
-    cell2.innerHTML = sampleFrequency[element];
-
+  sortedSampleIDs = Object.keys(sampleFrequencies).sort(function(a, b) {
+    return sortIDs(a, b, sampleFrequencies)
   });
 
+  formatTable(tableBody, sampleTable, sortedSampleIDs);
 
-  function updateTableandText(val) {
-    var retainedSampleCount = 0;
+  function updateTableandText(samplingDepth) {
+    let retainedSampleCount = 0;
 
     // start the counter at 1 to ignore the header row
     for (var i = 1; row = table.rows[i]; i++) {
-
-      if (Number(row.cells[1].innerHTML) < val) {
+      // The value in table is formatted as a string for display to the user,
+      // but the value in sampleFrequencies is formatted as an int for data
+      // manipulation, so we use the ID we are on in table to get the frequency
+      // from sampleFrequencies.
+      const sampleID = row.cells[0].innerText;
+      const sampleFrequency = sampleFrequencies[sampleID];
+
+      if (sampleFrequency < samplingDepth) {
         row.className = "danger";
       } else {
         row.className = "";
         retainedSampleCount += 1;
       }
     }
-    if (val == 0){
-
-    textField.innerHTML = defaultDescription;
-
+    if (samplingDepth == 0){
+      textField.innerHTML = defaultDescription;
     }
     else{
-      var retainedFeatureCount = retainedSampleCount * val;
-      textField.innerHTML = "Retained " + retainedFeatureCount.toLocaleString('en')
+      var retainedFeatureCount = retainedSampleCount * samplingDepth;
+      textField.innerHTML = "Retained " + formatter.format(retainedFeatureCount)
                                         + " (" + (retainedFeatureCount/totalFrequencies*100).toFixed(2) + "%) features in "
                                         + retainedSampleCount + " (" + (retainedSampleCount/sampleCount*100).toFixed(2)
                                         +  "%) samples at the specifed sampling depth.";
     }
-
   }
 
 
@@ -245,7 +235,6 @@
   function sliderHelperFunction(val){
     updateBoxVal(val);
     updateTableandText(val);
-
   }
 
 


=====================================
q2_feature_table/_summarize/summarize_assets/utils/util.js
=====================================
@@ -0,0 +1,33 @@
+// Formats the number in a manner based on the locale but using latin numerals
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat
+const formatter = new Intl.NumberFormat({numberingSystem: "latn"})
+
+
+function formatTable(tableBody, data, sortedKeys) {
+    for (const key of sortedKeys) {
+        let colIdx = 1;
+        let row = tableBody.insertRow(0);
+
+        let keyCell = row.insertCell(0)
+        keyCell.innerText = key;
+        keyCell.style.setProperty("text-align", "left");
+        keyCell.style.setProperty("font-weight", "bold");
+
+        for (const innerKey of Object.keys(data)) {
+            let dataCell = row.insertCell(colIdx++);
+            dataCell.innerText = formatter.format(data[innerKey][key]);
+        }
+    }
+}
+
+
+function sortIDs(a, b, data) {
+    const diff = data[a] - data[b];
+    // if two samples have the same number of features then we
+    // determine the order using the sample ID alphabetical order
+    if (diff == 0){
+      return b.localeCompare(a);
+    }
+
+    return diff;
+}


=====================================
q2_feature_table/_version.py
=====================================
@@ -23,9 +23,9 @@ def get_keywords():
     # setup.py/versioneer.py will grep for the variable names, so they must
     # each be defined on a line of their own. _version.py will just call
     # get_keywords().
-    git_refnames = " (tag: 2024.2.0, Release-2024.2)"
-    git_full = "8635d787a83854309dc6cb26afdc5dc5476db208"
-    git_date = "2024-02-16 21:57:35 +0000"
+    git_refnames = " (tag: 2024.5.0, Release-2024.5)"
+    git_full = "6b106f1aafe333144ee7f30918e5bb852639fc69"
+    git_date = "2024-05-29 04:15:53 +0000"
     keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
     return keywords
 


=====================================
q2_feature_table/tests/test_summarize.py
=====================================
@@ -9,6 +9,8 @@
 import os
 from unittest import TestCase, main
 import tempfile
+import re
+import json
 
 import skbio
 import biom
@@ -383,6 +385,23 @@ class SummarizeTests(TestCase):
             index_fp = os.path.join(output_dir, 'index.html')
             self.assertTrue(os.path.exists(index_fp))
 
+            sample_frequency_fp = os.path.join(output_dir,
+                                               'sample-frequency-detail.html')
+            self.assertTrue(os.path.exists(sample_frequency_fp))
+            rx = (r'<script id="table-data" type="application/json">' +
+                  r'\n.*[^}]*.*\n</script>')
+
+            with open(sample_frequency_fp) as fi:
+                text = fi.read()
+                tbl_rx = re.compile(rx)
+                tbl = tbl_rx.findall(text)[0].split('\n')[1].strip()
+
+                sample_ids = json.loads(tbl)['Frequency'].keys()
+
+                self.assertTrue('S1' in sample_ids)
+                self.assertTrue('S2' in sample_ids)
+                self.assertTrue('S3' in sample_ids)
+
     def test_frequency_ranges_are_zero(self):
         table = biom.Table(np.array([[25, 25, 25], [25, 25, 25]]),
                            ['O1', 'O2'],


=====================================
setup.py
=====================================
@@ -22,6 +22,7 @@ setup(
                       'summarize_assets/vega/licenses/*',
                       'summarize_assets/vega/js/*',
                       'summarize_assets/vega/css/*',
+                      'summarize_assets/utils/*',
                       'tabulate_seqs_assets/js/*',
                       'tabulate_seqs_assets/index.html'],
                   'q2_feature_table._core_features': [



View it on GitLab: https://salsa.debian.org/med-team/q2-feature-table/-/commit/43fd69066ad6c6a1931feaf35a4884eb8fb0694e

-- 
This project does not include diff previews in email notifications.
View it on GitLab: https://salsa.debian.org/med-team/q2-feature-table/-/commit/43fd69066ad6c6a1931feaf35a4884eb8fb0694e
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20240625/ee0a63fe/attachment-0001.htm>


More information about the debian-med-commit mailing list