5

I have a few boxplots in matplotlib that I want to zoom in on a particular y-range ([0,0.1]) using inset axes. It is not clear to me from the example in the documentation how I should do this for multiple boxplots on the same figure. I was trying to modify the code provided this example, but there was too much unnecessary complexity. My code is pretty simple:

# dataToPlot is a list of lists, containing some data. 
plt.figure()
plt.boxplot(dataToPlot)
plt.savefig( 'image.jpeg', bbox_inches=0)

How do I add inset axes and zoom in on the first boxplot of the two? How can I do it for both?

EDIT: I tried the code below, but here's what I got: enter image description here

What went wrong?

# what's the meaning of these two parameters?
fig = plt.figure(1, [5,4])
# what does 111 mean?
ax = fig.add_subplot(111)
ax.boxplot(data)
# ax.set_xlim(0,21)  # done automatically based on the no. of samples, right?
# ax.set_ylim(0,300) # done automatically based on max value in my samples, right?
# Create the zoomed axes
axins = zoomed_inset_axes(ax, 6, loc=1) # zoom = 6, location = 1 (upper right)
axins.boxplot(data)
# sub region of the original image
#here I am selecting the first boxplot by choosing appropriate values for x1 and x2 
# on the y-axis, I'm selecting the range which I want to zoom in, right?
x1, x2, y1, y2 = 0.9, 1.1, 0.0, 0.01
axins.set_xlim(x1, x2)
axins.set_ylim(y1, y2)
# even though it's false, I still see all numbers on both axes, how do I remove them?
plt.xticks(visible=False)
plt.yticks(visible=False)
# draw a bbox of the region of the inset axes in the parent axes and
# connecting lines between the bbox and the inset axes area
# what are fc and ec here? where do loc1 and loc2 come from?
mark_inset(ax, axins, loc1=2, loc2=4, fc="none", ec="0.5")
plt.savefig( 'img.jpeg', bbox_inches=0)
Ricky Robinson
  • 21,798
  • 42
  • 129
  • 185
  • I'm not sure I know what you mean by "multiple boxplots on the same figure". Do you have multiple subplots? – samb8s Aug 23 '12 at 14:25
  • No, `dataToPlot` contains more than one sample of data, and `plt.boxplot` treats it as such: it draws as many boxplots as there are samples in its input. – Ricky Robinson Aug 23 '12 at 14:28
  • So, can't you just do another `axins=zoomed_inset_axes(ax,6,loc=2)` and set different coordinate range for this next plot? – samb8s Aug 23 '12 at 14:32
  • I'm not setting the position of each boxplot, so I don't know where they will appear exactly. Or am I missing something? – Ricky Robinson Aug 23 '12 at 14:42
  • Maybe I don't exactly know what your question is... do you want to set the range of the zoom plots automatically, rather than explicitly typing the yrange? – samb8s Aug 23 '12 at 14:45
  • Also, `imshow` is for an image plot. Is that really what you want? – samb8s Aug 23 '12 at 14:46
  • Ok, sorry. I wanted to add a box for each boxplot with a zoomed-in view on a specific y-axis range. I thought I could do this with inset axes. Can I? – Ricky Robinson Aug 23 '12 at 14:47
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/15725/discussion-between-samb8s-and-ricky-robinson) – samb8s Aug 23 '12 at 14:51
  • fc is something like "foreground color". For example, setting it to "k" will make the box which shows the zoomed area in the original plot totally black - not very useful :) Setting it to "none" makes it transparent so that the original data are still visible. – domini1000 Nov 26 '14 at 13:04

1 Answers1

15

The loc determines the location of the zoomed axis, 1 for upper right, 2 for upper left and so on. I modified the example code slightly to generate multiple zoomed axis.

import matplotlib.pyplot as plt

from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes
from mpl_toolkits.axes_grid1.inset_locator import mark_inset

import numpy as np

def get_demo_image():
    from matplotlib.cbook import get_sample_data
    import numpy as np
    f = get_sample_data("axes_grid/bivariate_normal.npy", asfileobj=False)
    z = np.load(f)
    # z is a numpy array of 15x15
    return z, (-3,4,-4,3)


fig = plt.figure(1, [5,4])
ax = fig.add_subplot(111)

# prepare the demo image
Z, extent = get_demo_image()
Z2 = np.zeros([150, 150], dtype="d")
ny, nx = Z.shape
Z2[30:30+ny, 30:30+nx] = Z

# extent = [-3, 4, -4, 3]
ax.imshow(Z2, extent=extent, interpolation="nearest",
          origin="lower")

axins = zoomed_inset_axes(ax, 6, loc=1) # zoom = 6
axins.imshow(Z2, extent=extent, interpolation="nearest",
             origin="lower")

# sub region of the original image
x1, x2, y1, y2 = -1.5, -0.9, -2.5, -1.9
axins.set_xlim(x1, x2)
axins.set_ylim(y1, y2)

axins1 = zoomed_inset_axes(ax, 8, loc=2) # zoom = 8
axins1.imshow(Z2, extent=extent, interpolation="nearest",
             origin="lower")

# sub region of the original image
x1, x2, y1, y2 = -1.2, -0.9, -2.2, -1.9
axins1.set_xlim(x1, x2)
axins1.set_ylim(y1, y2)

plt.xticks(visible=False)
plt.yticks(visible=False)

# draw a bbox of the region of the inset axes in the parent axes and
# connecting lines between the bbox and the inset axes area
mark_inset(ax, axins, loc1=2, loc2=4, fc="none", ec="0.5")
mark_inset(ax, axins1, loc1=2, loc2=4, fc="none", ec="0.5")

plt.draw()
plt.show()

enter image description here

Edit1:

Similarly, you can also add zoomed axis in a boxplot. Here is an example

from pylab import *
from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes
from mpl_toolkits.axes_grid1.inset_locator import mark_inset

# fake up some data
spread = rand(50) * 100 
center = ones(25) * 50
flier_high = rand(10) * 100 + 100
flier_low = rand(10) * -100
data = concatenate((spread, center, flier_high, flier_low), 0)

# fake up some more data
spread= rand(50) * 100
center = ones(25) * 40
flier_high = rand(10) * 100 + 100
flier_low = rand(10) * -100
d2 = concatenate( (spread, center, flier_high, flier_low), 0 )
data.shape = (-1, 1)
d2.shape = (-1, 1)
data = [data, d2, d2[::2,0]]

# multiple box plots on one figure
fig = plt.figure(1, [5,4])
ax = fig.add_subplot(111)
ax.boxplot(data)
ax.set_xlim(0.5,5)
ax.set_ylim(0,300)

# Create the zoomed axes
axins = zoomed_inset_axes(ax, 3, loc=1) # zoom = 3, location = 1 (upper right)
axins.boxplot(data)

# sub region of the original image
x1, x2, y1, y2 = 0.9, 1.1, 125, 175
axins.set_xlim(x1, x2)
axins.set_ylim(y1, y2)
plt.xticks(visible=False)
plt.yticks(visible=False)

# draw bboxes of the two regions of the inset axes in the parent axes and
# connect lines between the bbox and the inset axes area
mark_inset(ax, axins, loc1=2, loc2=4, fc="none", ec="0.5")

show() 

enter image description here

Edit2

In case the distribution is heterogeneous, i.e., most values are small with few very large values, the above zooming procedure might not work, as it will zoom both the x as well as y axis. In that case, it is better to change the scale of y-axis to log.

from pylab import *

# fake up some data
spread = rand(50) * 1
center = ones(25) * .5
flier_high = rand(10) * 100 + 100
flier_low = rand(10) * -100
data = concatenate((spread, center, flier_high, flier_low), 0)

# fake up some more data
spread = rand(50) * 1
center = ones(25) * .4
flier_high = rand(10) * 100 + 100
flier_low = rand(10) * -100
d2 = concatenate( (spread, center, flier_high, flier_low), 0 )
data.shape = (-1, 1)
d2.shape = (-1, 1)
data = [data, d2, d2[::2,0]]

# multiple box plots on one figure
fig = plt.figure(1, [5,4]) # Figure Size
ax = fig.add_subplot(111)  # Only 1 subplot 
ax.boxplot(data)
ax.set_xlim(0.5,5)
ax.set_ylim(.1,300)
ax.set_yscale('log')

show()

enter image description here

Sandell0
  • 112
  • 1
  • 3
  • 8
imsc
  • 7,492
  • 7
  • 47
  • 69
  • Thank you. The documentation on this is still too complex for my purposes. I edited my original post to stress what I'm doing right now and removed all the unnecessary lines from the online example. Could you please modify my code, so I can see what I should actually do? Thanks. – Ricky Robinson Aug 27 '12 at 20:15
  • 1
    Check the edited answer. If you have difficulty in understanding any specific part of the code, do let me know. – imsc Aug 28 '12 at 07:40
  • Thank you. I edited my post and added the output of your code and questions on some of the parameters used. – Ricky Robinson Aug 28 '12 at 13:54
  • 2
    You put `y1, y2 = 0.0, 0.01` and `zoom = 6`. This means that in your zoomed y-axis is 0.06 which is still much smaller than [0-250] range in the main axis. You either have to increase the zoom or the y1, y2 value. – imsc Aug 28 '12 at 14:01
  • OK, thanks. It doesn't seem to be working as I expected. I wanted to show where the median is, that is, I wanted to show the range where the blue rectangle lies, but when I zoom in too much, it also zooms in on the x-axis and it is a bit of a mess. – Ricky Robinson Aug 28 '12 at 14:56
  • You can also change `x1, x2` to fix the zoomed x-axis. It seems that most of your data values are much small. In this case you can try setting the y-axis to log by using `ax.set_ylabel('log')`.Regarding other questions in your code, it is best to use help, e.g., `help plt.figure` to find out the more about the function. – imsc Aug 28 '12 at 15:54
  • Yes, I tried with `x1,x2=0.999999,1.000001`, but it didn't help much. Well, I guess there isn't much else I can do at this point. I'll try with log. Thanks. – Ricky Robinson Aug 28 '12 at 16:45
  • Thank you so much for your last edit. Now my graphs are definitely more meaningful. – Ricky Robinson Aug 29 '12 at 13:20