2011-10-29 13 views
23

In R, esiste una funzione denominata abline in cui è possibile tracciare una linea su un grafico basato sulla specifica della pendenza di intercettazione (primo argomento) (secondo argomento). Ad esempio,aggiungi la linea in base alla pendenza e intercetta in matplotlib?

plot(1:10,1:10) 
abline(0,1) 

dove la linea con intercetta 0 e pendenza 1 copre l'intera gamma di trama. Esiste una tale funzione in matplotlib.pyplot?

+2

No, non c'è. Sarebbe una funzione utile da avere. Ci sono 'axvline',' axvspan', 'axhline', e' axhspan', che sono simili funzioni verticali e orizzontali, ma il modo usuale in matplotlib è di tracciare una linea alla pendenza data (il che significa che alla fine zoom oltre, se stai lavorando in modo interattivo.). Il modo "corretto" di farlo (vale a dire che si estende sempre sull'asse indipendentemente dalla posizione dello zoom) è in realtà un po 'complicato, sebbene il framework ('matplotlib.transforms') sia presente. –

+1

Sì, è un peccato ... Anche Matlab non ha questa funzione. D'altra parte, i grafici di R sono statici (il sistema di grafica 'base' per cui esiste' abline'), quindi meno di cui preoccuparsi (è una cosa buona e cattiva, suppongo). – hatmatrix

risposta

4

I supporti per il caso di (intercept, slope) della (0, 1) seguente funzione potrebbe essere utilizzata ed esteso per accogliere altre piste e intercetta, ma non si riaggiustare se limiti assi vengono modificati o autoscale viene riattivata.

def abline(): 
    gca = plt.gca() 
    gca.set_autoscale_on(False) 
    gca.plot(gca.get_xlim(),gca.get_ylim()) 

import matplotlib.pyplot as plt 
plt.scatter(range(10),range(10)) 
abline() 
plt.draw() 
+1

Beh, se vuoi solo una linea che va dall'angolo inferiore sinistro all'angolo in alto a destra, non importa quanto tu faccia lo zoom, quindi puoi semplicemente fare 'plt.plot ([0,1], [0,1 ], transform = plt.gca(). transAxes) '. Questo non rappresenterà una pendenza da 1 a 1 nelle coordinate dei dati, tuttavia, e andrà sempre dall'angolo in basso a sinistra a quello in alto a destra, ovunque si ingrandisca ... Come hai detto, però, un ablina più generale. la sostituzione è più difficile per l'uso interattivo ... –

+0

Ah, questo è abbastanza interessante il transAxes. Posso immaginare che lo userò ad un certo punto ... (Spesso ho molti complotti dove xlim = ylim, o dovrebbe essere). – hatmatrix

8

Non riuscivo a immaginare un modo per farlo senza ricorrere a callback, ma questo sembra funzionare abbastanza bene.

import numpy as np 
from matplotlib import pyplot as plt 


class ABLine2D(plt.Line2D): 

    """ 
    Draw a line based on its slope and y-intercept. Additional arguments are 
    passed to the <matplotlib.lines.Line2D> constructor. 
    """ 

    def __init__(self, slope, intercept, *args, **kwargs): 

     # get current axes if user has not specified them 
     if not 'axes' in kwargs: 
      kwargs.update({'axes':plt.gca()}) 
     ax = kwargs['axes'] 

     # if unspecified, get the current line color from the axes 
     if not ('color' in kwargs or 'c' in kwargs): 
      kwargs.update({'color':ax._get_lines.color_cycle.next()}) 

     # init the line, add it to the axes 
     super(ABLine2D, self).__init__([], [], *args, **kwargs) 
     self._slope = slope 
     self._intercept = intercept 
     ax.add_line(self) 

     # cache the renderer, draw the line for the first time 
     ax.figure.canvas.draw() 
     self._update_lim(None) 

     # connect to axis callbacks 
     self.axes.callbacks.connect('xlim_changed', self._update_lim) 
     self.axes.callbacks.connect('ylim_changed', self._update_lim) 

    def _update_lim(self, event): 
     """ called whenever axis x/y limits change """ 
     x = np.array(self.axes.get_xbound()) 
     y = (self._slope * x) + self._intercept 
     self.set_data(x, y) 
     self.axes.draw_artist(self) 
+0

piccolo miglioramento: scambiare le righe: ax.figure.canvas.draw() e self._update_lim (Nessuno) così la trama viene effettivamente aggiornata senza dover fare clic sulla finestra – tal

+0

@tal Finalmente sulla mia versione di matplotlib (1.4.3) è necessario rendere gli assi padre almeno una volta prima di chiamare 'self.axes.draw_artist (self), altrimenti ottengo un' AssertionError' sulla riga 'asserire self._cachedRenderer non è None' in' Axes. draw_artist'. È sempre possibile inserire un'estrazione aggiuntiva dopo che è stato chiamato '_update_lim'. Di solito inizializzo 'ABLine' dall'interno di una funzione di convenienza che lo fa per me, invece di istanziarlo direttamente. –

26

So che questa domanda è un paio di anni, ma dal momento che non v'è alcuna risposta accettata, io aggiungo quello che funziona per me.

È possibile solo tracciare i valori nel grafico e quindi generare un altro insieme di valori per le coordinate della linea di adattamento ottimale e tracciare il grafico originale. Ad esempio, vedere il codice seguente:

import matplotlib.pyplot as plt 
import numpy as np 

# Some dummy data 
x = [1, 2, 3, 4, 5, 6, 7] 
y = [1, 3, 3, 2, 5, 7, 9] 

# Find the slope and intercept of the best fit line 
slope, intercept = np.polyfit(x, y, 1) 

# Create a list of values in the best fit line 
abline_values = [slope * i + intercept for i in x] 

# Plot the best fit line over the actual values 
plt.plot(x, y, '--') 
plt.plot(x, abline_values, 'b') 
plt.title(slope) 
plt.show() 
5
X = np.array([1, 2, 3, 4, 5, 6, 7]) 
Y = np.array([1.1,1.9,3.0,4.1,5.2,5.8,7]) 

scatter (X,Y) 
slope, intercept = np.polyfit(X, Y, 1) 
plot(X, X*slope + intercept, 'r') 
16

Un sacco di queste soluzioni si stanno concentrando su come aggiungere una linea per la trama che si adatta ai dati. Ecco una soluzione semplice per aggiungere una linea arbitraria alla trama basata su una pendenza e un'intercettazione.

def abline(slope, intercept): 
    """Plot a line from slope and intercept""" 
    axes = plt.gca() 
    x_vals = np.array(axes.get_xlim()) 
    y_vals = intercept + slope * x_vals 
    plt.plot(x_vals, y_vals, '--') 
Problemi correlati