diff --git a/README.rst b/README.rst index 27ee7e4a..a8fba59d 100644 --- a/README.rst +++ b/README.rst @@ -2,7 +2,7 @@ anesthetic: nested sampling post-processing =========================================== :Authors: Will Handley and Lukas Hergt -:Version: 2.1.1 +:Version: 2.1.2 :Homepage: https://github.com/handley-lab/anesthetic :Documentation: http://anesthetic.readthedocs.io/ diff --git a/anesthetic/_version.py b/anesthetic/_version.py index 55fa725b..f8115612 100644 --- a/anesthetic/_version.py +++ b/anesthetic/_version.py @@ -1 +1 @@ -__version__ = '2.1.1' +__version__ = '2.1.2' diff --git a/anesthetic/plot.py b/anesthetic/plot.py index 05429463..c6002486 100644 --- a/anesthetic/plot.py +++ b/anesthetic/plot.py @@ -699,6 +699,7 @@ def make_2d_axes(params, labels=None, lower=True, diagonal=True, upper=True, ticks=ticks, gridspec_kw=gridspec_kw, subplot_spec=subplot_spec) + fig.align_labels() return fig, axes @@ -966,17 +967,18 @@ def hist_plot_1d(ax, data, *args, **kwargs): q = quantile_plot_interval(q=q) xmin = quantile(data, q[0], weights) xmax = quantile(data, q[-1], weights) + range = kwargs.pop('range', (xmin, xmax)) if type(bins) == str and bins in ['knuth', 'freedman', 'blocks']: try: h, edges, bars = hist(data, ax=ax, bins=bins, - range=(xmin, xmax), histtype=histtype, + range=range, histtype=histtype, color=color, *args, **kwargs) except NameError: raise ImportError("You need to install astropy to use astropyhist") else: h, edges, bars = ax.hist(data, weights=weights, bins=bins, - range=(xmin, xmax), histtype=histtype, + range=range, histtype=histtype, color=color, *args, **kwargs) if histtype == 'bar' and not density: @@ -1285,7 +1287,8 @@ def hist_plot_2d(ax, data_x, data_y, *args, **kwargs): pdf[pdf < cmin] = np.ma.masked if cmax is not None: pdf[pdf > cmax] = np.ma.masked - image = ax.pcolormesh(x, y, pdf.T, cmap=cmap, vmin=vmin, + snap = kwargs.pop('snap', True) + image = ax.pcolormesh(x, y, pdf.T, cmap=cmap, vmin=vmin, snap=snap, *args, **kwargs) ax.add_patch(plt.Rectangle((0, 0), 0, 0, fc=cmap(0.999), ec=cmap(0.32), diff --git a/anesthetic/plotting/_matplotlib/hist.py b/anesthetic/plotting/_matplotlib/hist.py index 2e51ee25..68c6b42c 100644 --- a/anesthetic/plotting/_matplotlib/hist.py +++ b/anesthetic/plotting/_matplotlib/hist.py @@ -19,7 +19,9 @@ kde_plot_1d, fastkde_plot_1d, hist_plot_1d, + quantile_plot_interval, ) +from anesthetic.utils import quantile class HistPlot(_WeightedMPLPlot, _HistPlot): @@ -51,10 +53,9 @@ def _get_colors(self, num_colors=None, color_kwds='color'): return super()._get_colors(num_colors, color_kwds) def _post_plot_logic(self, ax, data): - super()._post_plot_logic(ax, data) + ax.set_xlabel(self.xlabel) ax.set_yticks([]) ax.set_ylim(bottom=0) - ax.set_xlim(self.bins[0], self.bins[-1]) class KdePlot(HistPlot, _KdePlot): @@ -138,6 +139,20 @@ class Hist1dPlot(HistPlot): def _kind(self) -> Literal["hist_1d"]: return "hist_1d" + def _calculate_bins(self, data): + if "range" not in self.kwds: + q = self.kwds.get('q', 5) + q = quantile_plot_interval(q=q) + weights = self.kwds.get("weights", None) + xmin = quantile(data, q[0], weights) + xmax = quantile(data, q[-1], weights) + self.kwds["range"] = (xmin, xmax) + result = super()._calculate_bins(data) + self.kwds.pop("range") + else: + result = super()._calculate_bins(data) + return result + @classmethod def _plot( cls, diff --git a/anesthetic/samples.py b/anesthetic/samples.py index 1cf5f0d0..c454f682 100644 --- a/anesthetic/samples.py +++ b/anesthetic/samples.py @@ -413,6 +413,7 @@ def plot_2d(self, axes=None, *args, **kwargs): 'kde': {'diagonal': 'kde_1d', 'lower': 'kde_2d'}, 'kde_1d': {'diagonal': 'kde_1d'}, 'kde_2d': {'lower': 'kde_2d'}, + 'fastkde': {'diagonal': 'fastkde_1d', 'lower': 'fastkde_2d'}, 'hist': {'diagonal': 'hist_1d', 'lower': 'hist_2d'}, 'hist_1d': {'diagonal': 'hist_1d'}, 'hist_2d': {'lower': 'hist_2d'}, diff --git a/anesthetic/weighted_pandas.py b/anesthetic/weighted_pandas.py index 6d4a55fe..614796b7 100644 --- a/anesthetic/weighted_pandas.py +++ b/anesthetic/weighted_pandas.py @@ -10,7 +10,7 @@ from pandas.util._exceptions import find_stack_level from pandas.util import hash_pandas_object from numpy.ma import masked_array -from anesthetic.utils import (compress_weights, neff as neff_, quantile, +from anesthetic.utils import (compress_weights, neff, quantile, temporary_seed, adjust_docstrings) from pandas.core.dtypes.missing import notna @@ -222,10 +222,10 @@ def reset_index(self, level=None, drop=False, inplace=False, else: return answer.__finalize__(self, "reset_index") - def neff(self, axis=0): + def neff(self, axis=0, beta=1): """Effective number of samples.""" if self.isweighted(axis): - return neff_(self.get_weights(axis)) + return neff(self.get_weights(axis), beta=beta) else: return self.shape[axis] diff --git a/tests/test_samples.py b/tests/test_samples.py index b4563fb9..329a0a81 100644 --- a/tests/test_samples.py +++ b/tests/test_samples.py @@ -990,20 +990,6 @@ def test_live_points(): assert not live_points.isweighted() -def test_hist_range_1d(): - """Test to provide a solution to #89""" - np.random.seed(3) - ns = read_chains('./tests/example_data/pc') - ax = ns.plot_1d('x0', kind='hist_1d') - x1, x2 = ax['x0'].get_xlim() - assert x1 > -1 - assert x2 < +1 - ax = ns.plot_1d('x0', kind='hist_1d', bins=np.linspace(-1, 1, 11)) - x1, x2 = ax['x0'].get_xlim() - assert x1 <= -1 - assert x2 >= +1 - - def test_contour_plot_2d_nan(): """Contour plots with nans arising from issue #96""" np.random.seed(3) @@ -1291,6 +1277,10 @@ def test_samples_dot_plot(): axes = samples.x2.plot.hist_1d(ax=ax) assert len(axes.containers) == 1 + fig, ax = plt.subplots() + axes = samples.x2.plot.hist_1d(ax=ax, range=[0, 0.2]) + assert axes.get_xlim()[1] < 0.3 + axes = samples.drop_labels().plot.kde_2d('x0', 'x1') assert len(axes.collections) == 5 assert axes.get_xlabel() == 'x0' @@ -1380,6 +1370,23 @@ def test_samples_plot_labels(): assert samples.get_label(col) == ax.get_xlabel() +@pytest.mark.parametrize('kind', ['kde', 'hist', 'fastkde'] + if 'fastkde' in sys.modules else + ['kde', 'hist']) +def test_samples_empty_1d_ylabels(kind): + samples = read_chains('./tests/example_data/pc') + columns = ['x0', 'x1', 'x2', 'x3', 'x4'] + + axes = samples.plot_1d(columns, kind=kind+'_1d') + for col in columns: + assert axes[col].get_ylabel() == '' + + axes = samples.plot_2d(columns, kind=kind) + for col in columns: + assert axes[col][col].get_ylabel() == samples.get_labels_map()[col] + assert axes[col][col].twin.get_ylabel() == '' + + def test_constructors(): samples = read_chains('./tests/example_data/pc') @@ -1693,3 +1700,83 @@ def test_groupby_plots(): gb_colors = [p.get_facecolor() for p in gb_ax.patches] assert_allclose(mcmc_colors, gb_colors) plt.close('all') + + +def test_hist_1d_no_Frequency(): + np.random.seed(42) + pc = read_chains("./tests/example_data/pc") + axes = pc.plot_2d(['x0', 'x1', 'x2'], kind={'diagonal': 'hist_1d'}) + for i in range(len(axes)): + assert axes.iloc[i, i].twin.get_ylabel() != 'Frequency' + + axes = pc.plot_1d(['x0', 'x1', 'x2'], kind='hist_1d') + for ax in axes: + assert ax.get_ylabel() != 'Frequency' + + fig, ax = plt.subplots() + ax = pc['x0'].plot(kind='hist_1d', ax=ax) + assert ax.get_ylabel() != 'Frequency' + + fig, ax = plt.subplots() + ax = pc.x0.plot.hist_1d(ax=ax) + assert ax.get_ylabel() != 'Frequency' + + +@pytest.mark.parametrize('kind', ['kde', 'hist']) +def test_axes_limits_1d(kind): + np.random.seed(42) + pc = read_chains("./tests/example_data/pc") + + axes = pc.plot_1d('x0', kind=f'{kind}_1d') + xmin, xmax = axes['x0'].get_xlim() + assert -0.9 < xmin < 0 + assert 0 < xmax < 0.9 + + pc.x0 += 3 + pc.plot_1d(axes, kind=f'{kind}_1d') + xmin, xmax = axes['x0'].get_xlim() + assert -0.9 < xmin < 0 + assert 3 < xmax < 3.9 + + pc.x0 -= 6 + pc.plot_1d(axes, kind=f'{kind}_1d') + xmin, xmax = axes['x0'].get_xlim() + assert -3.9 < xmin < -3 + assert 3 < xmax < 3.9 + + +@pytest.mark.parametrize('kind, kwargs', + [('kde', {}), + ('hist', {'levels': [0.95, 0.68]}), + ]) +def test_axes_limits_2d(kind, kwargs): + np.random.seed(42) + pc = read_chains("./tests/example_data/pc") + + axes = pc.plot_2d(['x0', 'x1'], kind=f'{kind}_2d', **kwargs) + xmin, xmax = axes['x0']['x1'].get_xlim() + ymin, ymax = axes['x0']['x1'].get_ylim() + assert -0.9 < xmin < 0 + assert 0 < xmax < 0.9 + assert -0.9 < ymin < 0 + assert 0 < ymax < 0.9 + + pc.x0 += 3 + pc.x1 -= 3 + pc.plot_2d(axes, kind=f'{kind}_2d', **kwargs) + xmin, xmax = axes['x0']['x1'].get_xlim() + ymin, ymax = axes['x0']['x1'].get_ylim() + assert -0.9 < xmin < 0 + assert 3 < xmax < 3.9 + assert -3.9 < ymin < -3 + assert 0 < ymax < 0.9 + + pc.x0 -= 6 + pc.x1 += 6 + pc.plot_2d(axes, kind=f'{kind}_2d', **kwargs) + xmin, xmax = axes['x0']['x1'].get_xlim() + ymin, ymax = axes['x0']['x1'].get_ylim() + assert -3.9 < xmin < -3 + assert 3 < xmax < 3.9 + assert -3.9 < ymin < -3 + assert 3 < ymax < 3.9 diff --git a/tests/test_weighted_pandas.py b/tests/test_weighted_pandas.py index aab44e11..493e90c0 100644 --- a/tests/test_weighted_pandas.py +++ b/tests/test_weighted_pandas.py @@ -379,14 +379,18 @@ def test_WeightedDataFrame_sample(frame): def test_WeightedDataFrame_neff(frame): - neff = frame.neff() - assert isinstance(neff, float) - assert neff < len(frame) - assert neff > len(frame) * np.exp(-0.25) - - neff = frame.neff(1) - assert isinstance(neff, int) - assert neff == len(frame.T) + N_eff = frame.neff() + assert isinstance(N_eff, float) + assert N_eff < len(frame) + assert N_eff > len(frame) * np.exp(-0.25) + + N_eff = frame.neff(1) + assert isinstance(N_eff, int) + assert N_eff == len(frame.T) + + # beta kwarg + for beta in [0.5, 1, 2, np.inf, '0.5', 'equal', 'entropy', 'kish']: + assert frame.neff(beta=beta) == neff(frame.get_weights(), beta=beta) def test_WeightedDataFrame_compress(frame):