Adicionando uma linha arbitrária a um gráfico matplotlib no notebook ipython


119

Sou bastante novo em python / matplotlib e em usá-lo por meio do notebook ipython. Estou tentando adicionar algumas linhas de anotação a um gráfico existente e não consigo descobrir como renderizar as linhas em um gráfico. Então, por exemplo, se eu plotar o seguinte:

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

Eu obtenho o seguinte gráfico:

lindo gráfico de dispersão

Então, como eu adicionaria uma linha vertical de (70.100) até (70.250)? Que tal uma linha diagonal de (70.100) a (90.200)?

Tentei algumas coisas que Line2D()resultaram em nada além de confusão da minha parte. Em Reu simplesmente usaria a função segmentos () que adicionaria segmentos de linha. Existe um equivalente em matplotlib?

Respostas:


185

Você pode plotar diretamente as linhas que deseja alimentando o plotcomando com os dados correspondentes (limites dos segmentos):

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

(é claro que você pode escolher a cor, largura da linha, estilo da linha, etc.)

Do seu exemplo:

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

novo gráfico


ótima resposta com ilustrações excelentes e completas! muitíssimo obrigado!
JD Long,

2
Correção menor, o código acima deve ser lido x = np.arange(1, 101).
WP McNeill de

Isso não vai desenhar uma linha, mas apenas um segmento. A questão de como traçar uma linha através de dois pontos dados permanece sem resposta.
Alexey

6
@Rmano você pode evitar que os segmentos sejam levados em consideração na legenda, adicionando um argumento de rótulo começando com "_". Ex:plt.plot([70, 70], [100, 250], 'k-', lw=2, label="_not in legend")
gcalmettes

1
O fato de 90ser usado como x2e e y1leva a muita ambigüidade. Para quem está vendo isso, observe que [70, 90]não se refere a um único ponto no local x1,y1. Para referência, aqui estão os significados dos valores:[x1: 70, x2: 90], [y1: 90, y2: 200]
pookie

61

Não é tarde demais para os recém - chegados .

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

Ele também leva o intervalo de y, usando ymin e ymax.


1
Os parâmetros mín. / Máx. De axhline e axvline são valores escalares entre 0 e 1 que representam as linhas em referência à borda do gráfico. Embora seja uma boa ferramenta, provavelmente não é a melhor solução para o problema do autor de desenhar linhas de anotação.
binarysubstrate

3
Isso é perfeito para adicionar uma linha de anotação no fundo que abrange todo o gráfico. Se eu usar a solução escolhida acima para desenhar uma linha vertical em x = 1, tenho que especificar o mínimo e o máximo y e, em seguida, o gráfico é redimensionado automaticamente com um buffer, para que a linha não se estenda por todo o gráfico, e isso é um aborrecimento. Isso é mais elegante e não redimensiona o gráfico.
Bonnie

40

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

As assinaturas de chamada básicas são:

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

2
isso é excelente. Eu não tinha visto as funções vline()ou hline(). E quanto às linhas diagonais? Editei a pergunta para adicionar a parte diagonal, agora que você me mostrou as linhas h e v.
JD Long,

Tente fazer um DataFramecontendo as coordenadas x, y e plotá-los comstyle='k-'
Austin Richardson

Obrigado, isso é muito útil
Alex

6

Matplolib agora permite 'linhas de anotação' como o OP estava procurando. A annotate()função permite várias formas de conectar caminhos e uma seta sem cabeça e sem cauda, ​​ou seja, uma linha simples, é uma delas.

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

Na documentação , diz que você pode desenhar apenas uma seta com uma string vazia como o primeiro argumento.

Do exemplo do 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()

Imagem embutida de exemplo

Assim como na abordagem da resposta de gcalmettes, você pode escolher a cor, largura da linha, estilo da linha, etc.

Aqui está uma alteração em uma parte do código que tornaria uma das duas linhas de exemplo vermelha, mais larga e não 100% opaca.

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

Você também pode adicionar curva à linha de conexão ajustando o connectionstyle.


1
É disso que acabei precisando. Eu queria traçar uma linha fora dos limites da trama, o que .plot()não posso fazer.
Nick S

5

Em vez de abusar de plotou annotate, que será ineficiente para muitas linhas, você pode usar 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 com duas linhas plotadas via LineCollection

Leva uma lista de linhas [l1, l2, ...], onde cada linha é uma sequência de N coordenadas ( N pode ser mais de duas).

As palavras-chave de formatação padrão estão disponíveis, aceitando um único valor, caso em que o valor se aplica a todas as linhas, ou uma sequência de M values , caso em que o valor da i- ésima linha é values[i % M].

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.