Skip to content

Commits on Source 4

language: python
python:
- "2.7"
- "3.5"
- "3.6"
- 2.7
- 3.4
- 3.5
- 3.6
# also includes 3.7 (see below)
env:
- NUMPY_VERSION="" WITH_COVERAGE=1 # environment to test with the latest version of NumPy
- NUMPY_VERSION="" # environment to test with the latest version of NumPy
- NUMPY_VERSION="<1.13"
matrix:
# support for python 3.7 is rather awkward via Travis CI
# https://github.com/travis-ci/travis-ci/issues/9815
include:
- python: 3.7
dist: xenial
sudo: true
env: NUMPY_VERSION=""
- python: 3.7
dist: xenial
sudo: true
env: NUMPY_VERSION="<1.13"
exclude:
# for older python versions only test the latest versions of NumPy
- python: 2.7
env: NUMPY_VERSION="<1.13"
- python: 3.4
env: NUMPY_VERSION="<1.13"
- python: 3.5
env: NUMPY_VERSION="<1.13"
before_install:
- phantomjs --version
- wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh -O miniconda.sh
- chmod +x miniconda.sh
- wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh -O miniconda.sh && chmod +x miniconda.sh
- ./miniconda.sh -b
- export PATH=/home/travis/miniconda2/bin:$PATH
# Update conda itself
- conda update --yes conda
install:
- conda create --yes -n env_name python=$TRAVIS_PYTHON_VERSION pip numpy${NUMPY_VERSION} 'scipy>=0.17.0' matplotlib pandas nose flake8 pep8 jupyter
- source activate env_name
- pip install https://github.com/google/closure-linter/archive/master.zip
- pip install 'sphinx<1.6' sphinx-bootstrap-theme coverage coveralls
# install lockfile before to prevent a failure in travis
- pip install 'lockfile>=0.10.2'
- conda create --yes -n travis python=$TRAVIS_PYTHON_VERSION pip numpy${NUMPY_VERSION} 'scipy>=0.17.0' matplotlib pandas flake8 pep8 jupyter coverage cython
- source activate travis
- conda install -c conda-forge phantomjs --yes
# scikit-learn has to be pinned down due to a bug: https://github.com/scikit-learn/scikit-learn/issues/12671
- pip install https://github.com/google/closure-linter/archive/master.zip 'sphinx<1.6' sphinx-bootstrap-theme coveralls 'scikit-learn==0.19.2' --no-binary scikit-learn
- pip install -e '.[all]' --verbose
- npm install -g jsdoc
script:
- flake8 emperor/*.py tests/*.py scripts/*.py setup.py
# we can only run gjslint in a python 2.7.x environment
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then gjslint --custom_jsdoc_tags 'module,function,constructs,alias,default' 'emperor/support_files/js/*.js' 'tests/javascript_tests/*.js'; fi
# execute the full test suite
- python tests/all_tests.py
# we just check coverage in the latest version of NumPy
- if [ ${WITH_COVERAGE} ]; then nosetests emperor --with-coverage --cover-package=emperor --cover-inclusive tests; fi
- coverage run tests/all_tests.py; coverage report
- make -C doc html
after_success:
- coveralls
......@@ -11,4 +11,10 @@ Before contributing code to Emperor, please familiarize yourself with the [contr
## Usage
You can start using Emperor through [QIIME2](https://qiime2.org)'s [interfaces](https://docs.qiime2.org/2.0.6/interfaces/) (the command line interface or the graphical user interface), or alternatively using the Python interface (compatible with the Jupyter notebook, see [this example](http://nbviewer.jupyter.org/github/biocore/emperor/blob/new-api/examples/keyboard.ipynb)). For more detalis, refer to our [online documentation](http://emperor.microbio.me/uno/).
You can start using Emperor through [QIIME2](https://qiime2.org)'s [interfaces](https://docs.qiime2.org/2018.8/interfaces/) (the command line interface or the graphical user interface), or alternatively using the Python interface (compatible with the Jupyter notebook, see [this example](http://nbviewer.jupyter.org/github/biocore/emperor/blob/new-api/examples/keyboard.ipynb)). For more detalis, refer to our [online documentation](http://emperor.microbio.me/uno/).
## Publications
- [EMPeror: a tool for visualizing high-throughput microbial community data](https://www.ncbi.nlm.nih.gov/pubmed/24280061). GigaScience, 2013.
- [Bringing the Dynamic Microbiome to Life with Animations](https://www.ncbi.nlm.nih.gov/pubmed/28081445). Cell Host & Microbe, 2016.
emperor (1.0.0-beta.17+dfsg-1) UNRELEASED; urgency=medium
emperor (1.0.0-beta.19+dfsg-1) UNRELEASED; urgency=medium
* packaging errors fixed
* Initial release (Closes #<bug>)
-- Kerim Ölçer <kerimlcr@gmail.com> Wed, 19 Oct 2016 22:19:34 +0300
-- Liubov Chuprikova <chuprikovalv@gmail.com> Wed, 17 July 2019 18:19:24 +0300
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: EMPeror
Upstream-Contact: Yoshiki Vázquez Baeza <yoshiki89@gmail.com>
Source: https://pypi.python.org/pypi/emperor
Source: https://github.com/biocore/emperor
Files-Excluded:
doc/files
doc/bootstrap
emperor/support_files/js/jquery-1.6.2.min.js
doc/bootstrap/js
emperor/support_files/vendor/js
Files: *
Copyright: © 2013, The Emperor Project
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -4,10 +4,14 @@
<meta charset="utf-8">
<title>Emperor</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Fav and touch icons -->
<link rel="icon" href="img/favicon.ico" type="image/x-icon" />
<link rel="shortcut icon" href="img/favicon.ico" type="image/x-icon" />
<script type="text/javascript" src="bootstrap/js/jquery-2.2.3.min.js"></script>
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
<meta name="description" content="">
<meta name="author" content="">
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
<style type="text/css">
body {
......@@ -42,9 +46,6 @@
<!--[if lt IE 9]>
<script src="bootstrap/js/html5shiv.js"></script>
<![endif]-->
<!-- Fav and touch icons -->
<link rel="shortcut icon" href="img/favicon.ico">
</head>
<body>
......@@ -61,12 +62,13 @@
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Documentation<span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="build/html/examples.html">Examples</a></li>
<li><a href="build/html/index.html">Python Documentation</a></li>
<li><a href="build/jsdoc/index.html">Javascript Documentation</a></li>
<li><a href="build/html/description.html">Description</a></li>
</ul>
</li>
<li><a href="build/html/examples.html">Examples</a></li>
<li><a href="build/html/description.html">Description</a></li>
<li><a href="https://nbviewer.jupyter.org/github/biocore/emperor/tree/new-api/examples/">Notebooks</a></li>
<li><a href="https://github.com/biocore/emperor/blob/new-api/INSTALL.md#emperor-installation-notes">Installation</a></li>
<li><a href="https://github.com/biocore/emperor/issues">Support</a></li>
</ul>
......
......@@ -65,10 +65,10 @@ copyright = u'2014, Emperor Development Team'
# built documents.
#
# The short X.Y version.
version = '1.0.0beta17'
version = '1.0.0beta19'
# The full version, including alpha/beta/rc tags.
release = '1.0.0beta17'
release = '1.0.0beta19'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
......
......@@ -73,6 +73,10 @@ class Emperor(object):
DataFrame object with the metadata associated to the samples in the
``ordination`` object, should have an index set and it should match the
identifiers in the ``ordination`` object.
feature_mapping_file: pd.DataFrame, optional
DataFrame object with the metadata associated to the features in the
``ordination`` object, should have an index set and it should match the
identifiers in the ``ordination.features`` object.
dimensions: int, optional
Number of dimensions to keep from the ordination data, defaults to 5.
Be aware that this value will determine the number of dimensions for
......@@ -213,6 +217,7 @@ class Emperor(object):
ValueError
If the remote argument is not of ``bool`` or ``str`` type.
If none of the samples in the ordination matrix are in the metadata.
If the data is one-dimensional.
KeyError
If there's samples in the ordination matrix but not in the metadata.
......@@ -227,6 +232,10 @@ class Emperor(object):
dimensions=5, remote=True, jackknifed=None, procrustes=None,
ignore_missing_samples=False):
if ordination.samples.shape[1] < 2:
raise ValueError('Ordinations with less than two dimensions are'
' not supported.')
self.ordination = ordination
self.jackknifed = jackknifed if jackknifed is not None else []
self.procrustes = procrustes if procrustes is not None else []
......@@ -703,10 +712,9 @@ class Emperor(object):
mf = pd.concat(mfs)
headers = [str(c) for c in [index_name] + mf.columns.tolist()]
metadata = mf.apply(lambda x: [str(x.name)] +
x.astype('str').tolist(),
axis=1).values.tolist()
# create a list of lists representation for the entire dataframe
headers = [index_name] + mf.columns.astype(str).tolist()
metadata = mf.reset_index().astype(str).values.tolist()
return headers, metadata
def _base_data_checks(self, category, data, d_type):
......
......@@ -489,7 +489,7 @@ define([
// get the current visible dimensions
var x = view.visibleDimensions[0], y = view.visibleDimensions[1],
z = view.visibleDimensions[2];
var is2D = (z === null);
var is2D = (z === null || z === undefined);
gradient = this.$gradientSelect.val();
trajectory = this.$trajectorySelect.val();
......
......@@ -584,7 +584,7 @@ define([
{id: 'Spectral', name: 'Spectral', type: DIVERGING},
{id: 'RdBu', name: 'Red-Blue', type: DIVERGING},
{id: 'RdYlGn', name: 'Red-Yellow-Green', type: DIVERGING},
{id: 'RdYlB', name: 'Red-Yellow-Blue', type: DIVERGING},
{id: 'RdYlBu', name: 'Red-Yellow-Blue', type: DIVERGING},
{id: 'RdGy', name: 'Red-Grey', type: DIVERGING},
{id: 'PiYG', name: 'Pink-Yellow-Green', type: DIVERGING},
{id: 'BrBG', name: 'Brown-Blue-Green', type: DIVERGING},
......
......@@ -332,7 +332,8 @@ define([
}
var spv = new ScenePlotView3D(this.renderer, this.decViews,
this.$plotSpace, 0, 0, 0, 0);
this.$plotSpace, 0, 0,
this.width, this.height);
this.sceneViews.push(spv);
// this will setup the appropriate sizes and widths
......@@ -591,6 +592,7 @@ define([
'labels' : {
name: 'Toggle label visibility',
visible: scope.decViews.biplot !== undefined,
icon: 'font',
callback: function() {
scope._hideBiplotLabels = Boolean(scope._hideBiplotLabels ^ true);
scope.decViews.biplot.toggleLabelVisibility();
......@@ -660,8 +662,29 @@ define([
disabled: isLargeDataset
}
}
},
fold2: {
name: 'Experimental',
disabled: function(key, opt) {
// Only enable if this is a "vanilla" plot
if (scope.decViews.scatter.lines.left === null &&
scope.decViews.scatter.lines.right === null &&
scope.decViews.biplot === undefined) {
return false;
}
return true;
},
icon: 'warning',
items: {
openInVegaEditor: {
name: 'Open in Vega Editor',
callback: function(key, opts) {
scope.exportToVega();
},
}
}
},
},
});
// The context menu is only shown if there's a single right click. We
......@@ -930,5 +953,55 @@ define([
return obj;
};
/**
*
* Helper that posts messages between browser tabs
*
* @private
*
*/
_postMessage = function(url, payload) {
// Shamelessly pulled from https://github.com/vega/vega-embed/
var editor = window.open(url);
var wait = 10000;
var step = 250;
var count = ~~(wait / step);
function listen(e) {
if (e.source === editor) {
count = 0;
window.removeEventListener('message', listen, false);
}
}
window.addEventListener('message', listen, false);
function send() {
if (count <= 0) {
return;
}
editor.postMessage(payload, '*');
setTimeout(send, step);
count -= 1;
}
setTimeout(send, step);
};
/**
*
* Open in Vega editor
*
*/
EmperorController.prototype.exportToVega = function() {
var url = 'https://vega.github.io/editor/';
var spec = this.decViews.scatter._buildVegaSpec();
var payload = {
mode: 'vega',
renderer: 'canvas',
spec: JSON.stringify(spec),
};
_postMessage(url, payload);
};
return EmperorController;
});
......@@ -514,6 +514,34 @@ function($, _, util) {
this.axesNames = names.nonNumeric.concat(replacement);
}
}
this._buildAxesLabels();
};
/**
*
* Helper method to build labels for all axes
*
*/
DecompositionModel.prototype._buildAxesLabels = function() {
var axesLabels = [], index, text;
for (index = 0; index < this.axesNames.length; index++) {
// when the labels get too long, it's a bit hard to look at
if (this.axesNames[index].length > 25) {
text = this.axesNames[index].slice(0, 20) + '...';
}
else {
text = this.axesNames[index];
}
// account for custom axes (their percentage explained will be -1 to
// indicate that this attribute is not meaningful).
if (this.percExpl[index] >= 0) {
text += ' (' + this.percExpl[index].toPrecision(4) + ' %)';
}
axesLabels.push(text);
}
this.axesLabels = axesLabels;
};
/**
......
......@@ -69,6 +69,17 @@ define([
* @default '#000000' (black)
*/
this.backgroundColor = '#000000';
/**
* True when changes have occured that require re-rendering of the canvas
* @type {Boolean}
*/
this.needsUpdate = true;
/**
* Array of integers indicating the index of the visible dimension at each
* axis ([x, y, z]).
* @type {Integer[]}
*/
this.visibleDimensions = _.clone(this.decViews.scatter.visibleDimensions);
/**
* Ranges for all the decompositions in this view (there's a min and a max
......@@ -96,8 +107,6 @@ define([
this.camera.position.set(0, 0, max * 5);
this.camera.zoom = 0.7;
this.updateCameraAspectRatio();
//need to initialize the scene
this.scene = new THREE.Scene();
this.scene.add(this.camera);
......@@ -131,23 +140,13 @@ define([
this.control.panSpeed = 0.8;
this.control.enableZoom = true;
this.control.enablePan = true;
this.control.enableDamping = true;
this.control.dampingFactor = 0.3;
this.control.addEventListener('change', function() {
scope.needsUpdate = true;
});
this.updateCameraTarget();
this.control.update();
/**
* True when changes have occured that require re-rendering of the canvas
* @type {Boolean}
*/
this.needsUpdate = true;
/**
* Array of integers indicating the index of the visible dimension at each
* axis ([x, y, z]).
* @type {Integer[]}
*/
this.visibleDimensions = [0, 1, 2];
/**
* Object with "min" and "max" attributes each of which is an array with
* the ranges that covers all of the decomposition views.
......@@ -426,7 +425,7 @@ define([
// shortcut to the index of the visible dimension and the range object
var x = this.visibleDimensions[0], y = this.visibleDimensions[1],
z = this.visibleDimensions[2], range = this.dimensionRanges,
is2D = z === null;
is2D = (z === null || z === undefined);
// Adds a padding to all dimensions such that samples don't overlap
// with the axes lines. Determined based on the default sphere radius
......@@ -457,11 +456,9 @@ define([
action(start, ends[0], x);
action(start, ends[1], y);
// when transitioning to 2D reset the camera and disable rotation to make
// the plot look straight at the camera, as opposed to an awkward angle
// when transitioning to 2D disable rotation to avoid awkward angles
if (is2D) {
this.control.enableRotate = false;
this.recenterCamera();
}
else {
action(start, ends[2], z);
......@@ -528,19 +525,7 @@ define([
decomp = this.decViews[firstKey].decomp;
this._dimensionsIterator(function(start, end, index) {
// when the labels get too long, it's a bit hard to look at
if (decomp.axesNames[index].length > 25) {
text = decomp.axesNames[index].slice(0, 20) + '...';
}
else {
text = decomp.axesNames[index];
}
// account for custom axes (their percentage explained will be -1 to
// indicate that this attribute is not meaningful).
if (decomp.percExpl[index] >= 0) {
text += ' (' + decomp.percExpl[index].toPrecision(4) + ' %)';
}
text = decomp.axesLabels[index];
axisLabel = makeLabel(end, text, color);
axisLabel.scale.set(axisLabel.scale.x * scaling,
......@@ -605,6 +590,7 @@ define([
this.height = height;
this.updateCameraAspectRatio();
this.control.update();
this.needsUpdate = true;
};
......@@ -616,10 +602,12 @@ define([
*
*/
ScenePlotView3D.prototype.updateCameraAspectRatio = function() {
var x = this.visibleDimensions[0], y = this.visibleDimensions[1];
// orthographic cameras operate in space units not in pixel units i.e.
// the width and height of the view is based on the objects not the window
var owidth = this.dimensionRanges.max[0] - this.dimensionRanges.min[0];
var oheight = this.dimensionRanges.max[1] - this.dimensionRanges.min[1];
var owidth = this.dimensionRanges.max[x] - this.dimensionRanges.min[x];
var oheight = this.dimensionRanges.max[y] - this.dimensionRanges.min[y];
var aspect = this.width / this.height;
......@@ -635,6 +623,36 @@ define([
this.camera.updateProjectionMatrix();
};
/**
* Updates the target and dimensions of the camera and control
*
* The target of the scene depends on the coordinate space of the data, by
* default it is set to zero, but we need to make sure that the target is
* reasonable for the data.
*/
ScenePlotView3D.prototype.updateCameraTarget = function() {
var x = this.visibleDimensions[0], y = this.visibleDimensions[1];
var owidth = this.dimensionRanges.max[x] - this.dimensionRanges.min[x];
var oheight = this.dimensionRanges.max[y] - this.dimensionRanges.min[y];
var xcenter = this.dimensionRanges.max[x] - (owidth / 2);
var ycenter = this.dimensionRanges.max[y] - (oheight / 2);
var max = _.max(this.decViews.scatter.decomp.dimensionRanges.max);
this.control.target.set(xcenter, ycenter, 0);
this.camera.position.set(xcenter, ycenter, max * 5);
this.camera.updateProjectionMatrix();
this.light.position.set(xcenter, ycenter, max * 5);
this.updateCameraAspectRatio();
this.control.saveState();
this.needsUpdate = true;
};
/**
*
* Convenience method to check if this or any of the decViews under this need
......@@ -675,6 +693,9 @@ define([
this.drawAxesWithColor(this.axesColor);
this.drawAxesLabelsWithColor(this.axesColor);
this.updateCameraTarget();
this.control.update();
updateDimensions = true;
}
......@@ -849,16 +870,6 @@ define([
*
*/
ScenePlotView3D.prototype.recenterCamera = function() {
this.camera.rotation.set(0, 0, 0);
this.camera.updateProjectionMatrix();
// reset the position of the this view
var max = _.max(this.dimensionRanges.max);
// 5 is inspired by the old emperor.js and by the init method of this class
this.camera.position.set(0, 0, max * 5);
// after all changes are made, reset the control
this.control.reset();
this.control.update();
......