How to calculate relative positions between objects

Hi everyone

I've been going around matplotlib objects trying to find a way to
pre-calculate positions depending on input.

I'm trying to create a function that draws two barplots facing opposite
directions.

This is what I managed so far:

···

###

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import gamma
from mpl_toolkits.axes_grid import make_axes_locatable

fig = plt.figure(1, (10, 10))
ax1 = fig.add_subplot(1, 1, 1)

ax1.set_xlim(1,0)

labels = ('0-9', '10-19', '20-29', '30-39', '40-49',
            '50-59', '60-69', '70-79', '80-89', '90-99')

pad = np.max([len(i) for i in labels]) * .15

divider = make_axes_locatable(ax1)
ax2 = divider.new_horizontal(size="100%", pad=pad, sharey=ax1)
fig.add_axes(ax2)

N = 10
ind = np.arange(N) # the x locations for the groups
width = 0.35 # the width of the bars

x = [gamma.pdf(i, 1, scale=2) for i in range(N)]
y = [gamma.pdf(i, 5, scale=1) for i in range(N)]

ind = np.arange(N)

ax1.barh(ind, x)
ax2.barh(ind, y)

for loc, spine in ax1.spines.iteritems():
    if loc in ['left','top']:
        spine.set_color('none') # don't draw spine

for loc, spine in ax2.spines.iteritems():
    if loc in ['right','top']:
        spine.set_color('none') # don't draw spine

ax1.set_title('Men')
ax2.set_title('Women')

ax1.set_ylim(0, N)
ax2.set_ylim(0, N)

# Name bars
ax1.set_yticks(ind + width)
ax1.xaxis.set_ticks_position('bottom')
ax2.xaxis.set_ticks_position('bottom')
ax1.yaxis.set_ticks_position('right')
ax2.yaxis.set_ticks_position('left')

for tl in ax1.get_yticklabels():
    tl.set_visible(False)

ax2.set_yticklabels(labels,
           horizontalalignment='center'
           )

for i in ax2.get_yticklabels():
    i.set_position((-(pad * .12) , 0))

plt.show()

###

However I would like to generalize this function but he space between
the two plots and the position of the labels is giving me a hard time.

In particular, the lines:

pad = np.max([len(i) for i in labels]) * .15
    i.set_position((-(pad * .12) , 0))

include two values (.15 and .12) that are completely arbitrary and
defined by trial and error for current labels.

However with different labels:
labels = ('0000-9000', '10-19', '20-29', '30-39', '40-49',
            '50-59', '60-69', '70-79', '80-89', '90000-99000')

the values are no longer valid and the final image is no longer properly
aligned.

I know my current approach is not the proper way but due to the
complexity of matplotlib, my very superficial knowledge about it and the
overwhelming documentation this was the closest I could get.

In the end I would like to contribute the final result as something to
be included in the gallery/examples section of the website as I'm
positive that it will be helpful to others.

Any comment or suggestion is extremely welcome.

Cheers,
Renato

Here is a slightly revised version of your script.
It has a separate axes for labeling whose width is determined by the
maximum width of the labels (using MaxExtent from axes_grid toolkit).
Give it a try and see if it fits your needs.

Regards,

-JJ

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import gamma
from mpl_toolkits.axes_grid import make_axes_locatable

fig = plt.figure(1, (10, 10))
ax1 = fig.add_subplot(1, 1, 1)

labels = ('0-9', '10-19', '20-29', '30-39', '40-49',
           '50-59', '60-69', '70-79', '80-89', '90-99')

pad = np.max([len(i) for i in labels]) * .15

divider = make_axes_locatable(ax1)

from mpl_toolkits.axes_grid.axes_size import MaxExtent

labeltexts = []
labelextent = MaxExtent(labeltexts, "width")
padsmall=0.1
label_axes = divider.new_horizontal(size=labelextent, pad=padsmall,
                                    sharey=ax1, frame_on=False)
label_axes.xaxis.set_visible(False)
label_axes.yaxis.set_visible(False)
fig.add_axes(label_axes)

ax2 = divider.new_horizontal(size="100%", pad=padsmall, sharey=ax1)
fig.add_axes(ax2)

N = 10
ind = np.arange(N) # the x locations for the groups
width = 0.35 # the width of the bars

x = [gamma.pdf(i, 1, scale=2) for i in range(N)]
y = [gamma.pdf(i, 5, scale=1) for i in range(N)]

ind = np.arange(N)

ax1.barh(ind, x, align="center")
ax2.barh(ind, y, align="center")

for loc, spine in ax1.spines.iteritems():
   if loc in ['left','top']:
       spine.set_color('none') # don't draw spine

for loc, spine in ax2.spines.iteritems():
   if loc in ['right','top']:
       spine.set_color('none') # don't draw spine

ax1.set_title('Men')
ax2.set_title('Women')

ax1.set_ylim(-0.5, N-0.5)
#ax2.set_ylim(0, N)

# Name bars
ax1.set_yticks(ind)
ax1.xaxis.set_ticks_position('bottom')
ax2.xaxis.set_ticks_position('bottom')
ax1.yaxis.set_ticks_position('right')
ax2.yaxis.set_ticks_position('left')

for tl in ax1.get_yticklabels()+ax2.get_yticklabels():
   tl.set_visible(False)

for i, l in zip(ind, labels):
    l = label_axes.annotate("$%s$"%l, (0.5, i), ha="center", va="center")
    labeltexts.append(l)

ax1.set_xlim(0.5, 0)
ax2.set_xlim(0, 0.5)

plt.show()

···

On Sun, Jan 31, 2010 at 7:40 PM, Renato Alves <rjalves@...2813...> wrote:

Hi everyone

I've been going around matplotlib objects trying to find a way to
pre-calculate positions depending on input.

I'm trying to create a function that draws two barplots facing opposite
directions.

This is what I managed so far:

###

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import gamma
from mpl_toolkits.axes_grid import make_axes_locatable

fig = plt.figure(1, (10, 10))
ax1 = fig.add_subplot(1, 1, 1)

ax1.set_xlim(1,0)

labels = ('0-9', '10-19', '20-29', '30-39', '40-49',
'50-59', '60-69', '70-79', '80-89', '90-99')

pad = np.max([len(i) for i in labels]) * .15

divider = make_axes_locatable(ax1)
ax2 = divider.new_horizontal(size="100%", pad=pad, sharey=ax1)
fig.add_axes(ax2)

N = 10
ind = np.arange(N) # the x locations for the groups
width = 0.35 # the width of the bars

x = [gamma.pdf(i, 1, scale=2) for i in range(N)]
y = [gamma.pdf(i, 5, scale=1) for i in range(N)]

ind = np.arange(N)

ax1.barh(ind, x)
ax2.barh(ind, y)

for loc, spine in ax1.spines.iteritems():
if loc in ['left','top']:
spine.set_color('none') # don't draw spine

for loc, spine in ax2.spines.iteritems():
if loc in ['right','top']:
spine.set_color('none') # don't draw spine

ax1.set_title('Men')
ax2.set_title('Women')

ax1.set_ylim(0, N)
ax2.set_ylim(0, N)

# Name bars
ax1.set_yticks(ind + width)
ax1.xaxis.set_ticks_position('bottom')
ax2.xaxis.set_ticks_position('bottom')
ax1.yaxis.set_ticks_position('right')
ax2.yaxis.set_ticks_position('left')

for tl in ax1.get_yticklabels():
tl.set_visible(False)

ax2.set_yticklabels(labels,
horizontalalignment='center'
)

for i in ax2.get_yticklabels():
i.set_position((-(pad * .12) , 0))

plt.show()

###

However I would like to generalize this function but he space between
the two plots and the position of the labels is giving me a hard time.

In particular, the lines:

pad = np.max([len(i) for i in labels]) * .15
i.set_position((-(pad * .12) , 0))

include two values (.15 and .12) that are completely arbitrary and
defined by trial and error for current labels.

However with different labels:
labels = ('0000-9000', '10-19', '20-29', '30-39', '40-49',
'50-59', '60-69', '70-79', '80-89', '90000-99000')

the values are no longer valid and the final image is no longer properly
aligned.

I know my current approach is not the proper way but due to the
complexity of matplotlib, my very superficial knowledge about it and the
overwhelming documentation this was the closest I could get.

In the end I would like to contribute the final result as something to
be included in the gallery/examples section of the website as I'm
positive that it will be helpful to others.

Any comment or suggestion is extremely welcome.

Cheers,
Renato

------------------------------------------------------------------------------
The Planet: dedicated and managed hosting, cloud storage, colocation
Stay online with enterprise data centers and the best network in the business
Choose flexible plans and management services without long-term contracts
Personal 24x7 support from experience hosting pros just a phone call away.
http://p.sf.net/sfu/theplanet-com
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-users