简体   繁体   中英

matplotlib autoscale(axis='y') after slicing plot with set_xlim()

As a demonstration, I'm plotting x^0 through x^9 with x values in range from 10 to 20.

Then I'm slicing those images so that I have 9 slices:

x = (10 to 11), (11 to 12) etc. to (18 to 19)

I want my images cropped so that y values are always spread from top to bottom in each slice, but what I'm getting is autoscale always scales to the full dataset rather than the current slice.

import matplotlib.pyplot as plt
import numpy as np


# create some test data
for i in range(10):
    x = np.arange(10,20)
    y = x**i
    plt.plot(x,y,c='red',marker='.',ms=2)

# get all x values in chart and reduce to a sorted list set
xd = [] 
for n in range(len(plt.gca().get_lines())):
        line = plt.gca().get_lines()[n]
        xd.append((line.get_xdata()).tolist())
xd = [item for sublist in xd for item in sublist]
xd = sorted(list(set(xd)))

# attempt to plot slices of x with autoscaled y
ax = plt.gca()
for i in range(len(xd)-1):
    ax.set_xlim([xd[i],xd[i+1]])
    ax.axes.autoscale(enable=True,axis='y', tight=True)
    plt.pause(1) #timing
    #uncommenting the next line will create nine tiny (6kb) image files
    #plt.savefig(('image_%s.png' % i), bbox_inches=0, dpi=48)

In my actual application, I'm attempting to generate 100k tiny images in this manner as a database from stochastic data. For every x there are between 2 and 200 y values. Then I'm using OpenCV to image match new images to the best fit amongst the historical database.

Its critical that the y values are stretched from top to bottom in each image for OpenCV to find a good match.

if it helps my x values will always be int() type and equally spaced

ETA: I've attempted to implement some of the solutions here but have made no progress:

Matplotlib - fixing x axis scale and autoscale y axis

Matplotlib scale y axis based on manually zoomed x axis

but at least I've learned:

Autoscaling always uses the full range of the data, so the y-axis is scaled by full extent of the y-data, not just what's within the x-limits.

still no solution that works here though

def autoscale_y()

presented by @DanHickstein

gives me:

h = np.max(y_displayed) - np.min(y_displayed)
ValueError: zero-size array to reduction operation maximum which has no identity

From those links, I'm unsure where to implement @Joe Kington's mask solution in my for loops.

I'm now working with @bernie solution proposed here to get Y values given X:

How to extract points from a graph?

maybe then I can set_ylim() given the min and max Y values at that X manually?

This would be so much easier if there was a way to autoscale within the defined xlim as a standard matplotlib method

I solved my issue last night by creating a dictionary with x's as keys and a respective list of y's as values.

This occurs as the data is created by the function y=x**i

in essence I'm creating dictionary structure pseudocode:

data[x0] = [x0y1,x0y2,x0y3....]
data[x1] = [x1y1,x1y2,x1y3....]
data[x2] = [x2y1,x2y2,x2y3....]
etc.

I can later reference all the y values at any given x. From there, find the min and max y value for the left and right side of my slice to manually set ylim. If your xlim slice was more than one x segment wide you'd have to repeat the process for each respective x slice within your xlim. In my instance, my x slices are exactly one segment wide.

import matplotlib.pyplot as plt
import numpy as np

# move my range function out of my data creation loop
x = np.arange(10,20,1)

# create a dictionary of my data with x values as keys
data = {}
for i in range(len(x)):
   data[x[i]]=[]

# create some test data
for i in range(10):
    y = x**i
    plt.plot(x,y,c='red',marker='.',ms=2)

    # store my y data to my data dictionary as its created
    xx = x[-len(y):]
    for j in range(len(xx)):
        data[xx[j]].append(y[j])

# get all x values in chart and reduce to a sorted list set
xd = [] 
for n in range(len(plt.gca().get_lines())):
        line = plt.gca().get_lines()[n]
        xd.append((line.get_xdata()).tolist())
xd = [item for sublist in xd for item in sublist]
xd = sorted(list(set(xd)))

# attempt to plot slices of x with autoscaled y
ax = plt.gca()
for i in range(len(xd)-1):
    ax.set_xlim([xd[i],xd[i+1]])

    # reference my min and max y values by left and right borders of x slice
    ymin = min(min(data[xd[i]]), min(data[xd[i+1]]))
    ymax = max(max(data[xd[i]]), max(data[xd[i+1]]))
    # manually set y limits
    ax.set_ylim([ymin,ymax])

    #eliminate my autoscale call
    #ax.axes.autoscale(enable=True,axis='y', tight=True)
    plt.pause(1) #timing

Now when it plots, y is autoscaled to the x slice, not the entire dataset.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM