2

I have been having trouble creating polar histograms with symlog axis. However this only breaks when there are negative r-values. What can I do to fix this?

I've included a minimal example which displays 5 plots. Plot 4 is the one which does not work (middle row right.) Plot 5 is to show it working with positive numbers.

Here's the output:

enter image description here

Here's the code:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.scale import SymmetricalLogTransform


MIN_BINS = 500
MAX_BINS = 1000
# utility function for making bins of correct size
def get_bins(min_val, max_val, scaling):
    w = 1 + max(MIN_BINS, min(MAX_BINS, abs(min_val - max_val) + 1))

    if scaling == 'log':
        return np.logspace(np.log10(min_val), np.log10(max_val), w)
    elif scaling == 'symlog':
        tr = SymmetricalLogTransform(base=10, linthresh=1, linscale=1)

        ss = tr.transform([min_val, max_val])

        ls = tr.inverted().transform(np.linspace(*ss, num=w))
        return ls
    else:# linear
        return np.linspace(min_val, max_val, w)




TESTS = [1,2,3,4,5]

plt.figure(figsize=(12,18))
plot_pos = 320
for TEST in TESTS:
    plot_pos += 1

    if TEST == 1:
        NEG = True
        YSCALE = 'linear'
        PROJECTION = None

    if TEST == 2:
        NEG = True
        YSCALE = 'symlog'
        PROJECTION = None


    if TEST == 3:
        NEG = True
        YSCALE = 'linear'
        PROJECTION = 'polar'

    # This is what I want to work
    if TEST == 4:
        NEG = True
        YSCALE = 'symlog'
        PROJECTION = 'polar'

    # However, remove the negative numbers and it seems to work
    if TEST == 5:
        NEG = False
        YSCALE = 'symlog'
        PROJECTION = 'polar'



    sample_size = 100000
    xs = np.pi * np.linspace(0.0, 2.0, sample_size)
    ys = 20 * np.random.random(sample_size)

    if NEG:
        ys -= 10

    ax = plt.subplot(plot_pos, projection=PROJECTION)

    ax.set_title('Neg/Scale/Proj.:%s,%s,%s' % (NEG, YSCALE, PROJECTION))

    min_y = np.min(ys)
    max_y = np.max(ys)
    min_x = np.min(xs)
    max_x = np.max(xs)

    x_bins = get_bins(min_x, max_x, 'linear')
    y_bins = get_bins(min_y, max_y, YSCALE)

    hist, xedges, yedges = np.histogram2d(
        xs,
        ys,
        bins=[x_bins, y_bins]
    )

    X, Y = np.meshgrid(xedges, yedges)

    ax.pcolormesh(
        X, Y,
        hist.T,
        cmap=cm.gray_r,
    )

    if PROJECTION == 'polar':
        ax.set_rscale(YSCALE)

        ax.set_rmax(max_y)
        ax.set_rmin(min_y)
    else:
        ax.set_ylim([min_y, max_y])

        ax.set_yscale(YSCALE)

    ax.grid(True)

plt.savefig('polar_example.png')
AnnanFay
  • 9,573
  • 15
  • 63
  • 86

1 Answers1

1

This might be a bug in matplotlib, unfortunately polar plots have multiple scaling issues and might have to go through some major revisions in the future.

I suspect that the issue is this, for your problematic fourth polar-symlog axes:

>>> ax.get_rmin()
-9.9998049982336408

>>> ax.get_rmax()
9.9999564676741315

Negative radii are present since symlog is prepared to handle them. However, matplotlib had some issues plotting these values, and corresponding behaviour even changed in the past. I think the only problem now is that the negative radial limits passed in during the call to ax.set_rmin() are interpreted as reflected positive values. Here's your test when I change the radial case to

if PROJECTION == 'polar':
    ax.set_rscale(YSCALE)

    ax.set_rmax(max_y)
    ax.set_rmin(max(0,min_y)) # change here

fixed result