Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

he third twinx axis can't be converted to log scale #4511

Closed
bhlevca opened this issue Jun 9, 2015 · 10 comments
Closed

he third twinx axis can't be converted to log scale #4511

bhlevca opened this issue Jun 9, 2015 · 10 comments
Labels
status: needs clarification Issues that need more information to resolve.

Comments

@bhlevca
Copy link

bhlevca commented Jun 9, 2015

The third twinx axis can't be converted to log scale if ax.fill_between is used last

If I set all 3 three Y axes to log and X axis is also log matplotlib gets confused if I fill_between is used last. In my complex example the third Y axis remains linear and the last graph is plotted in linear scale and the Ticks remain in linear scale. I am plotting two graphs on each of the 3 axes, one is using ax.plot the other one is using ax.fill_between. If fill_between is used last for the last axis the Y axis remains in linear scale and the fill_between is loted linear although the previous plot was log on the same scale.

twinx_bug

In linear scale all is fine:
twinx_bug3

However, if I reverse the order and use ax.fill_between first and ax.plot after on each axis everything is fine again:

twinx_bug4

Another variant of the bug can be seen by modifying the demo code by adding for each axis log scale:

host = host_subplot(111, axes_class=AA.Axes)
host.set_xscale('log')
host.set_yscale('log')
plt.subplots_adjust(right=0.75)

par1 = host.twinx()
par2 = host.twinx()
par1.set_xscale('log')
par1.set_yscale('log')
par2.set_xscale('log')
par2.set_yscale('log')

http://matplotlib.org/examples/axes_grid/demo_parasite_axes2.html

The ticks on the second Y axis disappears.

twinx_bug2

@tacaswell
Copy link
Member

If you explicitly set the limits does this work ok? There are some issues with auto-scale, log scale, and the order operations.

@bhlevca
Copy link
Author

bhlevca commented Jun 9, 2015

No. I already set the limits in both cases. It seems that fill_between used at the end (after I use plot on the third axis) does something that matplotlib can't handle. If I use ax.fill_between first and then ax.plot everything is fine as shown in the figures.

Actually, I looked closer at the linear scale plot and the shade for the last Y-axis (red plot) is not there. Thus, is not a log related issue is a more generic issue.

@tacaswell
Copy link
Member

I am still really unclear what the issue is, the example based on the parasite demo is fixed by setting the limits at the end and adding a host.set_xscale('log').

@bhlevca
Copy link
Author

bhlevca commented Jun 9, 2015

The example may be fixed with the limits. I used the example to show that there are some issues with simpler code. However if the code is more sophisticated the bug is different and more annoyiong.

Try using ax.fill_between for 2 or more parasite axes and you will see what happens. I will explain again point by point to make it clear:

  1. use 2 or more parasite axes (in addition to the host, 3 axes in total)

  2. set all axes scales to log

  3. plot on each axis 2 graphs:
    3.1 first use ax.plot
    32. then use ax.fill between to create some shade (can use data from the ax.plot +/- some value

  4. Result = > the last ax.fill_between plot will be platted in linear scale and ticks will be changed to linear scale too

  5. if you reverse the order and use ax.fill_betweeen first and use ax.plot after the result is fine

  6. Please look at the attached figures and it will be enough to see the bug.

Even though I found a work around the bug is obvious: fill_between messes up with the last parasite axis. As I said before this happens even in linear scale but it is less obvious.

Please let me know if the explanation is clear now!

@tacaswell
Copy link
Member

Can you provide a minimal example that exercises the bug? There are enough
ways to do the steps listed above it is still hard to understand what is
going on.

On Tue, Jun 9, 2015, 16:01 bhlevca notifications@github.com wrote:

The example may be fixed with the limits. BUT try using ax.fill_between
for 3 or more parasite axes and you will see what happens. I will explain
again point by point to make it clear:

  1. use 2 or more parasiet axex ( in addition to the host)
  2. set all axes scales to log
  3. plot on each axis 2 graphs:
    3.1 first use ax.plot
  1. then use ax.fill between to create some shade (can use data from the
    ax.plot +/- some value
  1. Result = > the last ax.fill_between plot will be platted in lienar
    scale and ticks will be changed to linear scale too

  2. if you reverse the order and use ax.fill_betweeen first and use ax.plot
    after the result is fine

  3. Please look at the attached figures and they will enough to shoe the
    bug.

Even though I found a work around the bug is obvious: fill_between messes
up with the last parasite axis. As I said before this happens even in
linear scale but it is less obvious.

Please let me know if the explanation is clear now!


Reply to this email directly or view it on GitHub
#4511 (comment)
.

@bhlevca
Copy link
Author

bhlevca commented Jun 9, 2015

I created a simple example, but this one works so I don't know what to say. there may be something else screwing up the graph environment. although I use the same function as below. however I have more includes that may interfere.

import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as AA
import numpy as np

def plot_n_Array_with_CI(title, xlabel, ylabel, x_arr, y_arr, ci05, ci95, legend = None, linewidth = 0.8, ymax_lim = None, log = 'linear', \
                         fontsize = 20, plottitle = False, grid = False, twoaxes = False, ylabel2 = None, ymax_lim2 = None, drawslope = False,\
                          threeaxes=False, ylabel3 = None, ymax_lim3 = None):
    fig = plt.figure(facecolor = 'w', edgecolor = 'k')


    ax=[]
    Y_labels=[]
    Y_labels.append(ylabel)
    if  twoaxes or threeaxes:
        ax.append(host_subplot(111, axes_class=AA.Axes))
        plt.subplots_adjust(right=0.75)
    else:
        ax.append(fig.add_subplot(111))
    if twoaxes or threeaxes and len(y_arr) >1:
        ax.append(ax[0].twinx())
        Y_labels.append(ylabel2)
    if threeaxes and len(y_arr) >2:
        ax.append(ax[0].twinx())
        offset = 70
        new_fixed_axis = ax[2].get_grid_helper().new_fixed_axis
        ax[2].axis["right"] = new_fixed_axis(loc="right", axes=ax[2],offset=(offset, 0))
        ax[2].axis["right"].toggle(all=True)   
        Y_labels.append(ylabel3)
    #endif
    for i in range(0,len(x_arr)):

        if log == 'loglog':
                #line = axs.loglog(x, y, linestyle = lst[i], linewidth = lwt, basex = 10, color = colors[i], label = legend[i])
                #axs.set_yscale('log')
                ax[i].set_xscale('log')
                ax[i].set_yscale('log')
                # axs.semilogx(x, y, linestyle = lst[i], linewidth = lwt, color = colors[i])
                print "xscale=log; yscale=log"
        elif log == 'log':
            ax[i].set_xscale('log')
            ax[i].set_yscale('linear')
            #line = axs.plot(x, y, linestyle = lst[i], linewidth = lwt, color = colors[i], label = legend[i])
            print "xscale=linear; yscale=log"
        else:
            #formatter =  EmbeddedSciFormatter(10, 1)
            #axs.yaxis.set_major_formatter(formatter)
            #line = axs.plot(x, y, linestyle = lst[i], linewidth = lwt, color = colors[i], label = legend[i])
            print "xscale=linear; yscale=linear"


    lines = []
    Xmax = []
    Ymax = []
    Xmin = []
    Ymin = []

    lst = ['-', '--', '-.', ':', '-', '--', ':', '-.']
    lst = ['-', '-', '-', '-', '-', '-', '-', '-']
    colors = ['b', 'c', 'r', 'k', 'y', 'm', 'aqua', 'k']


    i = 0
    for a in x_arr:
        x = x_arr[i][3:]
        y = y_arr[i][3:]


        if len(x_arr) < 5:
            lwt = 2.6 - i * 0.2
        else:
            lwt = 1 + i * 0.6

        if threeaxes and i == 2:
            axs = ax[2] 
        if (threeaxes and i == 1) or  (twoaxes and i == 1):
            axs = ax[1]
        else:
            axs = ax[0]


        line = axs.plot(x, y, linestyle = lst[i], linewidth = lwt, color = colors[i], label = legend[i])

        if drawslope:
            # select only the data we need to asses
            x1 = 0.1; x2 = 0.8
            xsl = []
            ysl = []
            for j in range(0, len(x)):
                if x[j] >= x1 and x[j] <= x2:
                    xsl.append(x[j])
                    ysl.append(y[j])
            # end for
            xs = np.array(xsl)
            ys = np.array(ysl)
            # perform regression

            slope, intercept = np.polyfit(np.log10(xs), np.log10(ys), 1)
            yfit = 10 ** (intercept + slope * np.log10(xs))
            # not necessary r_sq = r_squared(ys, ideal_y)
            print "<%d> | slope:%f" % (i, slope)
            axs.plot(xs, yfit, color = 'r')

        lines += line
        i += 1
    # end for

     # plot the confidence intervals
    if len(ci05) > 0:
        i = 0
        xc = 0
        for a in ci05:  # x_arr:
            arry = hasattr(a, "__len__")
            x = x_arr[i][3:]
            y = y_arr[i][3:]

            Xmax.append(np.max(x))
            Xmin.append(np.min(x))

            if (twoaxes and i == 1) or (threeaxes and i == 1):
                axs = ax[1]
            elif threeaxes and i == 2:
                axs = ax[2]
            else:
                axs = ax[0]

            y1 = ci05[i][3:]
            y2 = ci95[i][3:]
            sd = 0.65 - i * 0.15
            ymax = max(np.max(y1), np.max(y2))
            ymin = min(np.min(y1), np.min(y2))

            # line = axs.plot(x, y1, x, y2, color = [sd, sd, sd], alpha = 0.5)
            # print y2 > y1
            axs.fill_between(x, y1, y2, where = y2 > y1, facecolor = [sd, sd, sd], alpha = 0.5, interpolate = True, linewidth = 0.001)
            # axs.fill_between(x, y1, y2, facecolor = 'blue', alpha = 0.5, interpolate = True)
            if len(Ymin) <  i+1:
                Ymin.append(ymin)
            else:
                Ymin[i] = min(Ymin[i], ymin)
            if len(Ymax) <  i+1:
                Ymax.append(ymax)
            else:
                Ymax[i] = max(Ymax[i], ymax)

            i += 1
        #end for 
    # end if len(ci)


    #set limits
    for i in range(0, len(ax)):
        # ax.xaxis.grid(True, 'major')
        vertices = [(Xmin[i], Ymin[i]), (Xmax[i], Ymax[i])]

        ax[i].xaxis.grid(grid, 'minor')
        ax[i].yaxis.grid(grid, 'minor')
        plt.setp(ax[i].get_xticklabels(), visible = True, fontsize = fontsize)
        plt.setp(ax[i].get_yticklabels(), visible = True, fontsize = fontsize)
        #plt.setp(ax[i].get_xlabels(), visible = True, fontsize = fontsize+1)
        #plt.setp(ax[i].get_ylabels(), visible = True, fontsize = fontsize+1)

        ax[i].grid(grid)
        ax[i].set_ylabel(Y_labels[i], fontsize = fontsize)
        ax[i].set_xlabel(xlabel, fontsize = fontsize)
        #fontd = {'family' : 'serif',
        #         'color'  : 'darkred',
        #         'weight' : 'normal',
        #         'size'   : 'large',
        #}
        #ax[i].yaxis.set_label_text(ylabel, fontdict=fontd)
        if i  == 0 :  side = 'left' 
        else: side = "right"       

        if  twoaxes or threeaxes:
            ax[i].axis[side].label.set_fontsize(fontsize)
            ax[i].axis["bottom"].label.set_fontsize(fontsize)


    plt.xlim(xmin=np.min(Xmin), xmax=np.max(Xmax))
    #this causes some problems
    #plt.ylim(ymin=np.min(Ymin), ymax=np.max(Ymax))

    if plottitle:
            plt.title(title, fontsize = fontsize)

    if legend is not None:
        labs = [l.get_label() for l in lines]
        #legnd = ax[0].legend(lines, labs, loc = 'upper right')
        legnd = ax[0].legend(lines, labs, loc = 3)
        for label in legnd.get_texts():
            label.set_fontsize(fontsize - 6)

    plt.show()
# end

fs = 100
T = 1.0
nsamples = T * fs
t = np.linspace(0, T, nsamples, endpoint = False)

sin1 = 5 + np.sin(2 * np.pi * t * 10) * 4.0  # (B) sin1
c11 = 0.9 * sin1  
c12 = 1.1 * sin1 

sin2 = 5 + np.sin(2 * np.pi * t * 20) * 2.0  # (B) sin2
c21 = sin2 - sin2/5. 
c22 = sin2  + sin2/5. 

sin3 = 12 + np.sin(2 * np.pi * t * 3) * 6.0  # (B) sin3
c31 = sin3 - sin3/10. 
c32 = sin3  + sin3/10. 

xa = [t,t,t]
#xa = [t]
ya= [sin1, sin2, sin3]
#ya= [sin1]
ci05 =  [c11, c21, c31]
ci95 =  [c12, c22, c32]
#ci05 =  [c11]
#ci95 =  [c12]

title = "Test"
xlabel = 'Time'
ylabel1 = "Graph1"
ylabel2 = "Graph2"
ylabel3 = "Graph3"

plot_n_Array_with_CI(title, xlabel, ylabel1, xa, ya, ci05, ci95, legend = ["Graph1", "Graph2", "Graph3"], \
                                                    log = "loglog", fontsize = 20, plottitle = None, grid = True, ymax_lim = None,\
                                                    twoaxes = False, ylabel2 = ylabel2, ymax_lim2 = None, drawslope = False,\
                                                    threeaxes = True, ylabel3 = ylabel3, ymax_lim3 = None)

@tacaswell
Copy link
Member

I would not call that a 'simple test case' I am going to leave this open, but mark as needs clarification.

@tacaswell tacaswell added the status: needs clarification Issues that need more information to resolve. label Jun 12, 2015
@tacaswell tacaswell added this to the unassigned milestone Jun 12, 2015
@tacaswell
Copy link
Member

That is a pretty cool demo!

I am going to close this as I don't think this is a bug in mpl.

My guess from skimming this code is that you have a few plt.something calls left and it is operating on not the axes you think. Good luck debugging this!

@OceanWolf
Copy link
Contributor

@tacaswell I think @bhlevca tried to say that that long example didn't show the bug but that the short examples given in the original post do...

@tacaswell
Copy link
Member

@OceanWolf The problem in the initial example is a different issue which is related to the order of using log and auto limit and is fixed by simply the x and y limits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: needs clarification Issues that need more information to resolve.
Projects
None yet
Development

No branches or pull requests

3 participants