Python 将径向强度的一维numpy阵列旋转为空间强度的二维阵列
我有一个numpy阵列,在一个均匀的圆中充满不同半径的强度读数(例如,这是一个用于原恒星形成模型的1D辐射传输项目:虽然存在更好的模型,但我的主管让我有制作一个模型的经验,以便我了解其他模型的工作方式) 我想把那个1d数组“旋转”成一个圆圈,形成一个强度的2D数组,然后可以用imshow(或者,稍加努力,aplpy)显示出来。最终的数组需要是二维的,投影需要是笛卡尔坐标,而不是极坐标 我可以用嵌套for循环来实现,也可以用查找表来实现,但我觉得一定有一种简洁的方法可以用numpy或其他什么东西来实现 有什么想法吗 编辑: 我不得不回去重新创建我以前的for循环和if语句(坦率地说是可怕的)。如果我真的尝试过的话,我可能会通过浓缩一些东西来摆脱一个循环和一个If语句。但是,目的不是让它与for循环一起工作,而是查看是否有一种内置的方法来旋转阵列。 impB是一个数组,它与我之前说过的略有不同。它实际上只是一个半径列表,可以检测到粒子。然后我把它们放入半径容器中,得到每个半径的强度(或者频率,如果你喜欢的话)。R是以无量纲方式运行模型时半径的比例因子。iRes是一个分辨率比例因子,本质上是我希望对放射状容器进行采样的频率。其他一切都应该清楚Python 将径向强度的一维numpy阵列旋转为空间强度的二维阵列,python,arrays,numpy,rotation,Python,Arrays,Numpy,Rotation,我有一个numpy阵列,在一个均匀的圆中充满不同半径的强度读数(例如,这是一个用于原恒星形成模型的1D辐射传输项目:虽然存在更好的模型,但我的主管让我有制作一个模型的经验,以便我了解其他模型的工作方式) 我想把那个1d数组“旋转”成一个圆圈,形成一个强度的2D数组,然后可以用imshow(或者,稍加努力,aplpy)显示出来。最终的数组需要是二维的,投影需要是笛卡尔坐标,而不是极坐标 我可以用嵌套for循环来实现,也可以用查找表来实现,但我觉得一定有一种简洁的方法可以用numpy或其他什么东西来
radJ = np.ndarray(shape=(2*iRes, 2*iRes)) # Create array of 2xRadius square
for i in range(iRes):
n = len(impB[np.where(impB[:] < ((i+1.) * (R / iRes)))]) # Count number of things within this radius +1
m = len(impB[np.where(impB[:] <= ((i) * (R / iRes)))]) # Count number of things in this radius
a = (((i + 1) * (R / iRes))**2 - ((i) * (R / iRes))**2) * math.pi # A normalisation factor based on area.....dont ask
for x in range(iRes):
for y in range(iRes):
if (x**2 + y**2) < (i * iRes)**2:
if (x**2 + y**2) >= (i * iRes)**2: # Checks for radius, and puts in cartesian space
radJ[x+iRes,y+iRes] = (n-m) / a # Put in actual intensity bins
radJ[x+iRes,-y+iRes] = (n-m) / a
radJ[-x+iRes,y+iRes] = (n-m) / a
radJ[-x+iRes,-y+iRes] = (n-m) / a
radJ=np.ndarray(形状=(2*iRes,2*iRes))#创建2xRadius正方形数组
对于范围内的i(iRes):
n=len(impB[np.where(impB[:]<((i+1.)*(R/iRes))))#计算该半径内的事物数量+1
m=len(impB[np.where(impB[:]=(i*iRes)**2:#检查半径,并放入笛卡尔空间
radJ[x+iRes,y+iRes]=(n-m)/a#放入实际强度箱
radJ[x+iRes,-y+iRes]=(n-m)/a
radJ[-x+iRes,y+iRes]=(n-m)/a
radJ[-x+iRes,-y+iRes]=(n-m)/a
嵌套循环是一种简单的方法。如果ri\u data\u r
和y
分别包含半径值(与中间像素的差值)和旋转数组,我建议:
from scipy import interpolate
import numpy as np
y = np.random.rand(100)
ri_data_r = np.linspace(-len(y)/2,len(y)/2,len(y))
interpol_index = interpolate.interp1d(ri_data_r, y)
xv = np.arange(-1, 1, 0.01) # adjust your matrix values here
X, Y = np.meshgrid(xv, xv)
profilegrid = np.ones(X.shape, float)
for i, x in enumerate(X[0, :]):
for k, y in enumerate(Y[:, 0]):
current_radius = np.sqrt(x ** 2 + y ** 2)
profilegrid[i, k] = interpol_index(current_radius)
print(profilegrid)
这将为您提供所需的内容。您只需在数组中计算一个对称数组
ri\u data\u r
,该数组与您的数据数组具有相同的长度,并包含实际数据与数组中间的距离。该代码会自动执行此操作。我在一篇文章中偶然发现了这个问题不同的上下文,我希望我理解正确。这里有另外两种方法。第一种方法使用所需顺序的插值(这里我们使用顺序=0最近邻)。与第二种方法相比,这种方法速度较慢但更精确,需要的内存更少
第二个不使用插值,因此速度更快,但精度更低,并且需要更多的内存,因为它存储每个包含一个倾斜的2D数组,直到最后,在那里它们用np.nanmean()
平均
这两种解决方案之间的差异源于处理倾斜重叠最多的最终图像中心的问题,即第一种方法只是在每个倾斜超出原始范围的情况下添加值。这是通过在每个步骤中将矩阵剪裁为全局_min
和全局_max
(参考代码)。第二种方法通过取它们重叠处倾斜的平均值来解决,这迫使我们使用np.nan
为了理解情节标题,请阅读使用和健全性检查部分的示例
解决方案1:
用法示例:
健全性检查:
这些只是简单的检查,绝不涵盖所有可能的边缘情况。根据您的使用情况,您可能希望扩展检查并调整方法
# I. Sanity check
# Assuming n <= πr and v = np.ones(2r)
# Then sum(inplace) should be approx. equal to (n * (2πr - n)) / π
# which is an area that should be covered by the tilts
desired_area = (n * (2 * np.pi * r - n)) / np.pi
covered_area = np.sum(inplace)
covered_frac = covered_area / desired_area
print(f'This method covered {covered_frac * 100:.2f}% '
'of the area which should be covered in total.')
# II. Sanity check
# Assuming n <= πr and v = np.ones(2r)
# Then a circle M with radius m <= r should be the largest circle which
# is fully covered by the vectors. I.e. its mean should be no less than 1.
# If n = πr then m = r.
# m = n / π
m = int(n / np.pi)
# Code for circular mask not included
mask = create_circular_mask(2*r, 2*r, center=None, radius=m)
m_area = np.mean(inplace[mask])
print(f'Full radius r={r}, radius m={m}, mean(M)={m_area:.4f}.')
如果你已经实现了它,请分享你的循环代码?我刚刚用一个工作示例更新了我的代码。
import numpy as np
def rotate_vector(vector, deg_angle):
assert vector.ndim == 1, 'Pass only 1D vectors, e.g. use array.ravel()'
square = np.ones([vector.size, vector.size]) * np.nan
radius = vector.size // 2
r_values = np.linspace(-radius, radius, vector.size)
rad_angle = np.deg2rad(deg_angle)
ind_x = np.round(np.cos(rad_angle) * r_values + vector.size/2).astype(np.int)
ind_y = np.round(np.sin(rad_angle) * r_values + vector.size/2).astype(np.int)
ind_x = np.clip(ind_x, 0, vector.size-1)
ind_y = np.clip(ind_y, 0, vector.size-1)
square[ind_y, ind_x] = vector
return square
def place_vectors(vectors, deg_angles):
matrices = []
for deg_angle, vector in zip(deg_angles, vectors):
matrices.append(rotate_vector(vector, deg_angle))
matrix = np.nanmean(np.array(matrices), axis=0)
return np.nan_to_num(matrix, copy=False, nan=0.0)
r = 100 # Radius of the circle, i.e. half the length of the vector
n = int(np.pi * r / 8) # Number of vectors, e.g. number of tilts in tomography
v = np.ones(2*r) # One vector, e.g. one tilt in tomography
V = np.array([v]*n) # All vectors, e.g. a sinogram in tomography
# Rotate 1D vector to a specific angle (output is 2D)
angle = 45
rotated = rotate_vector(v, angle)
# Rotate each row of a 2D array according to its angle (output is 2D)
angles = np.linspace(-90, 90, num=n, endpoint=False)
inplace = place_vectors(V, angles)
# I. Sanity check
# Assuming n <= πr and v = np.ones(2r)
# Then sum(inplace) should be approx. equal to (n * (2πr - n)) / π
# which is an area that should be covered by the tilts
desired_area = (n * (2 * np.pi * r - n)) / np.pi
covered_area = np.sum(inplace)
covered_frac = covered_area / desired_area
print(f'This method covered {covered_frac * 100:.2f}% '
'of the area which should be covered in total.')
# II. Sanity check
# Assuming n <= πr and v = np.ones(2r)
# Then a circle M with radius m <= r should be the largest circle which
# is fully covered by the vectors. I.e. its mean should be no less than 1.
# If n = πr then m = r.
# m = n / π
m = int(n / np.pi)
# Code for circular mask not included
mask = create_circular_mask(2*r, 2*r, center=None, radius=m)
m_area = np.mean(inplace[mask])
print(f'Full radius r={r}, radius m={m}, mean(M)={m_area:.4f}.')
import matplotlib.pyplot as plt
plt.figure(figsize=(16, 8))
plt.subplot(121)
rotated = np.nan_to_num(rotated) # not necessary in case of the first method
plt.title(
f'Output of rotate_vector(), angle={angle}°\n'
f'Sum is {np.sum(rotated):.2f} and should be {np.sum(v):.2f}')
plt.imshow(rotated, cmap=plt.cm.Greys_r)
plt.subplot(122)
plt.title(
f'Output of place_vectors(), r={r}, n={n}\n'
f'Covered {covered_frac * 100:.2f}% of the area which should be covered.\n'
f'Mean of the circle M is {m_area:.4f} and should be 1.0.')
plt.imshow(inplace)
circle=plt.Circle((r, r), m, color='r', fill=False)
plt.gcf().gca().add_artist(circle)
plt.gcf().gca().legend([circle], [f'Circle M (m={m})'])