Анимация точек с метками с помощью matplotlib

У меня есть анимация с линиями, и теперь я хочу пометить точки. Я пробовал plt.annotate() и пробовал plt.text(), но губы не двигаются. Это мой пример кода:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

def update_line(num, data, line):
    newData = np.array([[1+num,2+num/2,3,4-num/4,5+num],[7,4,9+num/3,2,3]])
    line.set_data(newData)
    plt.annotate('A0', xy=(newData[0][0],newData[1][0]))
    return line,


fig1 = plt.figure()

data = np.array([[1,2,3,4,5],[7,4,9,2,3]])
l, = plt.plot([], [], 'r-')
plt.xlim(0, 20)
plt.ylim(0, 20)
plt.annotate('A0', xy=(data[0][0], data[1][0]))
# plt.text( data[0][0], data[1][0], 'A0')

line_ani = animation.FuncAnimation(fig1, update_line, 25, fargs=(data, l),
    interval=200, blit=True)
plt.show()

Не могли бы вы мне помочь?

Мой следующий шаг: у меня есть векторы с началом в этих точках. Эти векторы меняют свою длину и направление на каждом шаге анимации. Как я могу анимировать их?

Без анимации это работает:

soa =np.array( [ [data[0][0],data[1][0],F_A0[i][0][0],F_A0[i][1][0]],
               [data[0][1],data[1][1],F_B0[i][0][0],F_B0[i][1][0]],
               [data[0][2],data[1][2],F_D[i][0][0],F_D[i][1][0]] ])
X,Y,U,V = zip(*soa)
ax = plt.gca()
ax.quiver(X,Y,U,V,angles='xy',scale_units='xy',scale=1)

Во-первых, большое спасибо за ваш быстрый и очень полезный ответ!

Проблема с моей векторной анимацией, которую я решил следующим образом:

annotation = ax.annotate("C0", xy=(data[0][2], data[1][2]), xycoords='data',
    xytext=(data[0][2]+1, data[1][2]+1), textcoords='data',
    arrowprops=dict(arrowstyle="->"))

и в «функции обновления» я пишу:

annotation.xytext = (newData[0][2], newData[1][2])
annotation.xy = (data[0][2]+num, data[1][2]+num)

изменить начальное и конечное положение векторов (стрелки).

Но что, если у меня 100 векторов или больше? Нецелесообразно писать:

annotation1 = ...
annotation2 = ...
    .
    :
annotation100 = ...

Я пробовал со списком:

...
annotation = [annotation1, annotation2, ... , annotation100]
...

def update(num):
    ...
    return line, annotation

и получил эту ошибку: AttributeError: объект «список» не имеет атрибута «оси»

Что я могу сделать? Есть идеи?


person Christiane Homann    schedule 21.08.2013    source источник


Ответы (3)


У вас есть возврат всех объектов, которые изменились из вашей функции обновления. Итак, поскольку ваша аннотация изменила свою позицию, вы также должны вернуть ее:

line.set_data(newData)
annotation = plt.annotate('A0', xy=(newData[0][0],newData[1][0]))
return line, annotation

Подробнее об matplotlib анимации можно прочитать в этом руководстве.

Вы также должны указать функцию init, чтобы FuncAnimation знал, какие элементы нужно удалить из графика при перерисовке при первом обновлении. Таким образом, полный пример будет таким:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

# Create initial data
data = np.array([[1,2,3,4,5], [7,4,9,2,3]])

# Create figure and axes
fig = plt.figure()
ax = plt.axes(xlim=(0, 20), ylim=(0, 20))

# Create initial objects
line, = ax.plot([], [], 'r-')
annotation = ax.annotate('A0', xy=(data[0][0], data[1][0]))
annotation.set_animated(True)

# Create the init function that returns the objects
# that will change during the animation process
def init():
    return line, annotation

# Create the update function that returns all the
# objects that have changed
def update(num):
    newData = np.array([[1 + num, 2 + num / 2, 3, 4 - num / 4, 5 + num],
                        [7, 4, 9 + num / 3, 2, 3]])
    line.set_data(newData)
    # This is not working i 1.2.1
    # annotation.set_position((newData[0][0], newData[1][0]))
    annotation.xytext = (newData[0][0], newData[1][0])
    return line, annotation

anim = animation.FuncAnimation(fig, update, frames=25, init_func=init,
                               interval=200, blit=True)
plt.show()
person Viktor Kerkez    schedule 21.08.2013
comment
Вы можете использовать annotation.xytext = (newData[0][0], newData[1][0]) для обновления позиции аннотации. - person sodd; 21.08.2013
comment
Спасибо @nordev, обновите пример. Но почему annotation.set_position((newData[0][0], newData[1][0])) не работает? Он задокументирован как Установить (x, y) позицию текста. xytext задокументирован как геттер :-/ - person Viktor Kerkez; 21.08.2013
comment
Я использую annotation.set_position() внутри функции моего класса, и она работает - person Christiane Homann; 22.08.2013
comment
Странно, какую версию matplotlib вы используете? Может починили или сломали :D - person Viktor Kerkez; 22.08.2013
comment
Я использую matplotlib.__version__ '1.1.1' - person Christiane Homann; 22.08.2013
comment
Ага, теперь ты мне скажи :D Я уже представил проблему :D Что ты изменил, чтобы я мог протестировать это в 1.2.1 - person Viktor Kerkez; 22.08.2013
comment
@Victor Я также пытаюсь вернуть список объектов pyplot.text и matplotlib.offsetbox.AnnotationBbox и получаю ту же ошибку AttributeError: 'list' object has no attribute 'axes'. Была ли этим устранена проблема ОП? В функции обновления я сбрасываю позицию для каждого и все равно получаю эту ошибку. Можно ли вернуть списки этих объектов? Спасибо. - person ryanjdillon; 27.03.2014
comment
@ViktorKerkez Как это ответ на твой вопрос? Я сталкиваюсь с той же проблемой прямо сейчас. У меня есть динамическое количество векторов, которые я хотел бы анимировать с помощью аннотаций. Я не могу вернуть list из annotation объектов. В итоге я получаю сообщение об ошибке 'list' object has no attribute 'set_animated'. Есть ли у вас новости о том, как анимировать несколько объектов аннотаций? Спасибо! - person Hendrik Wiese; 01.05.2016
comment
Почему annotation.set_animated(True)? annotation возвращается как из init(), так и из update(num), и этого должно быть достаточно, верно? - person Petr Vepřek; 06.07.2020

Я пришел сюда из этот вопрос, где следует обновить аннотацию, в которой используются как xy, так и xytext. Похоже, что для правильного обновления аннотации необходимо установить атрибут .xy аннотации, чтобы установить положение аннотированной точки и использовать .set_position() метод аннотации, чтобы установить положение аннотации. Установка атрибута .xytext не имеет никакого эффекта - на мой взгляд, это несколько сбивает с толку. Ниже полный пример:

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation

fig, ax = plt.subplots()

ax.set_xlim([-1,1])
ax.set_ylim([-1,1])

L = 50
theta = np.linspace(0,2*np.pi,L)
r = np.ones_like(theta)

x = r*np.cos(theta)
y = r*np.sin(theta)

line, = ax.plot(1,0, 'ro')

annotation = ax.annotate(
    'annotation', xy=(1,0), xytext=(-1,0),
    arrowprops = {'arrowstyle': "->"}
)

def update(i):

    new_x = x[i%L]
    new_y = y[i%L]
    line.set_data(new_x,new_y)

    ##annotation.xytext = (-new_x,-new_y) <-- does not work
    annotation.set_position((-new_x,-new_y))
    annotation.xy = (new_x,new_y)

    return line, annotation

ani = animation.FuncAnimation(
    fig, update, interval = 500, blit = False
)

plt.show()

Результат выглядит примерно так:

результат приведенного выше кода

В случае, если версии имеют значение, этот код был протестирован на Python 2.7 и 3.6 с matplotlib версии 2.1.1, и в обоих случаях установка .xytext не имела никакого эффекта, а .set_position() и .xy работали как положено.

person Thomas Kühn    schedule 08.02.2018
comment
Да, так как 'Annotation' object has no attribute 'xytext', annotation.xytext просто не ожидается, что он вообще будет работать. - person ImportanceOfBeingErnest; 08.02.2018
comment
@ImportanceOfBeingErnest хорошее замечание, простое dir(annotation) на самом деле показывает это. Интересно, в какой момент это изменилось. По-видимому, в старых версиях это все еще было. Еще стоит отметить, что blit = True не дает ожидаемого результата в оконном режиме. По крайней мере, для меня он показывает статическую аннотацию с исходными координатами и вращающуюся сверху. Однако эта проблема github рекомендует не использовать блиттинг, если не возникает проблем с производительностью, поэтому может быть, это нормально. - person Thomas Kühn; 08.02.2018
comment
Когда вы устанавливаете blit=True, вам нужно вернуть обоих исполнителей, return line,annotation,. - person ImportanceOfBeingErnest; 08.02.2018
comment
@ImportanceOfBeingErnest На самом деле знаю (я отредактировал код после первой публикации), но проблема остается. В любом случае, меня это не беспокоит, я просто хотел указать на это. Согласно проблеме с github, при сохранении анимации на диск вся фигура все равно перерисовывается каждый кадр. - person Thomas Kühn; 08.02.2018
comment
У меня работает нормально. Может опять дело в макосах? - person ImportanceOfBeingErnest; 08.02.2018
comment
Этот ответ очень помог мне с аналогичной проблемой, но только после того, как я добавил «annotation_clip = False» к вызову «аннотировать». В противном случае, как только я его сохранил, в получившемся фильме отсутствовали стрелки. (Python3.6, матплотлиб 2) - person heisenBug; 10.10.2018
comment
@heisenBug согласно документации, аннотации обрезаются, когда значение для ключевое слово xy находится за пределами области рисования и xycoords не является 'data'. В этом случае установка annotation_clip=False должна помочь сделать аннотацию снова видимой. Вы пробовали какие-либо другие решения, такие как, например, увеличение x- и y-пределов осей, в которых вы строите график? - person Thomas Kühn; 11.10.2018
comment
Увеличение пределов также сработало бы, но я рисовал стрелку поверх изображения с фиксированным размером. В моем случае, чтобы стрелки немного выходили за пределы осей изображения, было предпочтительнее, чем изменение размера стрелки или осей. - person heisenBug; 11.10.2018

Я думаю, что понял, как анимировать несколько аннотаций через список. Сначала вы просто создаете свой список аннотаций:

for i in range(0,len(someMatrix)):
     annotations.append(ax.annotate(str(i), xy=(someMatrix.item(0,i), someMatrix.item(1,i))))

Затем в вашей «анимационной» функции вы делаете то, что уже написали:

for num, annot in enumerate(annotations):
    annot.set_position((someMatrix.item((time,num)), someMatrix.item((time,num))))

(Вы также можете написать его как традиционный цикл for, если вам не нравится способ перечисления). Не забудьте вернуть весь список аннотаций в операторе return.

Тогда важно установить «blit=False» в FuncAnimation:

animation.FuncAnimation(fig, animate, frames="yourframecount",
                          interval="yourpreferredinterval", blit=False, init_func=init)

Стоит отметить, что blit=False может замедлить работу. Но, к сожалению, это единственный способ заставить анимацию аннотаций в списках работать...

person lowlightbud    schedule 18.08.2017