diff --git a/README.md b/README.md index 64f3551..aee3c35 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ source ~/.bashrc ~~~ ## Inputs -_`Note: Support for the BIOM format has been dropped (temporarily) in OPAL 1.0.4 due to incompatibility with Python 3.7.*.`_ +_**`Note: Support for the BIOM format has been dropped (temporarily) in OPAL 1.0.4 due to incompatibility with Python 3.7.*.`**_ OPAL uses at least two files: 1. A gold standard taxonomic profile diff --git a/requirements/default.txt b/requirements/default.txt index 435e055..d1e0f5e 100644 --- a/requirements/default.txt +++ b/requirements/default.txt @@ -1,5 +1,5 @@ numpy==1.16.4 -matplotlib==2.0.2 +matplotlib==3.1.1 dendropy==4.4.0 pandas==0.24.2 h5py==2.9.0 diff --git a/src/html_opal.py b/src/html_opal.py index 0dcd03e..c43efa1 100644 --- a/src/html_opal.py +++ b/src/html_opal.py @@ -47,7 +47,7 @@ def create_heatmap_bar(output_dir): # hide heatmap pltx.gca().set_visible(False) - fig.savefig(os.path.join(output_dir, 'heatmap_bar.png'), dpi=100, format='png', bbox_inches='tight', pad_inches=-.035, transparent=True) + fig.savefig(os.path.join(output_dir, 'heatmap_bar.png'), dpi=100, format='png', bbox_inches='tight', pad_inches=-.001, transparent=True) pltx.close(fig) diff --git a/src/plots.py b/src/plots.py index 46629fc..0141a57 100644 --- a/src/plots.py +++ b/src/plots.py @@ -358,7 +358,7 @@ def spider_plot(metrics, labels, rank_to_metric_to_toolvalues, output_dir, file_ fig, axes = plt.subplots(figsize=(9, 9), nrows=2, ncols=3, subplot_kw=dict(projection='radar')) fig.subplots_adjust(wspace=0.35, hspace=0.05, top=0.87, bottom=0.3) - for ax, rank in zip(axes.flatten(), c.PHYLUM_SPECIES): + for ax, rank in zip(axes.flat, c.PHYLUM_SPECIES): if grid_points: ax.set_rgrids(grid_points, fontsize='xx-small') else: @@ -385,8 +385,7 @@ def spider_plot(metrics, labels, rank_to_metric_to_toolvalues, output_dir, file_ it += 1 ax.set_varlabels(labels) - ax.set_ylim([0.0, 1.0]) - ax.set_xlim([0.0, 1.0]) + ax.set_rmax(1) # color red label of tools without a value for at least one metric xticklabels = ax.get_xticklabels() @@ -394,6 +393,11 @@ def spider_plot(metrics, labels, rank_to_metric_to_toolvalues, output_dir, file_ for toolindex in metric: xticklabels[toolindex].set_color([1, 0, 0]) + # move ticks labels closer to plot and set font size + for xticklabel in xticklabels: + xticklabel.set_position((.1,.1)) + xticklabel.set_fontsize('small') + ax = axes[0, 0] ax.legend(metrics, loc=(1.6 - 0.353 * len(metrics), 1.3), labelspacing=0.1, fontsize='small', ncol=len(metrics)) fig.savefig(os.path.join(output_dir, file_name + '.pdf'), dpi=100, format='pdf', bbox_inches='tight') diff --git a/src/utils/spider_plot_functions.py b/src/utils/spider_plot_functions.py index f5eaeb2..1b593a0 100644 --- a/src/utils/spider_plot_functions.py +++ b/src/utils/spider_plot_functions.py @@ -2,11 +2,12 @@ import matplotlib matplotlib.use('Agg') -import matplotlib.pyplot as plt +from matplotlib.patches import Circle, RegularPolygon from matplotlib.path import Path -from matplotlib.spines import Spine from matplotlib.projections.polar import PolarAxes from matplotlib.projections import register_projection +from matplotlib.spines import Spine +from matplotlib.transforms import Affine2D def radar_factory(num_vars, frame='circle'): @@ -24,37 +25,25 @@ def radar_factory(num_vars, frame='circle'): """ # calculate evenly-spaced axis angles theta = np.linspace(0, 2*np.pi, num_vars, endpoint=False) - # rotate theta such that the first axis is at the top - theta += np.pi/2 - - def draw_poly_patch(self): - verts = unit_poly_verts(theta) - return plt.Polygon(verts, closed=True, edgecolor='k') - - def draw_circle_patch(self): - # unit circle centered on (0.5, 0.5) - return plt.Circle((0.5, 0.5), 0.5) - - patch_dict = {'polygon': draw_poly_patch, 'circle': draw_circle_patch} - if frame not in patch_dict: - raise ValueError('unknown value for `frame`: %s' % frame) class RadarAxes(PolarAxes): name = 'radar' # use 1 line segment to connect specified points RESOLUTION = 1 - # define draw_frame method - draw_patch = patch_dict[frame] - def fill(self, *args, **kwargs): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # rotate plot such that the first axis is at the top + self.set_theta_zero_location('N') + + def fill(self, *args, closed=True, **kwargs): """Override fill so that line is closed by default""" - closed = kwargs.pop('closed', True) - return super(RadarAxes, self).fill(closed=closed, *args, **kwargs) + return super().fill(closed=closed, *args, **kwargs) def plot(self, *args, **kwargs): """Override plot so that line is closed by default""" - lines = super(RadarAxes, self).plot(*args, **kwargs) + lines = super().plot(*args, **kwargs) for line in lines: self._close_line(line) @@ -67,37 +56,35 @@ def _close_line(self, line): line.set_data(x, y) def set_varlabels(self, labels): - self.set_thetagrids(np.degrees(theta), labels, fontsize='small') + return self.set_thetagrids(np.degrees(theta), labels) def _gen_axes_patch(self): - return self.draw_patch() + # The Axes patch must be centered at (0.5, 0.5) and of radius 0.5 + # in axes coordinates. + if frame == 'circle': + return Circle((0.5, 0.5), 0.5) + elif frame == 'polygon': + return RegularPolygon((0.5, 0.5), num_vars, + radius=.5, edgecolor="k") + else: + raise ValueError("unknown value for 'frame': %s" % frame) def _gen_axes_spines(self): if frame == 'circle': - return PolarAxes._gen_axes_spines(self) - # The following is a hack to get the spines (i.e. the axes frame) - # to draw correctly for a polygon frame. - - # spine_type must be 'left', 'right', 'top', 'bottom', or `circle`. - spine_type = 'circle' - verts = unit_poly_verts(theta) - # close off polygon by repeating first vertex - verts.append(verts[0]) - path = Path(verts) - - spine = Spine(self, spine_type, path) - spine.set_transform(self.transAxes) - return {'polar': spine} + return super()._gen_axes_spines() + elif frame == 'polygon': + # spine_type must be 'left'/'right'/'top'/'bottom'/'circle'. + spine = Spine(axes=self, + spine_type='circle', + path=Path.unit_regular_polygon(num_vars)) + # unit_regular_polygon gives a polygon of radius 1 centered at + # (0, 0) but we want a polygon of radius 0.5 centered at (0.5, + # 0.5) in axes coordinates. + spine.set_transform(Affine2D().scale(.5).translate(.5, .5) + + self.transAxes) + return {'polar': spine} + else: + raise ValueError("unknown value for 'frame': %s" % frame) register_projection(RadarAxes) return theta - - -def unit_poly_verts(theta): - """Return vertices of polygon for subplot axes. - - This polygon is circumscribed by a unit circle centered at (0.5, 0.5) - """ - x0, y0, r = [0.5] * 3 - verts = [(r*np.cos(t) + x0, r*np.sin(t) + y0) for t in theta] - return verts diff --git a/version.py b/version.py index 8a81504..858de17 100644 --- a/version.py +++ b/version.py @@ -1 +1 @@ -__version__ = '1.0.4' +__version__ = '1.0.5'