Итак, у меня есть данные неба по азимуту и высоте (импортированные из Matlab), которые «сгруппированы» в выборки размером 3 х 3 градуса. Итак, матрица равна 30 х 120 (3x30=90 градусов эл., 3x120=360 аз). С каждым углом возвышения 0, 3, 6,..., 87 связано 120 значений азимута...
Однако по мере приближения к зениту вам действительно не нужны 120 ячеек (az cols): по мере продвижения вверх по el, 3 градуса az представляют собой все большую и большую часть неба. Последняя строка данных имеет только 3 ненулевых значения, которые по существу соответствуют азимутам 0–120, 120–240 и 240–360 при высоте 87–90 градусов.
Таким образом, матрица содержит все больше и больше нулей по мере перехода от строки 0 (которая составляет 0–3 градуса) к строке 29 (87–90 градусов). Данные здесь .
Я хочу построить это на полярном графике, но не знаю, как использовать контур или pcolor для построения этих нерегулярных сеток (где цвет задается значением в матрице).
Я пробовал:
epfd = np.loadtxt("data.txt")
azimuths = np.radians(np.arange(0, 360, 3))
els = np.arange(0,90,3)
r, theta = np.meshgrid(els, azimuths)
fig, ax = plt.subplots(subplot_kw=dict(projection='polar'))
im = ax.contourf(theta, r,epfd.T,norm=mpl.colors.LogNorm())
ax.set_theta_direction(-1)
ax.set_theta_offset(np.pi / 2.0)
ax.invert_yaxis()
Но это приводит к
Спасибо за ваши идеи.






Вот как я обычно это делаю:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
data = np.loadtxt(r"C:\Users\serge.degossondevare\Downloads\data.txt")
def create_custom_bins(data):
azimuths = []
values = []
# Offset elevations such that bin edge is at tick for that value
elevations = np.linspace(0, 90, data.shape[0]) + 90/data.shape[0]/2
num_values = np.count_nonzero(data, axis=1) # Number of nonzero values in each row, array
for i in range(data.shape[0]):
# Create rows of bins that map to the shape of the elevation data - 30 rows
# Each row has a number of non-zero entries... These are how many bins we want
# To align the az bins on the left edge we need to subtract half the binsize
az_bins = np.linspace(0, 360, num_values[i], endpoint=False) + 360/num_values[i]/2
azimuths.append(az_bins)
values.append(data[i, :num_values[i]])
return elevations, azimuths, values
elevations, azimuths, values = create_custom_bins(data)
min = values[values>0].min()
max = values.max()
fig, ax = plt.subplots(subplot_kw=dict(projection='polar'))
for el, az, val in zip(elevations, azimuths, values):
theta, r = np.meshgrid(np.radians(az), [el, el + 3])
val = np.tile(val, (2, 1))
# Important to set min max since each row will have a different scale based on that rows min/max.
im = ax.pcolormesh(theta, r, val, norm=mpl.colors.LogNorm(vmin=min,vmax=max), shading='auto')
ax.set_theta_direction(-1)
ax.set_theta_offset(np.pi / 2.0)
ax.invert_yaxis()
cbar = fig.colorbar(im, ax=ax, orientation='vertical')
cbar.set_label('PFD [W/m2/s/Hz]')
plt.show()
который дает
Если вам нужен более плавный график, вариант предлагаемого решения
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from scipy.interpolate import griddata
data = np.loadtxt(r"C:\Users\serge.degossondevare\Downloads\data.txt")
def create_custom_bins(data):
azimuths = []
values = []
elevations = np.linspace(0, 90, data.shape[0]) + 90/data.shape[0]/2
num_values = np.count_nonzero(data, axis=1) # Number of nonzero values in each row, array
for i in range(data.shape[0]):
# Create rows of bins that map to the shape of the elevation data - 30 rows
# Each row has a number of non-zero entries... These are how many bins we want
# To align the az bins on the left edge we need to subtract half the binsize
az_bins = np.linspace(0, 360, num_values[i], endpoint=False) + 360/num_values[i]/2
azimuths.append(az_bins)
values.append(data[i, :num_values[i]])
return elevations, azimuths, values
elevations, azimuths, values = create_custom_bins(data)
azimuth_flat = np.hstack([az for az in azimuths])
elevation_flat = np.hstack([[el]*len(az) for el, az in zip(elevations, azimuths)])
value_flat = np.hstack(values)
azimuth_pad = np.hstack([azimuth_flat, azimuth_flat + 360, azimuth_flat - 360])
elevation_pad = np.hstack([elevation_flat, elevation_flat, elevation_flat])
value_pad = np.hstack([value_flat, value_flat, value_flat])
azimuth_fine = np.linspace(0, 360, 361)
elevation_fine = np.linspace(0, 90, 180)
azimuth_grid, elevation_grid = np.meshgrid(azimuth_fine, elevation_fine)
value_grid = griddata((azimuth_pad, elevation_pad), value_pad, (azimuth_grid, elevation_grid), method='linear')
fig, ax = plt.subplots(subplot_kw=dict(projection='polar'))
theta = np.radians(azimuth_grid)
r = elevation_grid
im = ax.pcolormesh(theta, r, value_grid, norm=mpl.colors.LogNorm(), shading='auto', cmap='viridis')
ax.set_theta_direction(-1)
ax.set_theta_offset(np.pi / 2.0)
ax.invert_yaxis()
cbar = fig.colorbar(im, ax=ax, orientation='vertical')
cbar.set_label('PFD [W/m2/s/Hz]')
plt.show()
Кроме того, очень важно указать мин/макс (логарифмический) масштаб, поскольку каждая строка (набор высот) отображается независимо от другой: im = ax.pcolormesh(theta, r, val,normal=mpl.colors.LogNorm( vmin=1e-32,vmax=5e-28), shading='auto') Без минимального/максимального значения каждая строка имеет свой собственный масштаб, и цветовая полоса становится бессмысленной!
@serge_de_gosson_de_varennes Итак, я вижу, что вы сделали, вытащив данные в зависимости от высоты: уменьшив на 4 количество записей для каждого приращения высоты от 0 до> 87 ... Однако мои данные этого не делают. , поэтому я использовал np.count_nonzero(data, axis=1), чтобы получить массив счетчиков для каждой высоты, и использовал его. (вместо data.shape[1] - 4*(i)) ... Это гарантирует, что я использую все точки данных для каждой отметки. Но все остальное работает отлично! Спасибо еще раз.