From dcadcdda5d4b4094e830f91ec8f0b988f7626721 Mon Sep 17 00:00:00 2001 From: Juan Eiros Date: Wed, 13 Sep 2017 16:36:51 +0100 Subject: [PATCH 01/14] Add 2D trace plot Based on issue #105 --- msmexplorer/plots/misc.py | 57 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/msmexplorer/plots/misc.py b/msmexplorer/plots/misc.py index 4c81625..6b3f816 100644 --- a/msmexplorer/plots/misc.py +++ b/msmexplorer/plots/misc.py @@ -11,7 +11,7 @@ from ..utils import msme_colors from .. import palettes -__all__ = ['plot_chord', 'plot_stackdist', 'plot_trace'] +__all__ = ['plot_chord', 'plot_stackdist', 'plot_trace', 'plot_trace2d'] def plot_chord(data, ax=None, cmap=None, labels=None, labelsize=12, norm=True, @@ -303,3 +303,58 @@ def plot_trace(data, label=None, window=1, ax=None, side_ax=None, side_ax.set_title('') return ax, side_ax + + +@msme_colors +def plot_trace2d(data, ts=1.0, cbar=True, ax=None, xlabel=None, + ylabel=None, labelsize=14, cbar_kwargs=None, scatter_kwargs=None): + """ + Plot a 2D trace of time-series data. + + Parameters + ---------- + data : array-like (nsamples, 2) + The samples. This should be a 2-D time-series array. + ts: float, optional (default: 1.0) + Step in units of time between each data point in data + cbar: bool, optional (default: True) + Adds a colorbar that maps the evolution of points in data + ax : matplotlib axis, optional + main matplotlib figure axis for trace. + xlabel : str, optional + x-axis label + ylabel : str, optional + y-axis label + labelsize : int, optional (default: 14) + Font side for axes labels. + cbar_kwargs: dict, optional + Arguments to pass to matplotlib cbar + scatter_kwargs: dict, optional + Arguments to pass to matplotlib scatter + + Returns + ------- + ax : matplotlib axis + main matplotlib figure axis for 2D trace. + """ + + if ax is None: + ax = pp.gca() + if scatter_kwargs is None: + scatter_kwargs = {} + + c = ax.scatter(data[:, 0], data[:, 1], + c=np.linspace(0, data.shape[0] * ts, data.shape[0]), + **scatter_kwargs) + + if cbar: + if not cbar_kwargs: + cbar_kwargs = {} + pp.colorbar(c, **cbar_kwargs) + + if xlabel: + ax.set_xlabel(xlabel, size=labelsize) + if ylabel: + ax.set_ylabel(ylabel, size=labelsize) + + return ax From d62d81fb9cbdc3ceba97fb71f6f21b4f5c54630a Mon Sep 17 00:00:00 2001 From: Juan Eiros Date: Wed, 13 Sep 2017 16:37:10 +0100 Subject: [PATCH 02/14] Add example of trace2d On top of 2d projection of free energy for clarity --- examples/plot_trace2d.py | 46 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 examples/plot_trace2d.py diff --git a/examples/plot_trace2d.py b/examples/plot_trace2d.py new file mode 100644 index 0000000..13a3db1 --- /dev/null +++ b/examples/plot_trace2d.py @@ -0,0 +1,46 @@ +""" +Two dimensional trace plot +=============== +""" +from msmbuilder.example_datasets import FsPeptide +from msmbuilder.featurizer import DihedralFeaturizer +from msmbuilder.decomposition import tICA +from msmbuilder.cluster import MiniBatchKMeans +from msmbuilder.msm import MarkovStateModel + +import numpy as np + +import msmexplorer as msme + +rs = np.random.RandomState(42) + +# Load Fs Peptide Data +trajs = FsPeptide().get().trajectories + +# Extract Backbone Dihedrals +featurizer = DihedralFeaturizer(types=['phi', 'psi']) +diheds = featurizer.fit_transform(trajs) + +# Perform Dimensionality Reduction +tica_model = tICA(lag_time=2, n_components=2) +tica_trajs = tica_model.fit_transform(diheds) + +# Plot free 2D free energy (optional) +txx = np.concatenate(tica_trajs, axis=0) +ax = msme.plot_free_energy( + txx, obs=(0, 1), n_samples=100000, + random_state=rs, + shade=True, + clabel=True, + clabel_kwargs={'fmt': '%.1f'}, + cbar=True, + cbar_kwargs={'format': '%.1f', 'label': 'Free energy (kcal/mol)'} +) +# Now plot the first single trajectory on top of it to inspect it's movement +msme.plot_trace2d( + data=tica_trajs[0], ts=.2, ax=ax, + scatter_kwargs={'s': 2}, + cbar_kwargs={'format': '%.1f', 'label': 'Time (ns)', + 'orientation': 'horizontal'}, + xlabel='tIC 1', ylabel='tIC 2' +) From 64d1933685bf5c828217008d2b5e566fa6ce3cb6 Mon Sep 17 00:00:00 2001 From: Juan Eiros Date: Wed, 13 Sep 2017 16:37:17 +0100 Subject: [PATCH 03/14] Update changelog --- docs/changelog.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 88a3e19..5120edb 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -18,6 +18,9 @@ New Features - ``plot_free_energy`` now accepts a ``return_data`` flag that will return the data used for the free energy plot(#78). +- Added a new function ``plot_trace2d`` that plots the time evolution of a 2D numpy array + using a colorbar to map the time. + Improvements ~~~~~~~~~~~~ From 10afa9df8f1438c7eec49785a3446ee7ee6cbb61 Mon Sep 17 00:00:00 2001 From: Juan Eiros Date: Wed, 13 Sep 2017 16:41:51 +0100 Subject: [PATCH 04/14] Add PR number --- docs/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 5120edb..4d9633d 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -19,7 +19,7 @@ New Features the data used for the free energy plot(#78). - Added a new function ``plot_trace2d`` that plots the time evolution of a 2D numpy array - using a colorbar to map the time. + using a colorbar to map the time (#108). Improvements ~~~~~~~~~~~~ From 6e47f410768907b9d23f2f981697f97daa76e897 Mon Sep 17 00:00:00 2001 From: Juan Eiros Date: Wed, 13 Sep 2017 17:00:40 +0100 Subject: [PATCH 05/14] Add option to plot more than one array --- msmexplorer/plots/misc.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/msmexplorer/plots/misc.py b/msmexplorer/plots/misc.py index 6b3f816..29b1e2b 100644 --- a/msmexplorer/plots/misc.py +++ b/msmexplorer/plots/misc.py @@ -313,8 +313,13 @@ def plot_trace2d(data, ts=1.0, cbar=True, ax=None, xlabel=None, Parameters ---------- - data : array-like (nsamples, 2) - The samples. This should be a 2-D time-series array. + data : array-like (nsamples, 2) or list thereof + The samples. This should be a single 2-D time-series array or a list of 2-D + time-series arrays. + If it is a single 2D np.array, the elements will be scatter plotted and + color mapped to their values. + If it is a list of 2D np.arrays, each will be plotted with a single color on + the same axis. ts: float, optional (default: 1.0) Step in units of time between each data point in data cbar: bool, optional (default: True) @@ -331,7 +336,8 @@ def plot_trace2d(data, ts=1.0, cbar=True, ax=None, xlabel=None, Arguments to pass to matplotlib cbar scatter_kwargs: dict, optional Arguments to pass to matplotlib scatter - + plot_kwargs: dict, optional + Arguments to pass to matplotlib plot Returns ------- ax : matplotlib axis @@ -343,9 +349,14 @@ def plot_trace2d(data, ts=1.0, cbar=True, ax=None, xlabel=None, if scatter_kwargs is None: scatter_kwargs = {} - c = ax.scatter(data[:, 0], data[:, 1], - c=np.linspace(0, data.shape[0] * ts, data.shape[0]), - **scatter_kwargs) + if isinstance(data, list): + cbar = False + for item in data: + ax.plot(item[:, 0], item[:, 1], **plot_kwargs) + else: + c = ax.scatter(data[:, 0], data[:, 1], + c=np.linspace(0, data.shape[0] * ts, data.shape[0]), + **scatter_kwargs) if cbar: if not cbar_kwargs: From 21ecb1405b1a255f0eb90d5bc6ebf3d56d01ec47 Mon Sep 17 00:00:00 2001 From: Juan Eiros Date: Wed, 13 Sep 2017 17:01:00 +0100 Subject: [PATCH 06/14] Add multi array example On white background for clarity --- examples/plot_trace2d.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/plot_trace2d.py b/examples/plot_trace2d.py index 13a3db1..f28ddac 100644 --- a/examples/plot_trace2d.py +++ b/examples/plot_trace2d.py @@ -7,7 +7,7 @@ from msmbuilder.decomposition import tICA from msmbuilder.cluster import MiniBatchKMeans from msmbuilder.msm import MarkovStateModel - +from matplotlib import pyplot as pp import numpy as np import msmexplorer as msme @@ -44,3 +44,7 @@ 'orientation': 'horizontal'}, xlabel='tIC 1', ylabel='tIC 2' ) + +# Finally, let's plot every trajectory to see the region that each one is covering +f, ax = pp.subplots() +msme.plot_trace2d(tica_trajs, ax) From 62a84d52f7bb80373a10d5ea6d06c8fbb4df78fd Mon Sep 17 00:00:00 2001 From: Juan Eiros Date: Wed, 13 Sep 2017 17:02:36 +0100 Subject: [PATCH 07/14] Add plot_kwargs argument --- msmexplorer/plots/misc.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/msmexplorer/plots/misc.py b/msmexplorer/plots/misc.py index 29b1e2b..53f5301 100644 --- a/msmexplorer/plots/misc.py +++ b/msmexplorer/plots/misc.py @@ -307,7 +307,8 @@ def plot_trace(data, label=None, window=1, ax=None, side_ax=None, @msme_colors def plot_trace2d(data, ts=1.0, cbar=True, ax=None, xlabel=None, - ylabel=None, labelsize=14, cbar_kwargs=None, scatter_kwargs=None): + ylabel=None, labelsize=14, + cbar_kwargs=None, scatter_kwargs=None, plot_kwargs=None): """ Plot a 2D trace of time-series data. @@ -348,6 +349,8 @@ def plot_trace2d(data, ts=1.0, cbar=True, ax=None, xlabel=None, ax = pp.gca() if scatter_kwargs is None: scatter_kwargs = {} + if plot_kwargs is None: + plot_kwargs = {} if isinstance(data, list): cbar = False From b391f52e26f4b21135976cbb4983ed26203c7006 Mon Sep 17 00:00:00 2001 From: Juan Eiros Date: Wed, 13 Sep 2017 17:07:34 +0100 Subject: [PATCH 08/14] Named args for ax Gave error as thought ax was ts arg --- examples/plot_trace2d.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/plot_trace2d.py b/examples/plot_trace2d.py index f28ddac..4786498 100644 --- a/examples/plot_trace2d.py +++ b/examples/plot_trace2d.py @@ -44,7 +44,6 @@ 'orientation': 'horizontal'}, xlabel='tIC 1', ylabel='tIC 2' ) - # Finally, let's plot every trajectory to see the region that each one is covering f, ax = pp.subplots() -msme.plot_trace2d(tica_trajs, ax) +msme.plot_trace2d(tica_trajs, ax=ax, xlabel='tIC 1', ylabel='tIC 2')pp.show() From 4a22dfa1e20620cbab883d89af8259206e3f6511 Mon Sep 17 00:00:00 2001 From: Juan Eiros Date: Wed, 13 Sep 2017 17:11:08 +0100 Subject: [PATCH 09/14] Add test for plot_trace2d --- msmexplorer/tests/test_misc_plot.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/msmexplorer/tests/test_misc_plot.py b/msmexplorer/tests/test_misc_plot.py index b7a3658..1c111df 100644 --- a/msmexplorer/tests/test_misc_plot.py +++ b/msmexplorer/tests/test_misc_plot.py @@ -2,12 +2,13 @@ from matplotlib.axes import SubplotBase from seaborn.apionly import FacetGrid -from ..plots import plot_chord, plot_stackdist, plot_trace +from ..plots import plot_chord, plot_stackdist, plot_trace, plot_trace2d from . import PlotTestCase rs = np.random.RandomState(42) data = rs.rand(12, 12) ts = rs.rand(100000, 1) +ts2 = rs.rand(100000, 2) class TestChordPlot(PlotTestCase): @@ -38,3 +39,10 @@ def test_plot_trace(self): assert isinstance(ax, SubplotBase) assert isinstance(side_ax, SubplotBase) + + def test_plot_trace2d(self): + ax1 = plot_trace(ts2) + ax2 = test_plot_trace2d([ts2, ts2]) + + assert isinstance(ax1, SubplotBase) + assert isinstance(ax2, SubplotBase) From 8d3f588d27ab43c2ba5559138b200d5b0024984a Mon Sep 17 00:00:00 2001 From: Juan Eiros Date: Wed, 13 Sep 2017 17:18:11 +0100 Subject: [PATCH 10/14] Typos in test --- msmexplorer/tests/test_misc_plot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/msmexplorer/tests/test_misc_plot.py b/msmexplorer/tests/test_misc_plot.py index 1c111df..a781eaa 100644 --- a/msmexplorer/tests/test_misc_plot.py +++ b/msmexplorer/tests/test_misc_plot.py @@ -41,8 +41,8 @@ def test_plot_trace(self): assert isinstance(side_ax, SubplotBase) def test_plot_trace2d(self): - ax1 = plot_trace(ts2) - ax2 = test_plot_trace2d([ts2, ts2]) + ax1 = plot_trace2d(ts2) + ax2 = plot_trace2d([ts2, ts2]) assert isinstance(ax1, SubplotBase) assert isinstance(ax2, SubplotBase) From 918a3472ee4ebfc2191d70d6c073e4ff639551a6 Mon Sep 17 00:00:00 2001 From: Juan Eiros Date: Wed, 13 Sep 2017 18:25:54 +0100 Subject: [PATCH 11/14] add check for 2d array --- msmexplorer/plots/misc.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/msmexplorer/plots/misc.py b/msmexplorer/plots/misc.py index 53f5301..f407322 100644 --- a/msmexplorer/plots/misc.py +++ b/msmexplorer/plots/misc.py @@ -357,6 +357,8 @@ def plot_trace2d(data, ts=1.0, cbar=True, ax=None, xlabel=None, for item in data: ax.plot(item[:, 0], item[:, 1], **plot_kwargs) else: + if len(data.shape) != 2: + raise ValueError('Data must be a 2d array.') c = ax.scatter(data[:, 0], data[:, 1], c=np.linspace(0, data.shape[0] * ts, data.shape[0]), **scatter_kwargs) From 501faf3fba16da8e195081a4a64978de89a1e07e Mon Sep 17 00:00:00 2001 From: Juan Eiros Date: Thu, 14 Sep 2017 10:24:19 +0100 Subject: [PATCH 12/14] cbar_kwargs is not a bool --- msmexplorer/plots/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msmexplorer/plots/misc.py b/msmexplorer/plots/misc.py index f407322..a969d2c 100644 --- a/msmexplorer/plots/misc.py +++ b/msmexplorer/plots/misc.py @@ -364,7 +364,7 @@ def plot_trace2d(data, ts=1.0, cbar=True, ax=None, xlabel=None, **scatter_kwargs) if cbar: - if not cbar_kwargs: + if cbar_kwargs is None: cbar_kwargs = {} pp.colorbar(c, **cbar_kwargs) From 48e6fac18cf4fa528c9d27a7d849b44e6bfe759b Mon Sep 17 00:00:00 2001 From: Juan Eiros Date: Fri, 15 Sep 2017 15:15:58 +0100 Subject: [PATCH 13/14] Add obs argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit À la `plot_free_energy` --- msmexplorer/plots/misc.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/msmexplorer/plots/misc.py b/msmexplorer/plots/misc.py index a969d2c..56a2dca 100644 --- a/msmexplorer/plots/misc.py +++ b/msmexplorer/plots/misc.py @@ -306,7 +306,7 @@ def plot_trace(data, label=None, window=1, ax=None, side_ax=None, @msme_colors -def plot_trace2d(data, ts=1.0, cbar=True, ax=None, xlabel=None, +def plot_trace2d(data, obs=(0, 1), ts=1.0, cbar=True, ax=None, xlabel=None, ylabel=None, labelsize=14, cbar_kwargs=None, scatter_kwargs=None, plot_kwargs=None): """ @@ -321,6 +321,8 @@ def plot_trace2d(data, ts=1.0, cbar=True, ax=None, xlabel=None, color mapped to their values. If it is a list of 2D np.arrays, each will be plotted with a single color on the same axis. + obs: tuple, optional (default: (0,1)) + Observables to plot. ts: float, optional (default: 1.0) Step in units of time between each data point in data cbar: bool, optional (default: True) @@ -352,21 +354,25 @@ def plot_trace2d(data, ts=1.0, cbar=True, ax=None, xlabel=None, if plot_kwargs is None: plot_kwargs = {} + if not isinstance(obs, tuple): + raise ValueError('obs must be a tuple') + if isinstance(data, list): - cbar = False + # Plot each item in the list with a single color and join with lines for item in data: - ax.plot(item[:, 0], item[:, 1], **plot_kwargs) + prune = item[:, obs] + ax.plot(prune[:, 0], prune[:, 1], **plot_kwargs) else: - if len(data.shape) != 2: - raise ValueError('Data must be a 2d array.') - c = ax.scatter(data[:, 0], data[:, 1], + # A single array of data is passed, so we scatter plot + prune = data[:, obs] + c = ax.scatter(prune[:, 0], prune[:, 1], c=np.linspace(0, data.shape[0] * ts, data.shape[0]), **scatter_kwargs) - - if cbar: - if cbar_kwargs is None: - cbar_kwargs = {} - pp.colorbar(c, **cbar_kwargs) + if cbar: + # Map the time evolution between the data points to a colorbar + if cbar_kwargs is None: + cbar_kwargs = {} + pp.colorbar(c, **cbar_kwargs) if xlabel: ax.set_xlabel(xlabel, size=labelsize) From 470005c0a2c3d5cea937528957a2e9271dea3898 Mon Sep 17 00:00:00 2001 From: Juan Eiros Date: Fri, 15 Sep 2017 15:16:10 +0100 Subject: [PATCH 14/14] Improve example --- examples/plot_trace2d.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/plot_trace2d.py b/examples/plot_trace2d.py index 4786498..31b3234 100644 --- a/examples/plot_trace2d.py +++ b/examples/plot_trace2d.py @@ -36,14 +36,15 @@ cbar=True, cbar_kwargs={'format': '%.1f', 'label': 'Free energy (kcal/mol)'} ) -# Now plot the first single trajectory on top of it to inspect it's movement +# Now plot the first trajectory on top of it to inspect it's movement msme.plot_trace2d( - data=tica_trajs[0], ts=.2, ax=ax, + data=tica_trajs[0], ts=0.2, ax=ax, scatter_kwargs={'s': 2}, - cbar_kwargs={'format': '%.1f', 'label': 'Time (ns)', + cbar_kwargs={'format': '%d', 'label': 'Time (ns)', 'orientation': 'horizontal'}, xlabel='tIC 1', ylabel='tIC 2' ) -# Finally, let's plot every trajectory to see the region that each one is covering +# Finally, let's plot every trajectory to see the individual sampled regions f, ax = pp.subplots() -msme.plot_trace2d(tica_trajs, ax=ax, xlabel='tIC 1', ylabel='tIC 2')pp.show() +msme.plot_trace2d(tica_trajs, ax=ax, xlabel='tIC 1', ylabel='tIC 2') +pp.show()