Как установить xlim при сохранении некоторых кадров в файл с помощью matplotlib в Python?

Я использую Python и matplotlib для создания анимации гонок на гистограммах, данные представляют собой временные ряды и обновляются каждый день. Я хочу сохранять в файл только обновленные кадры, например, до вчерашнего дня было 10460 кадров, видеофайл был создан за один час.

Сегодня добавлены новые 10 кадров. Я использовал код для сохранения новейших кадров в файл, как показано ниже, но ось a слишком коротка. Как мне установить для оси x то же значение, что и максимальное значение x в прошлом?

anim = FuncAnimation(self.fig, self.anim_func, frames[10460:], init_func, interval=interval)

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
55
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Краткое изложение того, что следует, и ответ, отвечающий ожиданиям, сформулированным в вопросе, которые не могут быть удовлетворены с помощью matplotlib:

Сам Matplotlib не выполняет автоматически предварительные вычисления и не оценивает все кадры анимации, чтобы заранее установить пределы оси или другие свойства, когда вы используете его метод FuncAnimation().

В FuncAnimation() данные каждого кадра вычисляются и визуализируются независимо в рамках итеративного процесса обновления. Это связано с необходимостью повышения эффективности, особенно в сценариях, где кадры представляют собой непрерывный временной ряд или симуляцию, а предварительное вычисление данных каждого кадра может быть непрактичным из-за ограничений памяти или вычислительных затрат.

Но вы можете преодолеть это ограничение, используя решения, представленные в этом ответе ниже.

Краткий обзор предоставленного кода, отвечающего на вопросы о x_limit для значений оси X графика:

  • код, демонстрирующий, как установить максимальное значение оси X для всех кадров анимации
  • код, демонстрирующий, как динамически регулировать максимальное значение оси X для всех кадров анимации
  • код, демонстрирующий, как сохранить только последние десять кадров анимации
  • код, демонстрирующий, как сохранить только последние десять кадров анимации, устанавливая постоянное максимальное значение для оси X, полученное заранее из набора данных, чтобы все кадры имели одинаковый предел оси X

Посмотрите демонстрацию того, как:

# Set up the figure and axis
fig, ax = plt.subplots()
ax.set_xlim(0, 10)
ax.set_ylim(-1.5, 1.5)

влияет на результат анимации:

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

# Set up the figure and axis
fig, ax = plt.subplots()
ax.set_xlim(0, 10)
ax.set_ylim(-1.5, 1.5)

# Initialize the line plot
line, = ax.plot([], [], lw=2)

# Initial data
x = np.linspace(0, 10, 1000)
y = np.sin(x)

def init():
    line.set_data([], [])
    return line,

def update(frame):
    # Adjust the right endpoint to shorten the wave from the right
    x_new = np.linspace(0, 10 - frame / 10, 1000)
    y_new = np.sin(x_new * np.pi)  # Multiply by pi to maintain wave pattern despite shrinking domain

    line.set_data(x_new, y_new)
    return line,

# Create animation
ani = animation.FuncAnimation(fig, update, frames=np.linspace(0, 30, 100), init_func=init, blit=True)
ani.event_source.stop() #stop the looping
ani.save('fixXaxisRange.gif', writer='pillow', fps=10)

plt.show()

Другой подход — динамически устанавливать диапазон оси X в зависимости от входящих данных:

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

def generate_sine_wave(frame, step=0.1):
    """ Generate x and y data for a sine wave up to the current frame. """
    # Ensure that frame is positive to avoid empty array
    if frame <= 0:
        return np.array([]), np.array([])
    x = np.linspace(0, 2 * np.pi * frame * step, int(frame * step * 100))
    y = np.sin(x)
    return x, y

# Set up the figure, axis, and plot element
fig, ax = plt.subplots()
line, = ax.plot([], [], lw=2)
ax.set_ylim(-1.5, 1.5)  # Set fixed y-axis limits for clarity

# Initialize the animation by setting blank data
def init():
    line.set_data([], [])
    ax.set_xlim(0, 10)  # Initial x-axis limit
    return line,

# Update function for animation
def update(frame):
    x, y = generate_sine_wave(frame)
    if x.size == 0:
        return line,  # If x is empty, just return the existing line object

    line.set_data(x, y)
    
    # Update the x-axis limits dynamically as the line grows
    if x[-1] > ax.get_xlim()[1]:  # Check if we need to expand the x-axis
        ax.set_xlim(ax.get_xlim()[0], x[-1] + 1)  # Add some padding to the right limit
        ax.figure.canvas.draw()  # Redraw the canvas to update the axis labels
    
    return line,

# Number of frames depends on how long you want the animation to run
frames = 60

# Create the animation
ani = animation.FuncAnimation(fig, update, frames=range(1, frames + 1), init_func=init, blit=False, repeat=False)

ani.save('fixXaxisRange_2.gif', writer='pillow', fps=10)

plt.show()

Если вы хотите сохранить только последние 10 кадров анимации, но запустить весь диапазон данных, вы можете указать номер начального кадра анимации (версия, сохраняющая все кадры, закомментирована):

# Total frames in the full animation
total_frames = 100
# Calculate start frame for the last ten frames
start_frame = total_frames - 10

# Create the animation object, but only generate the last ten frames
ani = animation.FuncAnimation(fig, update, frames=range(start_frame, total_frames), init_func=init, blit=False, repeat=False)
#ani = animation.FuncAnimation(fig, update, frames=range(1,total_frames), init_func=init, blit=False, repeat=False)

Ниже приведен случай, в котором диапазон x-данных определяется сначала запуском исходной функции анимации, позволяющей ей сначала сгенерировать все данные для анимации, чтобы определить фактическую функцию анимации с использованием сгенерированных данных. Таким образом, все данные для анимации известны заранее, что позволяет установить ограничение по оси X для всей анимации:

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

def generate_sine_wave(num_frames, step=0.1):
    data = []
    for frame in range(1, num_frames + 1):
        x = np.linspace(0, 2 * np.pi * frame * step, int(100 * step * frame))
        y = np.sin(x)
        data.append((x, y))
    return data

# Number of frames
total_frames = 100

# Precompute all frame data
frame_data = generate_sine_wave(total_frames)

# Determine the maximum x-value across all frames
max_x = max(np.max(x) for x, y in frame_data)

# Set up the figure, axis, and plot element
fig, ax = plt.subplots()
line, = ax.plot([], [], lw=2)
ax.set_ylim(-1.5, 1.5)  # Set fixed y-axis limits for clarity
ax.set_xlim(0, max_x + 1)  # Set the x-axis to the maximum found

# Initialize the animation by setting blank data
def init():
    line.set_data([], [])
    return line,

# Update function for animation
def update(frame):
    x, y = frame_data[frame - 1]  # Use precomputed data
    line.set_data(x, y)
    return line,

# Create the animation object using precomputed frame data
numberOfFinalFramesToSave = 10
ani = animation.FuncAnimation(fig, update, frames=range(total_frames-numberOfFinalFramesToSave, total_frames + 1), init_func=init, blit=False, repeat=False)

# Save the animation as GIF
ani.save('fixXaxisRange_4.gif', writer='pillow', fps=10)

plt.show()

В случае ваших данных возьмите функцию обновления анимации, переименуйте ее и позвольте ей генерировать данные для всех кадров. Затем определите функцию анимации, как это сделано в приведенном выше коде, покадрово предоставляя данные, сгенерированные на первом этапе. Побочным эффектом такого способа создания анимации является то, что максимальное значение x известно заранее и может быть установлено для всех кадров изнутри сценария без необходимости фиксации его значения в коде.

Я запускаю ваш код, нет ни одной строки....

mikezang 30.04.2024 06:24

Можете ли вы объяснить, почему предел x можно установить на максимум?

mikezang 30.04.2024 06:38

Я не хочу каждый раз устанавливать xlim, я хочу знать, может ли matplotlib знать максимальный предел во всех кадрах.

mikezang 30.04.2024 06:40

Извините... Я не понимаю вашего требования... То, что вы спрашиваете, для меня не имеет смысла. Вероятно, проблема с пониманием языка... или проблема с переводом...

oOosys 30.04.2024 06:42

maxplotlib НЕ МОЖЕТ угадать, что вы делаете в своей функции анимации... вы можете, например, анимировать случайные данные в зависимости от времени... невозможно заранее знать, какие значения они будут генерировать.

oOosys 30.04.2024 06:47

сохранение занимает больше времени, я хочу отобразить все кадры в памяти, а затем сохранить последние 10 кадров в файл, поэтому предел x должен быть установлен на максимум, возможно ли это?

mikezang 30.04.2024 06:53

Вот что мне интересно!

mikezang 30.04.2024 06:54

Мои данные одинаковы, кроме последней строки, и я использую те же параметры для создания анимации, возможно ли это в этом случае?

mikezang 30.04.2024 07:01

Другими словами, могу ли я передать все кадры в matplotlib и позволить ему сохранить некоторые кадры в файл?

mikezang 30.04.2024 07:10

Вы невнимательно просмотрели мой вопрос, сохранять все кадры нужно больше часа!

mikezang 30.04.2024 07:22

У меня есть идея, но мне нужно взломать некоторые функции: передать все кадры и сохранить кадры для создания анимации, вывести кадры в buf и получить максимальные значения xlim и ylim перед сохранением кадров, затем установить xlim и ylim, а затем сохранить кадры в файл.

mikezang 30.04.2024 07:43

Моя вторая картинка - это ваша идея, макс х отличается от предыдущей.

mikezang 30.04.2024 07:45

См. последний код, который я добавил. Он должен делать именно то, что вы хотите. Имеет ли это?

oOosys 30.04.2024 08:21

Правильно ли я понимаю, что ваш вопрос возник не из-за того, что вы ничего не знали о matplotlib, а из-за того, что не удалось разработать соответствующую функцию обновления для анимации, доставляющей предварительно вычисленные данные кадра? Теперь вы понимаете, как сложно угадать, о чем вы спрашиваете? Вы уже знали все, что вам нужно для получения желаемого результата... но не сумели правильно применить эти знания, верно?

oOosys 30.04.2024 08:30

Кстати: распечатайте максимальное значение x при запуске скрипта, и у вас будет все, что вам нужно, чтобы при следующем запуске была та же ось X, что и в предыдущем.

oOosys 30.04.2024 08:32

ОК... Надеюсь, я наконец-то дал определенный ответ, который вы искали: то, что вы просили, невозможно с помощью функции matplotlib для создания анимации.

oOosys 30.04.2024 08:57

В вашем коде учитывается только длина стержня, не включая длину метки стержня, на моем изображении за 2 нс показана полоска максимальной длины, но без метки, но на первой показана полная метка стержня, это то, что я здесь спрашивал, это не так просто.

mikezang 30.04.2024 09:03

Если вы не предоставили достаточно информации о том, что хотите, ни данных, ни кода, не ждите ответа, решающего все ваши возможные проблемы. Приведите минимальный пример кода и данных, показывающий, что вы получаете и чем это отличается от того, чего вы хотите достичь.

oOosys 30.04.2024 09:08

Извините, что не четко задал вопрос, мне казалось, что на моих двух фотографиях все по-разному...

mikezang 30.04.2024 09:24

Теперь у вас есть идеальный ответ на ваш вопрос: вам нужно самостоятельно позаботиться о максимальном значении x для оси x, matplotlib вам не поможет. Я понимаю, что вы ожидали другого исхода своего вопроса... но разве я не виноват, что matplotlib не соответствует вашим ожиданиям?

oOosys 30.04.2024 09:28

Другие вопросы по теме