2015-12-01 26 views
14

Chciałbym dodać strzałkę do wykresu liniowego z matplotlib, jak na poniższym wykresie (rys. pgfplots).Strzałka na wykresie liniowym z matplotlib

enter image description here

Jak mogę to zrobić (położenie i kierunek strzałki powinny być parametry idealnie)?

Oto kod do eksperymentowania.

from matplotlib import pyplot 
import numpy as np 

t = np.linspace(-2, 2, 100) 
plt.plot(t, np.sin(t)) 
plt.show() 

Dzięki.

Odpowiedz

8

Z mojego doświadczenia to działa najlepiej za pomocą annotate. W ten sposób unikasz dziwnego wypaczenia, które dostajesz z ax.arrow, co jest trudne do kontrolowania.

EDIT: Zawinęłam go w małą funkcję.

from matplotlib import pyplot as plt 
import numpy as np 


def add_arrow(line, position=None, direction='right', size=15, color=None): 
    """ 
    add an arrow to a line. 

    line:  Line2D object 
    position: x-position of the arrow. If None, mean of xdata is taken 
    direction: 'left' or 'right' 
    size:  size of the arrow in fontsize points 
    color:  if None, line color is taken. 
    """ 
    if color is None: 
     color = line.get_color() 

    xdata = line.get_xdata() 
    ydata = line.get_ydata() 

    if position is None: 
     position = xdata.mean() 
    # find closest index 
    start_ind = np.argmin(np.absolute(xdata - position)) 
    if direction == 'right': 
     end_ind = start_ind + 1 
    else: 
     end_ind = start_ind - 1 

    line.axes.annotate('', 
     xytext=(xdata[start_ind], ydata[start_ind]), 
     xy=(xdata[end_ind], ydata[end_ind]), 
     arrowprops=dict(arrowstyle="->", color=color), 
     size=size 
    ) 


t = np.linspace(-2, 2, 100) 
y = np.sin(t) 
# return the handle of the line 
line = plt.plot(t, y)[0] 

add_arrow(line) 

plt.show() 

To nie jest bardzo intuicyjne, ale działa. Następnie możesz grać ze słownikiem arrowprops, dopóki nie będzie wyglądać dobrze.

+0

Niezły pomysł. Dzięki (+1). Nie ma sposobu na owijanie tego wszystkiego wewnątrz "fabuły"? – cjorssen

+0

Nie, chyba że napiszesz własną funkcję "fabuły" :). Zaletą tego jest to, że rzeczy takie jak adnotacje i tekst są traktowane inaczej przez matplotlib niż rzeczy, które rysujesz, tzn. Zawsze zachowują one swój rozmiar i proporcje, itp., Gdy przeskalujesz lub powiększysz. – thomas

+0

Ok. Ma sens. – cjorssen

3

Nie najpiękniejszy rozwiązanie, ale powinno działać:

import matplotlib.pyplot as plt 
import numpy as np 


def makeArrow(ax,pos,function,direction): 
    delta = 0.0001 if direction >= 0 else -0.0001 
    ax.arrow(pos,function(pos),pos+delta,function(pos+delta),head_width=0.05,head_length=0.1) 

fun = np.sin 
t = np.linspace(-2, 2, 100) 
ax = plt.axes() 
ax.plot(t, fun(t)) 
makeArrow(ax,0,fun,+1) 

plt.show() 
+0

Dzięki (+1) Niezły pomysł. Nie ma szansy na przeciążenie 'plot'? – cjorssen

7

Wystarczy dodać plt.arrow():

from matplotlib import pyplot as plt 
import numpy as np 

# your function 
def f(t): return np.sin(t) 

t = np.linspace(-2, 2, 100) 
plt.plot(t, f(t)) 
plt.arrow(0, f(0), 0.01, f(0.01)-f(0), shape='full', lw=0, length_includes_head=True, head_width=.05) 
plt.show() 

EDIT: Zmieniono parametry strzałką to pozycja & kierunek funkcji do rysowania.

enter image description here

+0

Jeśli zmienię' np.sin' na 'np.cos', muszę zgadnąć nowe współrzędne dla strzałki. Chciałbym tego uniknąć. Odpowiedź @elzell jest wtedy lepsza. Dzięki i tak. – cjorssen

+1

@cjorssen Zmieniono moją odpowiedź, aby dynamicznie obliczyć położenie i kierunek strzałki. – adrianus

+0

Dzięki, to wszystko (+1). – cjorssen

Powiązane problemy