diff --git a/ChangeLog.md b/ChangeLog.md index 197b5dde..43a37278 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -13,6 +13,7 @@ Emperor 0.9.3-dev (changes since Emperor 0.9.2 go here) * Add toggle visible button (`Invert Selected`) under the `Visibility` tab, this button will change hidden categories to visible and vice-versa. * Supports both NumPy 1.7 and 1.8. * Depends on scikit-bio-dev. +* Emperor provides a Python object that is IPython aware (emperor.Emperor) that will display a usable plot from within the IPython notebook. *Bug Fixes* diff --git a/emperor/__init__.py b/emperor/__init__.py index b5fb1dc4..19da017e 100644 --- a/emperor/__init__.py +++ b/emperor/__init__.py @@ -10,4 +10,6 @@ __email__ = "antgonza@gmail.com" __status__ = "Development" -__all__ = ['biplots', 'format', 'filter', 'sort', 'util'] +from emperor.core import Emperor + +__all__ = ['Emperor', 'biplots', 'format', 'filter', 'sort', 'util'] diff --git a/emperor/core.py b/emperor/core.py new file mode 100644 index 00000000..e26285ae --- /dev/null +++ b/emperor/core.py @@ -0,0 +1,143 @@ +r""" +Emperor 3D PCoA viewer +====================== + +This module provides an Object to interact and visualize an Emperor plot +from the IPython notebook. + +Classes +------- +.. autosummary:: + Emperor + +""" +from __future__ import division + +from emperor.format import (format_mapping_file_to_js, format_pcoa_to_js, + format_taxa_to_js, format_vectors_to_js, + format_comparison_bars_to_js, + EMPEROR_HEADER_HTML_STRING, + format_emperor_html_footer_string) + +# we are going to use this remote location to load external resources +RESOURCES_URL = 'http://emperor.colorado.edu/master/make_emperor/emperor_outpu\ +t/emperor_required_resources' + +__author__ = "Yoshiki Vazquez Baeza" +__copyright__ = "Copyright 2013, The Emperor Project" +__credits__ = ["Yoshiki Vazquez Baeza"] +__license__ = "BSD" +__version__ = "0.9.3-dev" +__maintainer__ = "Yoshiki Vazquez Baeza" +__email__ = "yoshiki89@gmail.com" +__status__ = "Development" + +class Emperor(object): + """Display principal coordinates analysis plots + + Use this object to interactively display a PCoA plot using the Emperor + GUI. IPython provides a rich display system that will let you display a + plot inline, without the need of creating a temprorary file or having to + write to disk. + + Parameters + ---------- + ordination: skbio.maths.stats.ordination.OrdinationResults + Object containing the computed values for an ordination method in + scikit-bio. + mapping_file_data: list of list objects + Metadata mapping file used to color the plot. + mapping_file_headers: list of str objects + List of strings representing the header names of the + `mapping_file_data`. All names should be unique. + + Examples + -------- + Create an Emperor object and display it from the IPython notebook: + + >>> data = [['PC.354', 'Control', '20061218', 'Control_mouse_I.D._354'], + ... ['PC.355', 'Control', '20061218', 'Control_mouse_I.D._355'], + ... ['PC.356', 'Control', '20061126', 'Control_mouse_I.D._356'], + ... ['PC.481', 'Control', '20070314', 'Control_mouse_I.D._481'], + ... ['PC.593', 'Control', '20071210', 'Control_mouse_I.D._593'], + ... ['PC.607', 'Fast', '20071112', 'Fasting_mouse_I.D._607'], + ... ['PC.634', 'Fast', '20080116', 'Fasting_mouse_I.D._634'], + ... ['PC.635', 'Fast', '20080116', 'Fasting_mouse_I.D._635'], + ... ['PC.636', 'Fast', '20080116', 'Fasting_mouse_I.D._636']] + >>> headers = ['SampleID', 'Treatment', 'DOB', 'Description'] + >>> ordination = OrdinationResults.from_file('unweighted_unifrac_pc.txt') + + Now import the Emperor object and display it using IPython, note that this + call will have no effect under an interactive Python session: + + >>> from emperor import Emperor + >>> Emperor(ordination, data, headers) + + Notes + ----- + This object currently does not support the full range of actions that the + GUI does support and should be considered experimental at the moment. + + References + ---------- + .. [1] EMPeror: a tool for visualizing high-throughput microbial community + data Vazquez-Baeza Y, Pirrung M, Gonzalez A, Knight R. Gigascience. 2013 + Nov 26;2(1):16. + + """ + def __init__(self, ordination, mapping_file_data, mapping_file_headers): + self.ordination = ordination + self.mapping_file_data = mapping_file_data + self.mapping_file_headers = mapping_file_headers + self.ids = [s[0] for s in mapping_file_data] + self._html = None + + def __str__(self): + if self._html is None: + self._make_emperor() + return self._html + + def _repr_html_(self): + """Used to be displayed in the IPython notebook""" + + # we import here as IPython shouldn't be a dependency of Emperor + # however if this method is called it will be from an IPython notebook + # otherwise the developer is responsible for calling this method + from IPython.display import display, HTML + + # this provides a string representation that's independent of the + # filesystem, it will instead retrieve them from the official website + output = str(self).replace('emperor_required_resources', + RESOURCES_URL) + + # thanks to the IPython devs for helping me figure this one out + return display(HTML(output), metadata=dict(isolated=True)) + + def _make_emperor(self): + """Private method to build an Emperor HTML string""" + pcoa_string = format_pcoa_to_js(self.ids, + self.ordination.site, + self.ordination.eigvals, + self.ordination.proportion_explained) + + # we pass the mapping file headers twice so nothing is filtered out + mf_string = format_mapping_file_to_js(self.mapping_file_data, + self.mapping_file_headers, + self.mapping_file_headers) + + # A lot of this is going to be empty because we don't really need any + # of it + footer = format_emperor_html_footer_string(False, False, False, False) + taxa = format_taxa_to_js([], [], []) + bars = format_comparison_bars_to_js([], [], 0) + vectors = format_vectors_to_js([], [], [], [], None) + + # build the HTML string + output = [EMPEROR_HEADER_HTML_STRING, mf_string, pcoa_string, taxa, + bars, vectors, footer] + + # add the remote resources + _emperor = '\n'.join(output) + + self._html = _emperor + diff --git a/tests/test_core.py b/tests/test_core.py new file mode 100755 index 00000000..3175d353 --- /dev/null +++ b/tests/test_core.py @@ -0,0 +1,366 @@ +#!/usr/bin/env python +# File created on 20 Jul 2014 +from __future__ import division + +__author__ = "Yoshiki Vazquez Baeza" +__copyright__ = "Copyright 2013, The Emperor Project" +__credits__ = ["Yoshiki Vazquez Baeza"] +__license__ = "BSD" +__version__ = "0.9.3-dev" +__maintainer__ = "Yoshiki Vazquez Baeza" +__email__ = "yoshiki89@gmail.com" +__status__ = "Development" + +from unittest import TestCase, main + +from StringIO import StringIO +from skbio.math.stats.ordination import OrdinationResults + +from emperor.core import Emperor + +class TopLevelTests(TestCase): + def setUp(self): + or_f = StringIO(PCOA_STRING) + self.ord_res = OrdinationResults.from_file(or_f) + + self.data = [['PC.354', 'Control', '20061218', 'Ctrol_mouse_I.D._354'], + ['PC.355', 'Control', '20061218', 'Control_mouse_I.D._355'], + ['PC.356', 'Control', '20061126', 'Control_mouse_I.D._356'], + ['PC.481', 'Control', '20070314', 'Control_mouse_I.D._481'], + ['PC.593', 'Control', '20071210', 'Control_mouse_I.D._593'], + ['PC.607', 'Fast', '20071112', 'Fasting_mouse_I.D._607'], + ['PC.634', 'Fast', '20080116', 'Fasting_mouse_I.D._634'], + ['PC.635', 'Fast', '20080116', 'Fasting_mouse_I.D._635'], + ['PC.636', 'Fast', '20080116', 'Fasting_mouse_I.D._636']] + self.headers = ['SampleID', 'Treatment', 'DOB', 'Description'] + + def test_str(self): + emp = Emperor(self.ord_res, self.data, self.headers) + self.assertEqual(HTML_STRING, str(emp)) + + def test_ids(self): + emp = Emperor(self.ord_res, self.data, self.headers) + self.assertEqual(['PC.354', 'PC.355', 'PC.356', 'PC.481', 'PC.593', + 'PC.607', 'PC.634', 'PC.635', 'PC.636'], + emp.ids) + +PCOA_STRING = """Eigvals 9 +0.479412119045 0.29201495623 0.247449246064 0.201496072404 0.180076127632\ + 0.147806772727 0.135795927213 0.112259695609 0.0 + +Proportion explained 9 +0.266887048633 0.162563704022 0.137754129161 0.11217215823 0.10024774995\ + 0.0822835130237 0.0755971173665 0.0624945796136 0.0 + +Species 0 0 + +Site 9 9 +PC.636 -0.276542163845 -0.144964375408 0.0666467344429 -0.0677109454288\ + 0.176070269506 0.072969390136 -0.229889463523 -0.0465989416581\ + -0.0 +PC.635 -0.237661393984 0.0460527772512 -0.138135814766 0.159061025229\ + -0.247484698646 -0.115211468101 -0.112864033263 0.0647940729676\ + -0.0 +PC.356 0.228820399536 -0.130142097093 -0.287149447883 0.0864498846421\ + 0.0442951919304 0.20604260722 0.0310003571386 0.0719920436501 -0.0 +PC.481 0.0422628480532 -0.0139681511889 0.0635314615517 -0.346120552134\ + -0.127813807608 0.0139350721063 0.0300206887328 0.140147849223 -0.0 +PC.354 0.280399117569 -0.0060128286014 0.0234854344148 -0.0468109474823\ + -0.146624450094 0.00566979124596 -0.0354299634191\ + -0.255785794275 -0.0 +PC.593 0.232872767451 0.139788385269 0.322871079774 0.18334700682\ + 0.0204661596818 0.0540589147147 -0.0366250872041 0.0998235721267\ + -0.0 +PC.355 0.170517581885 -0.194113268955 -0.0308965283066 0.0198086158783\ + 0.155100062794 -0.279923941712 0.0576092515759 0.0242481862127 -0.0 +PC.607 -0.0913299284215 0.424147148265 -0.135627421345 -0.057519480907\ + 0.151363490722 -0.0253935675552 0.0517306152066 -0.038738217609\ + -0.0 +PC.634 -0.349339228244 -0.120787589539 0.115274502117 0.0694953933826\ + -0.0253722182853 0.067853201946 0.244447634756 -0.0598827706386\ + -0.0 + +Biplot 0 0 + +Site constraints 0 0 +""" + +HTML_STRING = """ + + + + Emperor + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+
+ +

WebGL is not enabled!

+

Emperor's visualization framework is WebGL based, it seems that your system doesn't have this resource available. Here is what you can do:

+

Chrome: Type "chrome://flags/" into the address bar, then search for "Disable WebGL". Disable this option if you haven't already. Note: If you follow these steps and still don't see an image, go to "chrome://flags/" and then search for "Override software rendering list" and enable this option.

+

Safari: Open Safari's menu and select Preferences. Click on the advanced tab, and then check "Show Developer" menu. Then open the "Developer" menu and select "Enable WebGL".

+

Firefox: Go to Options through Firefox > Options or Tools > Options. Go to Advanced, then General. Check "Use hardware acceleration when available" and restart Firefox.

+

Other browsers: The only browsers that support WebGL are Chrome, Safari, and Firefox. Please switch to these browsers when using Emperor.

+

Note: Once you went through these changes, reload the page and it should work!

+

Sources: Instructions for Chrome and Safari, and Firefox

+
+
+ +
+
+
+ + +
+
+
+
+ + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+
+ +
+ +
+
+ +
+
+ +
+
+
+
+
+ Use gradient colors +

+ +
+
+
+
+ + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ + +
+
+ +
+
+
+ + + + + + + + + + +
+ +
+
+
+
+
+
+ + +
+
+
+
+
+
+ Samples Label Visibility +
+
+ + +
+
+ + +

+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ + + + + +
Axes Labels Color
Axes Color
Background Color
+
+
+
+
+
+ Scale coords by percent explained +
+
+
+

+
+
Filename (only letters, numbers, ., - and _): +
+
Create legend + +

For a PNG, simply press 'ctrl+p'. +
+
+
+
+
+
+
+
+ + + +""" + +if __name__ == "__main__": + main()