2025, Nov 01 03:47

Преобразование сетки детектора в полярные координаты: theta против радиуса

Переводим матрицу бинов детектора в полярные координаты: theta и радиус. Почему cos(theta) искажает анализ и как построить правильный график в Python.

Преобразовать матрицу бинов детектора во что‑то действительно полезное для анализа чаще всего помогает верный выбор системы координат. Если распределение примерно круговое вокруг известного центра, отображение по радиусу и углу сразу выявляет структуру. Трудность не в API построения графиков, а в отображении: как перейти от декартовой сетки индексов бинов к полярному виду, который будет согласованным и однозначным. Ниже — короткое руководство по подходу: почему размещение cos(theta) на одной оси смешивает системы координат и как вместо этого получить аккуратную визуализацию «угол–радиус».

Постановка задачи

У нас есть двумерная сетка счетчиков, индексируемая как [x, y]. В некоторых ячейках нули, часть помечена как непригодные. Центр интереса — в точке (8, 8); для быстрой декартовой визуализации используем pcolor и пару окружностей‑ориентиров.

import numpy as np
from matplotlib import pyplot as plt
from matplotlib.patches import Circle
counts_grid = np.asarray(
[[ 0.,  0.,  0.,  0.,  0.,  0.,  2.,  5.,  2.,  3.,  3.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
 [ 0.,  0.,  0.,  0.,  0.,  4., 10.,  9.,  7., 10.,  6.,  6.,  0.,  0.,  0.,  0.,  0.,  0.],
 [ 0.,  0.,  0.,  0.,  9., 12., 10., 11., 14., 13., 11., 12.,  6.,  0.,  0.,  0.,  0.,  0.],
 [ 0.,  0.,  0.,  0., 12., 16., 17., 14., 13., 14., 13., 12., 12.,  6.,  0.,  0.,  0.,  0.],
 [ 0.,  0., 10., 11.,  0., 14., 18., 16., 14., 18., 16., 14., 13.,  0.,  9.,  0.,  0.,  0.],
 [ 0.,  7., 10., 13., 13., 16., 16., 15., 14., 16., 13., 16., 13., 13., 13.,  7.,  0.,  0.],
 [ 1.,  6., 15., 14., 17., 14., 13., 13., 14., 15.,  1., 13., 13., 12., 12.,  7.,  2.,  0.],
 [ 5., 13., 11., 14., 12., 14., 14., 16., 16., 16., 12.,  1., 12., 14., 12.,  9.,  5.,  0.],
 [ 2., 11., 11., 16., 13., 17., 15., 14.,  0., 14., 14., 13., 13., 16., 10.,  9.,  6.,  1.],
 [ 4., 11., 13., 12., 14., 14., 16., 16., 14., 18., 16.,  1., 14., 12., 12., 11.,  5.,  1.],
 [ 1.,  7., 10., 11., 13., 14.,  1., 19., 15., 19.,  1.,  1., 14., 14., 11., 10.,  1.,  0.],
 [ 0.,  5., 10., 15., 14., 15., 16.,  1., 14.,  1.,  1., 16., 12., 13., 10.,  5.,  0.,  0.],
 [ 0.,  0.,  7., 12., 16., 15., 13., 17., 14., 16., 14., 14., 14., 14.,  7.,  0.,  0.,  0.],
 [ 0.,  0.,  0.,  7.,  0., 14., 14., 15., 16., 16., 14., 11., 13.,  0.,  0.,  0.,  0.,  0.],
 [ 0.,  0.,  0.,  0.,  8., 12., 14., 12., 14., 10., 11., 12.,  7.,  0.,  0.,  0.,  0.,  0.],
 [ 0.,  0.,  0.,  0.,  0.,  8.,  8., 11.,  9.,  9., 10.,  5.,  0.,  0.,  0.,  0.,  0.,  0.],
 [ 0.,  0.,  0.,  0.,  0.,  0.,  4.,  3.,  7.,  6.,  3.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
 [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  2.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.]]
)/12
counts_grid[4][4] = None
counts_grid[-5][-5] = None
counts_grid[4][-5] = None
counts_grid[-5][4] = None
counts_grid[8][8] = None
# Неисправные ячейки
counts_grid[6, 10] = None
counts_grid[10, 6] = None
counts_grid[7, 11] = None
counts_grid[11, 7] = None
counts_grid[11, 9] = None
counts_grid[11, 10] = None
counts_grid[10, 11] = None
counts_grid[9, 11] = None
counts_grid[10, 10] = None
fig_obj, axes_obj = plt.subplots()
plt.pcolor(counts_grid)
plt.colorbar(label="Count per hour")
ring = Circle((8.5, 8.5), 8, edgecolor='red', facecolor='none', linewidth=1)
axes_obj.add_patch(ring)
ring = Circle((8.5, 8.5), 6, edgecolor='red', facecolor='none', linewidth=1)
axes_obj.add_patch(ring)
plt.show()

Что на самом деле происходит

Цель заключалась в том, чтобы поместить cos(phi) на ось X и расстояние от центра — на ось Y. В этом контексте плоский угол удобнее обозначать как theta. Использовать cos(theta) как ось — значит смешивать представления: x = r*cos(theta) — часть преобразования из полярных координат в декартовы, так что одна ось окажется полярной, а другая — нет. Есть и неоднозначность: cos(theta) сопоставляет двум разным углам одно и то же значение, поэтому точки с theta = pi/4 и theta = -pi/4 схлопнутся в одну x‑координату. Это приемлемо лишь тогда, когда анализ явно допускает такую вырожденность.

Более аккуратный способ диагностики круговой или радиальной структуры — последовательно работать в полярных координатах. Для каждого центра ячейки вычислите theta и радиус относительно выбранного центра, затем постройте график: theta по одной оси, радиус по другой, а цвет используйте для значения в ячейке. Так сохраняется взаимно‑однозначное соответствие между ячейками и точками, избегается непреднамеренное «складывание», и удобнее оценивать радиальную симметрию.

Решение: преобразовать индексы сетки в полярные координаты и построить theta против радиуса

Этот фрагмент с помощью meshgrid формирует декартовы координаты центров ячеек, смещает начало в точку (8, 8), «расплющивает» массивы, отбрасывает нулевые ячейки, переводит координаты в полярные и строит диаграмму рассеяния theta против радиуса, кодируя цветом значение ячейки.

x_idx = np.arange(counts_grid.shape[1])
y_idx = np.arange(counts_grid.shape[0])
xx, yy = np.meshgrid(x_idx, y_idx)
origin = (8, 8)
xx = xx - origin[0]
yy = yy - origin[1]
xx_vec = xx.flatten()
yy_vec = yy.flatten()
vals_vec = counts_grid.flatten()
# Оставляем ненулевые ячейки
keep = vals_vec != 0
xx_vec = xx_vec[keep]
yy_vec = yy_vec[keep]
vals_vec = vals_vec[keep]
angle = np.atan2(yy_vec, xx_vec)
radius = np.sqrt(xx_vec ** 2 + yy_vec ** 2)
plt.scatter(angle, radius, c=vals_vec, s=30)
plt.xlabel("Theta (radians)")
plt.ylabel("Radius (unknown unit)")
plt.colorbar(label="Count per hour")
plt.show()

Если для дальнейшего анализа вам действительно нужен cos(theta), можно спроецировать угловую координату на этапе построения, заменив вызов scatter строкой ниже. Имейте в виду: таким образом симметричные углы намеренно сводятся к одним и тем же x‑значениям.

plt.scatter(np.cos(angle), radius, c=vals_vec, s=30)

Почему это важно

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

Выводы

Когда двумерная сетка намекает на круговую симметрию, переведите индексы в полярные координаты относительно интересующего центра и визуализируйте theta против радиуса. Используйте meshgrid для генерации декартовых координат ячеек, сдвигайте к выбранному началу, вычисляйте theta через atan2 и радиус через sqrt, а цветом показывайте значения. Оставляйте cos(theta) для случаев, когда намеренно нужно «схлопнуть» симметричные углы; в остальных ситуациях сохраняйте theta как есть, чтобы получить однозначную картину.

Статья основана на вопросе на StackOverflow от David K. и ответе Nick ODell.