2011-01-14 15 views
11

Haritalarımın özelleştirilmesinde yardıma ihtiyacım var. Tuvalin MacOS'un Grapher'undan varsayılan 2D-grafik şablonu gibi görünmesini istiyorum (ekran görüntüsüne bakın). Matplotlib'deki merkez kaynağı

açıklamak için - I

  • ortalanmış bir eksen oklar
  • tek ile
  • axislines (tercihen bir ilave koyu ızgara her 1 birim ile birlikte)
  • bir ızgara mi origo'da sıfır (en iyi çalıştığım zaman, x ekseninden bir sıfır aldım ve y ekseninden ikinci bir tane aldım.), hafifçe sola taşındı, böylece arkada değil y ekseni

Yardımlarınız için gerçekten minnettarım! http://matplotlib.sourceforge.net/examples/pylab_examples/spine_placement_demo.html

ızgara: ax.grid (True)

orijinde sıfır kurtulun:

+0

Şüphesiz mümkün, ama bir güçlük olabilir: ax.xaxis.set_major_locator(MultipleLocator(1))

Şimdi sadece böyle bir şey yapmak için center_spines çağırabilir. TikZ ile TeX, bu bir seçenek ise, bunu daha kolay yapabilir. En azından, orta eksenli eksenler ve ızgara TikZ'de kesinlikle kolaydır. –

cevap

31

Bu kesinlikle matplotlib ile değerinden daha fazla sorun kategorisine girer, ama işte gidiyorsunuz. Ayrıca, temel durum için centering spines demo in the documentation'a bir göz atın.

Bunu birkaç farklı şekilde yapabilirsiniz, ancak en iyi görsel efekt için, aşağıdakilerin çizgileri boyunca bir şey düşünün. Bu mükemmel olmaktan uzak, ama makul esnek:

import matplotlib.pyplot as plt 
import matplotlib as mpl 
import matplotlib.patheffects 
import numpy as np 

def center_spines(ax=None, centerx=0, centery=0): 
    """Centers the axis spines at <centerx, centery> on the axis "ax", and 
    places arrows at the end of the axis spines.""" 
    if ax is None: 
     ax = plt.gca() 

    # Set the axis's spines to be centered at the given point 
    # (Setting all 4 spines so that the tick marks go in both directions) 
    ax.spines['left'].set_position(('data', centerx)) 
    ax.spines['bottom'].set_position(('data', centery)) 
    ax.spines['right'].set_position(('data', centerx - 1)) 
    ax.spines['top'].set_position(('data', centery - 1)) 

    # Draw an arrow at the end of the spines 
    ax.spines['left'].set_path_effects([EndArrow()]) 
    ax.spines['bottom'].set_path_effects([EndArrow()]) 

    # Hide the line (but not ticks) for "extra" spines 
    for side in ['right', 'top']: 
     ax.spines[side].set_color('none') 

    # On both the x and y axes... 
    for axis, center in zip([ax.xaxis, ax.yaxis], [centerx, centery]): 
     # Turn on minor and major gridlines and ticks 
     axis.set_ticks_position('both') 
     axis.grid(True, 'major', ls='solid', lw=0.5, color='gray') 
     axis.grid(True, 'minor', ls='solid', lw=0.1, color='gray') 
     axis.set_minor_locator(mpl.ticker.AutoMinorLocator()) 

     # Hide the ticklabels at <centerx, centery> 
     formatter = CenteredFormatter() 
     formatter.center = center 
     axis.set_major_formatter(formatter) 

    # Add offset ticklabels at <centerx, centery> using annotation 
    # (Should probably make these update when the plot is redrawn...) 
    xlabel, ylabel = map(formatter.format_data, [centerx, centery]) 
    ax.annotate('(%s, %s)' % (xlabel, ylabel), (centerx, centery), 
      xytext=(-4, -4), textcoords='offset points', 
      ha='right', va='top') 

# Note: I'm implementing the arrows as a path effect rather than a custom 
#  Spines class. In the long run, a custom Spines class would be a better 
#  way to go. One of the side effects of this is that the arrows aren't 
#  reversed when the axes are reversed! 

class EndArrow(mpl.patheffects._Base): 
    """A matplotlib patheffect to add arrows at the end of a path.""" 
    def __init__(self, headwidth=5, headheight=5, facecolor=(0,0,0), **kwargs): 
     super(mpl.patheffects._Base, self).__init__() 
     self.width, self.height = headwidth, headheight 
     self._gc_args = kwargs 
     self.facecolor = facecolor 

     self.trans = mpl.transforms.Affine2D() 

     self.arrowpath = mpl.path.Path(
       np.array([[-0.5, -0.2], [0.0, 0.0], [0.5, -0.2], 
          [0.0, 1.0], [-0.5, -0.2]]), 
       np.array([1, 2, 2, 2, 79])) 

    def draw_path(self, renderer, gc, tpath, affine, rgbFace): 
     scalex = renderer.points_to_pixels(self.width) 
     scaley = renderer.points_to_pixels(self.height) 

     x0, y0 = tpath.vertices[-1] 
     dx, dy = tpath.vertices[-1] - tpath.vertices[-2] 
     azi = np.arctan2(dy, dx) - np.pi/2.0 
     trans = affine + self.trans.clear(
       ).scale(scalex, scaley 
       ).rotate(azi 
       ).translate(x0, y0) 

     gc0 = renderer.new_gc() 
     gc0.copy_properties(gc) 
     self._update_gc(gc0, self._gc_args) 

     if self.facecolor is None: 
      color = rgbFace 
     else: 
      color = self.facecolor 

     renderer.draw_path(gc0, self.arrowpath, trans, color) 
     renderer.draw_path(gc, tpath, affine, rgbFace) 
     gc0.restore() 

class CenteredFormatter(mpl.ticker.ScalarFormatter): 
    """Acts exactly like the default Scalar Formatter, but yields an empty 
    label for ticks at "center".""" 
    center = 0 
    def __call__(self, value, pos=None): 
     if value == self.center: 
      return '' 
     else: 
      return mpl.ticker.ScalarFormatter.__call__(self, value, pos) 

Ben kasten 1'e x ve y majör kene aralıklarını ayarlamak yoktu, ama bunu yapmak kolay. matplotlib ile

x = np.arange(-5, 5) 
y = x 

line, = plt.plot(x, y) 
center_spines() 
plt.axis('equal') 
plt.show() 

alt text

+2

Aferin! Çok iş gibi görünüyor; Erken kalktığım için sevindim. :-) –

+0

Vay, gerçekten ağır gözüküyor. Teşekkürler! Matplotlib yerine ne önerirsiniz? – 0sh

+0

@Steve - Teşekkürler! @mewoshh - Gnuplot'ta biraz daha kolay olabilir. Bununla birlikte, gnuplot'ta eksen çizgilerinin nasıl çizileceğini bilmiyorum (çizim yeniden ölçeklendiğinde güncellenir). Gerisi (merkezlenmiş eksen dikenleri) gnuplotta daha kolaydır, ancak matplotlib'de de bu zor değildir. –

2

Merkezli eksenler, burada bir örnek görmek ("sıfırlanmasını dikenler" arayın) (set_ticks bakınız ...) burada: başka neye baktınız?