Skip to content

align breaks rwidth for log-scaled histogram plots #11506

Open
@lzkelley

Description

@lzkelley

Bug report

Bug summary

When plotting a histogram (plt.hist) on a log-scaled axis, using the align parameter to set either 'left' or 'right' alignments changes the width of bars when using the rwidth parameter. There are two plots from the below script attached. Each plot contains a group of three axes in an L-shape, the left set plots histograms using 'mid' for all alignments while the right set uses ['left', 'mid', 'right'] for alignments of the [red, blue, green] histograms respectively. The first example sets the x and y axes scales to 'linear' (which produces correct results) and second sets the scales to 'log', which messes up the widths of the histogram bars.

Code for reproduction

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt

np.random.seed(12345)
nbins = 10
shp = (int(1e2), 3)
WID = 0.7
colors = ['red', 'blue', 'forestgreen']
ALIGN = ['left', 'mid', 'right']

# THIS WORKS FINE:
# XSCALE = 'linear'
# YSCALE = 'linear'

# THIS DOES NOT WORK:
XSCALE = 'log'
YSCALE = 'log'

fig, axes = plt.subplots(figsize=[12, 6], ncols=4, nrows=2)
plt.subplots_adjust(left=0.05, right=0.95, bottom=0.05, top=0.9)

# Generate Data
# ------------------------
xx = np.random.lognormal(1.0, 2.0, shp)
yy = np.random.lognormal(1.0, 2.0, shp)

for ii in range(shp[1]):
    aa = 4.0
    bb = (ii + 1)
    xx[:, ii] += np.maximum(np.random.normal(aa, bb, shp[0]), 0.0)
    yy[:, ii] += np.maximum(np.random.normal(aa, bb, shp[0]), 0.0)

bads = (xx < np.percentile(xx, 1)) | (xx > np.percentile(xx, 99))
xx[bads] = np.random.choice(xx[~bads], bads.sum())

bads = (yy < np.percentile(yy, 1)) | (yy > np.percentile(yy, 99))
yy[bads] = np.random.choice(yy[~bads], bads.sum())

xlim = [np.min(xx), np.max(xx)]
ylim = [np.min(yy), np.max(yy)]


# Plot Data
# -----------------------

def plot_scatter_hist(axes, align_flag):
    align = [aa if align_flag else 'mid' for aa in ALIGN]

    # Scatter
    axes[0, 1].axis('off')
    ax = axes[1, 0]
    ax.set(xscale=XSCALE, yscale=YSCALE, xlim=xlim, ylim=ylim)
    for ii in range(3):
        ax.scatter(xx[:, ii], yy[:, ii], alpha=0.2, color=colors[ii])


    # Marginalize y
    if XSCALE.startswith('log'):
        edges = np.logspace(*np.log10(xlim), nbins)
    else:
        edges = np.linspace(*xlim, nbins)
        
    ax = axes[0, 0]
    ax.set(xscale=XSCALE, yscale=YSCALE, xlim=xlim)
    for ii in range(3):
        ax.hist(xx[:, ii], bins=edges, color=colors[ii], rwidth=WID, alpha=0.5, align=align[ii])

    # Marginalize x
    if YSCALE.startswith('log'):
        edges = np.logspace(*np.log10(ylim), nbins)
    else:
        edges = np.linspace(*ylim, nbins)

    ax = axes[1, 1]
    ax.set(xscale=XSCALE, yscale=YSCALE, ylim=ylim)
    for ii in range(3):
        ax.hist(yy[:, ii], bins=edges, color=colors[ii], rwidth=WID, alpha=0.5,
                align=align[ii], orientation='horizontal')

    text = "Align: {}".format(align) + "\n" + "Matplotlib version: {}".format(mpl.__version__)
    axes[0, 0].set_title(text)
    

plot_scatter_hist(axes[:, :2], False)
plot_scatter_hist(axes[:, 2:], True)
    
fig.savefig('scatter-hist-align_x-{}_y-{}_mpl-{}.png'.format(XSCALE, YSCALE, mpl.__version__))

Actual outcome

Linear axes look fine (widths of histograms in the right triad are all uniform):

scatter-hist-align_x-linear_y-linear_mpl-2 2 2

Log axes the bar widths get screwed up (widths of histograms in the right triad are all different):

scatter-hist-align_x-log_y-log_mpl-2 2 2

Matplotlib version

  • Operating system: macOS 10.12.6
  • Matplotlib version: 2.2.2
  • Matplotlib backend (print(matplotlib.get_backend())): MacOSX
  • Python version: 3.5.5
  • Jupyter version (if applicable): 5.5.0
  • Other libraries:

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions