Добавление произвольной строки в график matplotlib в блокноте ipython

119

Я новичок как в python / matplotlib, так и в использовании его через ноутбук ipython. Я пытаюсь добавить несколько линий аннотации к существующему графику, и я не могу понять, как отобразить линии на графике. Так, например, если я нарисую следующее:

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

Получаю следующий график:

красивый график разброса

Итак, как мне добавить вертикальную линию от (70,100) до (70,250)? А как насчет диагональной линии от (70,100) до (90,200)?

Я пробовал несколько вещей, Line2D()но это не привело ни к чему, кроме замешательства. В Rя бы просто использовать сегменты () функция , которая будет добавлять отрезки. Есть ли аналог в matplotlib?

JD Long
источник

Ответы:

185

Вы можете напрямую построить нужные линии, введя в plotкоманду соответствующие данные (границы сегментов):

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

(конечно, вы можете выбрать цвет, ширину линии, стиль линии и т. д.)

Из вашего примера:

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

новый график

gcalmettes
источник
отличный ответ с отличными и полными иллюстрациями! огромное спасибо!
JD Long
2
Незначительное исправление, код выше должен читаться x = np.arange(1, 101).
WP McNeill
При этом будет нарисована не линия, а только сегмент. Остается без ответа вопрос, как провести линию с двумя заданными очками.
Алексей
6
@Rmano, вы можете избежать включения сегментов в легенду, добавив аргумент метки, начинающийся с "_". Пример:plt.plot([70, 70], [100, 250], 'k-', lw=2, label="_not in legend")
gcalmettes
1
Тот факт, что 90используется как x2и, и y1приводит к большой двусмысленности. Для всех, кто просматривает это, обратите внимание, что [70, 90]это не относится к одной точке в местоположении x1,y1. Для справки, вот значения значений:[x1: 70, x2: 90], [y1: 90, y2: 200]
pookie
61

Для новичков еще не поздно .

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

Он также принимает диапазон y, используя ymin и ymax.

lifelogger
источник
1
Параметры min / max для axhline и axvline представляют собой скалярные значения от 0 до 1, которые строят линии относительно края графика. Несмотря на то, что это хороший инструмент, он, вероятно, не лучшее решение авторской задачи рисования линий аннотации.
binarysubstrate
3
Это идеально подходит для добавления линии аннотации на заднем плане, охватывающей весь график. Если я использую выбранное выше решение для рисования вертикальной линии в точке x = 1, я должен указать минимальное и максимальное значение y, а затем график автоматически изменит размер с помощью буфера, чтобы линия не растягивалась по всему графику, и это хлопотно. Это более элегантно и не меняет размер сюжета.
Бонни
40

Использование 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)

Основные подписи вызовов:

vlines(x, ymin, ymax)
hlines(y, xmin, xmax)
Остин Ричардсон
источник
2
это отлично. Я не видел функций vline()или hline(). А как насчет диагональных линий? Я отредактировал вопрос, чтобы добавить диагональный бит, теперь, когда вы показали мне линии h и v.
JD Long
Попробуйте создать, DataFrameсодержащий координаты x, y, и нанести их на графикstyle='k-'
Остин Ричардсон,
Спасибо, это очень удобно,
Alex
6

Matplolib теперь позволяет использовать «строки аннотации», как искал OP. annotate()Функция позволяет несколько форм соединительных путей и обезглавленная и tailess стрелки, т.е. простой линии, является одним из них.

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

В документации говорится, что вы можете нарисовать только стрелку с пустой строкой в ​​качестве первого аргумента.

Из примера 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()

Пример встроенного изображения

Как и в случае с ответом gcalmettes, вы можете выбрать цвет, ширину линии, стиль линии и т. Д.

Вот изменение в части кода, которое сделало бы одну из двух строк примера красной, широкой и не на 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."), 
              )

Вы также можете добавить кривую к соединительной линии, отрегулировав connectionstyle.

Wayne
источник
1
Это то, что мне в итоге понадобилось. Я хотел провести черту, выходящую за пределы сюжета, чего .plot()не может быть.
Nick S
5

Вместо того, чтобы злоупотреблять plotили annotate, что будет неэффективно для многих строк, вы можете использовать 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()

Рисунок с двумя линиями, построенными с помощью LineCollection

Требуется список строк [l1, l2, ...], каждая из которых представляет собой последовательность из N координат ( N может быть больше двух).

Доступны стандартные ключевые слова форматирования, принимающие либо одно значение, в этом случае значение применяется к каждой строке, либо последовательность M values , и в этом случае значение для i- й строки равно values[i % M].

Qualia
источник