Dodanie dowolnej linii do wykresu matplotlib w notatniku ipython

119

Jestem raczej nowy w Pythonie / Matplotlib i używam go w notatniku ipython. Próbuję dodać kilka linii adnotacji do istniejącego wykresu i nie mogę dowiedzieć się, jak wyrenderować linie na wykresie. Na przykład, jeśli wykreślę co następuje:

import numpy as np
np.random.seed(5)
x = arange(1, 101)
y = 20 + 3 * x + np.random.normal(0, 60, 100)
p =  plot(x, y, "o")

Otrzymuję następujący wykres:

piękna wykres punktowy

Jak więc dodać pionową linię od (70,100) do (70,250)? A co z linią ukośną od (70,100) do (90,200)?

Próbowałem kilku rzeczy, które Line2D()doprowadziły mnie jedynie do zamieszania. W Rpo prostu użyłbym funkcji segmentów (), która dodałaby segmenty linii. Czy jest odpowiednik w matplotlib?

JD Long
źródło

Odpowiedzi:

185

Możesz bezpośrednio wykreślić żądane linie, wprowadzając do plotpolecenia odpowiednie dane (granice segmentów):

plot([x1, x2], [y1, y2], color='k', linestyle='-', linewidth=2)

(oczywiście możesz wybrać kolor, szerokość linii, styl linii itp.)

Z twojego przykładu:

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(5)
x = np.arange(1, 101)
y = 20 + 3 * x + np.random.normal(0, 60, 100)
plt.plot(x, y, "o")


# draw vertical line from (70,100) to (70, 250)
plt.plot([70, 70], [100, 250], 'k-', lw=2)

# draw diagonal line from (70, 90) to (90, 200)
plt.plot([70, 90], [90, 200], 'k-')

plt.show()

nowy wykres

gcalmettes
źródło
świetna odpowiedź z doskonałymi i kompletnymi ilustracjami! wielkie dzięki!
JD Long,
2
Niewielka poprawka, powyższy kod powinien przeczytać x = np.arange(1, 101).
WP McNeill
To nie spowoduje narysowania linii, ale tylko odcinek. Pytanie, jak narysować linię rzutem dwa podane punkty, pozostaje bez odpowiedzi.
Alexey
6
@Rmano możesz uniknąć branych pod uwagę segmentów w legendzie, dodając argument etykiety zaczynający się od „_”. Np .:plt.plot([70, 70], [100, 250], 'k-', lw=2, label="_not in legend")
gcalmettes
1
Fakt, że 90jest używany zarówno jako, jak x2i, y1prowadzi do wielu niejasności. Każdy, kto to przegląda, powinien pamiętać, że [70, 90]nie odnosi się do żadnego punktu w lokalizacji x1,y1. Dla odniesienia, oto znaczenie wartości:[x1: 70, x2: 90], [y1: 90, y2: 200]
pookie
61

Dla nowoprzybyłych jeszcze nie jest za późno .

plt.axvline(x, color='r')

Przyjmuje również zakres y, używając ymin i ymax.

lifelogger
źródło
1
Parametry min / max axhline i axvline to wartości skalarne z zakresu od 0 do 1, które kreślą linie w odniesieniu do krawędzi wykresu. Chociaż jest to dobre narzędzie, prawdopodobnie nie jest najlepszym rozwiązaniem problemu autora dotyczącego rysowania linii adnotacji.
podłoże binarne
3
Jest to idealne rozwiązanie, jeśli chcesz dodać linię adnotacji w tle, która obejmuje cały wykres. Jeśli użyję wybranego powyżej rozwiązania do narysowania pionowej linii przy x = 1, muszę określić min i max y, a następnie rozmiar wykresu zostanie automatycznie zmieniony za pomocą bufora, aby linia nie rozciągała się przez całą działkę i to jest kłopotliwe. Jest to bardziej eleganckie i nie zmienia rozmiaru fabuły.
Bonnie
40

Używając vlines:

import numpy as np
np.random.seed(5)
x = arange(1, 101)
y = 20 + 3 * x + np.random.normal(0, 60, 100)
p =  plot(x, y, "o")
vlines(70,100,250)

Podstawowe sygnatury wywoławcze to:

vlines(x, ymin, ymax)
hlines(y, xmin, xmax)
Austin Richardson
źródło
2
To wspaniale. Nie widziałem vline()ani hline()funkcji. A co z liniami ukośnymi? Zredagowałem pytanie, aby dodać przekątną teraz, gdy pokazałeś mi linie H & V.
JD Long
Spróbuj utworzyć a DataFramezawierający współrzędne x, y i wykreśl je za pomocąstyle='k-'
Austin Richardson
Dziękuję, to bardzo przydatne
Alex
6

Matplolib pozwala teraz na „linie adnotacji”, o które zabiegał OP. annotate()Funkcja umożliwia kilka form ścieżkach łączących i bezgłowy i tailess strzałkę, tj prostą linię, jest jednym z nich.

ax.annotate("",
            xy=(0.2, 0.2), xycoords='data',
            xytext=(0.8, 0.8), textcoords='data',
            arrowprops=dict(arrowstyle="-",
                      connectionstyle="arc3, rad=0"),
            )

W dokumentacji jest napisane, że możesz narysować tylko strzałkę z pustym ciągiem jako pierwszym argumentem.

Z przykładu OP:

%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(5)
x = np.arange(1, 101)
y = 20 + 3 * x + np.random.normal(0, 60, 100)
plt.plot(x, y, "o")


# draw vertical line from (70,100) to (70, 250)
plt.annotate("",
              xy=(70, 100), xycoords='data',
              xytext=(70, 250), textcoords='data',
              arrowprops=dict(arrowstyle="-",
                              connectionstyle="arc3,rad=0."), 
              )

# draw diagonal line from (70, 90) to (90, 200)
plt.annotate("",
              xy=(70, 90), xycoords='data',
              xytext=(90, 200), textcoords='data',
              arrowprops=dict(arrowstyle="-",
                              connectionstyle="arc3,rad=0."), 
              )

plt.show()

Przykładowy obraz w treści

Podobnie jak w podejściu w odpowiedzi gcalmettes, możesz wybrać kolor, szerokość linii, styl linii itp.

Oto zmiana w części kodu, która sprawiłaby, że jedna z dwóch przykładowych linii byłaby czerwona, szersza i nie w 100% nieprzezroczysta.

# draw vertical line from (70,100) to (70, 250)
plt.annotate("",
              xy=(70, 100), xycoords='data',
              xytext=(70, 250), textcoords='data',
              arrowprops=dict(arrowstyle="-",
                              edgecolor = "red",
                              linewidth=5,
                              alpha=0.65,
                              connectionstyle="arc3,rad=0."), 
              )

Możesz również dodać krzywą do linii łączącej, dostosowując connectionstyle.

Wayne
źródło
1
Właśnie tego potrzebowałem. Chciałem narysować linię wychodzącą poza granice działki, czego .plot()nie mogę.
Nick S
5

Zamiast nadużywać plotlub annotate, co będzie nieefektywne w przypadku wielu linii, możesz użyć matplotlib.collections.LineCollection:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection

np.random.seed(5)
x = np.arange(1, 101)
y = 20 + 3 * x + np.random.normal(0, 60, 100)
plt.plot(x, y, "o")

# Takes list of lines, where each line is a sequence of coordinates
l1 = [(70, 100), (70, 250)]
l2 = [(70, 90), (90, 200)]
lc = LineCollection([l1, l2], color=["k","blue"], lw=2)

plt.gca().add_collection(lc)

plt.show()

Rysunek z dwoma liniami wykreślonymi przez LineCollection

Potrzeba listę linii [l1, l2, ...], w którym każda z linii jest sekwencją N współrzędnych ( N może być więcej niż dwa).

Dostępne są standardowe słowa kluczowe formatujące, akceptujące albo pojedynczą wartość, w którym to przypadku wartość dotyczy każdego wiersza, albo sekwencja M values , w którym to przypadku wartość dla i- tego wiersza to values[i % M].

Qualia
źródło