2025, Nov 28 09:01
Вторичная ось X в Matplotlib: как избежать деления на ноль
Разбираем предупреждение «деление на ноль» при 1/x на secondary_xaxis в Matplotlib и даем безопасное преобразование для связи частоты и периода с примером кода.
При добавлении вторичной оси X в Matplotlib и отображении частоты в период с помощью простого преобразования 1/x вы можете получить предупреждение “деление на ноль”, даже если ваши данные никогда не достигают нуля. На первый взгляд это выглядит странно, ведь все значения x на графике положительные и корректные.
Как воспроизвести проблему
В примере ниже используется логарифмическая шкала по оси X и верхняя вторичная ось, заданная обратным преобразованием. График строится, но во время выполнения появляется предупреждение о делении на ноль.
import matplotlib.pyplot as plt
fig_plot, axes_main = plt.subplots(figsize=(10, 10))
freq_vals = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.6, 1.8, 2.0]
amp_vals = [0.0, 0.0, 0.0, 0.0, 0.0, 0.012, 0.031, 0.074, 0.082, 0.084, 0.080, 0.078, 0.072, 0.059, 0.039, 0.019, 0.010]
axes_main.semilogx(freq_vals, amp_vals, marker='s', color='purple')
plt.xlim(0.1, 10)
plt.xlabel('Frequency (Hz)')
plt.ylabel('Crest Loss (ft)')
axes_main.set(title='Fundamental Frequency')
axes_main.grid()
axes_main.grid(which="minor", color="0.9")
def tx_forward(x):
return 1 / x
def tx_inverse(x):
return 1 / x
axes_top = axes_main.secondary_xaxis('top', functions=(tx_forward, tx_inverse))
axes_top.set_xlabel('Period (s)')
plt.show()Почему возникает предупреждение
Такое поведение связано с тем, как механизм вторичной оси вычисляет переданные вами функции преобразования. В официальном примере Matplotlib 1/x явно защищено от нуля, чтобы избежать предупреждений о делении на ноль. Иными словами, дело не в ваших данных на графике; важно, чтобы используемое преобразование было безопасным для всех значений, которые библиотека может попытаться вычислить. Третий пример в документации демонстрирует защитную реализацию и доступен по адресу https://matplotlib.org/stable/gallery/subplots_axes_and_figures/secondary_axis.html#secondary-axis; сопутствующий пример на странице документации secondary_axis делает то же самое.
Решение: обратная функция с защитой от нуля
Следуйте подходу из документации: оберните обратную функцию в обработчик, который отдельно учитывает ноль. Ниже — вариант, который можно подставить в пример выше; он сохраняет поведение и устраняет предупреждение.
import matplotlib.pyplot as plt
import numpy as np
fig_plot, axes_main = plt.subplots(figsize=(10, 10))
freq_vals = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.6, 1.8, 2.0]
amp_vals = [0.0, 0.0, 0.0, 0.0, 0.0, 0.012, 0.031, 0.074, 0.082, 0.084, 0.080, 0.078, 0.072, 0.059, 0.039, 0.019, 0.010]
axes_main.semilogx(freq_vals, amp_vals, marker='s', color='purple')
plt.xlim(0.1, 10)
plt.xlabel('Frequency (Hz)')
plt.ylabel('Crest Loss (ft)')
axes_main.set(title='Fundamental Frequency')
axes_main.grid()
axes_main.grid(which="minor", color="0.9")
def safe_reciprocal(x):
x_arr = np.array(x, float)
zmask = np.isclose(x_arr, 0)
x_arr[zmask] = np.inf
x_arr[~zmask] = 1 / x_arr[~zmask]
return x_arr
inv_safe_reciprocal = safe_reciprocal
axes_top = axes_main.secondary_xaxis('top', functions=(safe_reciprocal, inv_safe_reciprocal))
axes_top.set_xlabel('Period (s)')
plt.show()Для корректных входных значений функционально это эквивалентно и просто обходится без случая деления на ноль. Как отмечают практики, добавление этой проверки убирает предупреждение.
Почему это важно
Вторичные оси удобны для показа связанных единиц измерения — например, частоты и периода, — но они опираются на заданные пользователем преобразования, которые должны быть устойчивыми на всей области вычислений. Если у преобразования есть известные сингулярности, их учет предотвращает предупреждения во время выполнения и делает графики предсказуемыми.
Итоги
При использовании secondary_xaxis в Matplotlib с обратным соответствием явно защищайте преобразование от нуля — как показано в официальных примерах. Если все же возникли неожиданные предупреждения, проверьте, какие значения получает функция преобразования, и при отладке прикладывайте полный трассировочный вывод: оба шага помогают яснее увидеть первопричину и решение.