Aggiunta di una riga arbitraria a un grafico matplotlib in ipython notebook


119

Sono piuttosto nuovo sia per python / matplotlib che per usarlo tramite il notebook ipython. Sto cercando di aggiungere alcune linee di annotazione a un grafico esistente e non riesco a capire come renderizzare le linee su un grafico. Quindi, ad esempio, se tracciamo quanto segue:

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")

Ottengo il seguente grafico:

bellissimo grafico a dispersione

Quindi come aggiungerei una linea verticale da (70,100) a (70,250)? Che dire di una linea diagonale da (70,100) a (90,200)?

Ho provato alcune cose con il Line2D()risultato di nient'altro che confusione da parte mia. In Rvorrei semplicemente utilizzare la funzione segmenti () che aggiungerebbe segmenti di linea. C'è un equivalente in matplotlib?

Risposte:


185

Puoi tracciare direttamente le linee che desideri alimentando il plotcomando con i dati corrispondenti (confini dei segmenti):

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

(ovviamente puoi scegliere il colore, la larghezza della linea, lo stile della linea, ecc.)

Dal tuo esempio:

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()

nuovo grafico


ottima risposta con illustrazioni eccellenti e complete! molte molte grazie!
JD Long

2
Correzione minore, il codice sopra dovrebbe leggere x = np.arange(1, 101).
WP McNeill

Questo non disegnerà una linea, ma solo un segmento. La domanda su come disegnare una linea lancia due punti dati rimane senza risposta.
Alexey

6
@Rmano puoi evitare che i segmenti vengano presi in considerazione nella legenda aggiungendo un argomento label che inizia con "_". Es:plt.plot([70, 70], [100, 250], 'k-', lw=2, label="_not in legend")
gcalmettes

1
Il fatto che 90sia usato sia come x2che sia y1porta a molte ambiguità. Per chiunque visualizzi questo, nota che [70, 90]non si riferisce a un singolo punto nella posizione x1,y1. Per riferimento, ecco i significati dei valori:[x1: 70, x2: 90], [y1: 90, y2: 200]
pookie

61

Non è troppo tardi per i nuovi arrivati .

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

Accetta anche l'intervallo di y, usando ymin e ymax.


1
I parametri min / max di axhline e axvline sono valori scalari compresi tra 0 e 1 che tracciano le linee in riferimento al bordo del grafico. Sebbene sia un buon strumento, probabilmente non è la soluzione migliore per l'affermazione problematica dell'autore di disegnare linee di annotazione.
binarysubstrate

3
Questo è perfetto per voler aggiungere una linea di annotazione sullo sfondo che copre l'intero grafico. Se utilizzo la soluzione scelta sopra per disegnare una linea verticale in x = 1, devo specificare il minimo e il massimo y, quindi la trama si ridimensiona automaticamente con un buffer, quindi la linea non si estende su tutta la trama, e è una seccatura. Questo è più elegante e non ridimensiona la trama.
Bonnie

40

Utilizzando 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)

Le firme di chiamata di base sono:

vlines(x, ymin, ymax)
hlines(y, xmin, xmax)

2
è eccellente. Non avevo visto le funzioni vline()o hline(). E le linee diagonali? Ho modificato la domanda per aggiungere il bit diagonale ora che mi hai mostrato le linee h & v.
JD Long

Prova a creare un DataFramecontenente le coordinate x, y e tracciarle constyle='k-'
Austin Richardson

Grazie, è molto utile
Alex

6

Matplolib ora consente le "linee di annotazione" come stava cercando l'OP. La annotate()funzione consente diverse forme di percorsi di collegamento e una freccia senza testa e senza coda, cioè una semplice linea, è una di queste.

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

Nella documentazione dice che puoi disegnare solo una freccia con una stringa vuota come primo argomento.

Dall'esempio dell'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()

Immagine in linea di esempio

Proprio come nell'approccio nella risposta di gcalmettes, puoi scegliere il colore, la larghezza della linea, lo stile della linea, ecc.

Ecco un'alterazione a una parte del codice che renderebbe una delle due linee di esempio rossa, più larga e non opaca al 100%.

# 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."), 
              )

È inoltre possibile aggiungere una curva alla linea di collegamento regolando il connectionstyle.


1
Questo è ciò di cui ho finito per aver bisogno. Volevo tracciare una linea che oltrepassasse i confini della trama, cosa che .plot()non posso fare.
Nick S

5

Invece di abusare di ploto annotate, che sarà inefficiente per molte linee, puoi usare 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()

Figura con due linee tracciate tramite LineCollection

Richiede un elenco di linee [l1, l2, ...], dove ogni linea è una sequenza di N coordinate ( N può essere più di due).

Sono disponibili le parole chiave di formattazione standard, che accettano un singolo valore, nel qual caso il valore si applica a ogni riga, o una sequenza di M values , nel qual caso il valore per la i- esima riga è values[i % M].

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.