我有一个python代码,它用数字导入4列txt文件 前三列是x,y,z坐标,第四列是该坐标处的密度。
下面是读取,转换为ndarray,傅里叶变换该字段,计算距原点(k =(0,0,0))和变换坐标的距离,取平均值并绘制它们的代码。 感谢pandas(用于数据分析的python库)和python FFT,加载256 ^ 3行和傅里叶变换非常快,并在几秒钟内完成。
然而,将加载的txt转换为numpy ndarray,计算平均密度(每个坐标的平均值),以及计算距离原点的距离(k =(0,0,0))需要很长时间。
我认为问题是最后的部分,但我无法弄清楚如何优化它。
我有32台核心机器的资源。
有人可以教我如何加速,使它成为一个多进程代码,或类似的东西,以便那些可以很快完成?感谢。
(如果您是宇宙学家并且需要此代码,您可以使用它,但如果可以,请与我联系。谢谢)
from __future__ import division
import numpy as np
ngridx = 128
ngridy = 128
ngridz = 128
maxK = max(ngridx,ngridy,ngridz)
#making input file
f = np.zeros((ngridx*ngridy*ngridz,4))
i = 0
for i in np.arange(len(f)):
f[i][0] = int(i/(ngridy*ngridz))
f[i][1] = int((i/ngridz))%ngridy
f[i][2] = int(i%ngridz)
f[i][3] = np.random.rand(1)
if i%1000000 ==0:
print i
#This takes forever
#end making input file
#Thanks to Mike,
a = f[:,3].reshape(ngridx,ngridy,ngridz)
avg =np.sum(f[:,3])/len(f)
a /= avg
p = np.fft.fftn(a)
#This part is much much faster than before (Original Post).
#Keeping track of corresponding wavenumbers (k_x, k_y,k_z) for each element in p
#This is just a convension on fourier transformation so you can ignore this part
kValx = np.fft.fftfreq( ngridx , (1.0 / ngridx ) )
kValy = np.fft.fftfreq( ngridy , (1.0 / ngridy ) )
kValz = np.fft.fftfreq( ngridz , (1.0 / ngridz ) )
kx = np.zeros((ngridx,ngridy,ngridz))
ky = np.zeros((ngridx,ngridy,ngridz))
kz = np.zeros((ngridx,ngridy,ngridz))
rangecolx = np.arange(ngridx)
rangecoly = np.arange(ngridy)
rangecolz = np.arange(ngridz)
for row in np.arange(ngridx):
for column in np.arange(ngridy):
for height in np.arange(ngridz):
kx[row][column][height] = (kValx[row])
ky[row][column][height] = (kValy[column])
kz[row][column][height] = (kValz[height])
if row%10 == 0:
print row
print 'wavenumber generate complete!'
#Calculating the average powerspectrum in terms of fixed K (Distance from origin to a point in fourier space)
#by taking the spherical shell of thickness 1 and averaging out the values inside it.
#I am sure that this process can be optimised somehow, but I gave up.
qlen = maxK/2 #Nyquist frequency
q = np.zeros(((qlen),4),dtype=complex)
#q is a four column array with length maxK/2.
#q[:,0] is integer wavenumber (K, which is the distance from the origin = sqrt(kx^2+ky^2+kz^2))
#q[:,1] is the sum of square of the fourier transformed value
#q[:,2] is the sum of the fourier transformed value,
#and q[:,3] is the total number of samples with K=q[:,0]
for i in np.arange(len(q)):
q[i][0] = i
i = 0
for i in np.arange(len(p)):
for r in np.arange(len(p[0])):
for s in np.arange(len(p[0,0])):
K = np.around(np.sqrt(kx[i,r,s]**2+ky[i,r,s]**2+kz[i,r,s]**2))
if K < qlen:
q[K][1]=q[K][1]+np.abs(p[i,r,s])**2
q[K][2]=q[K][2]+p[i,r,s]
q[K][3]=q[K][3]+1
if i%10 ==0:
print 'i = ',i,' !'
print q
答案 0 :(得分:5)
Numpy通常可以比普通的python快数百倍的事情,只需要很少的额外努力。您只需要知道编写代码的正确方法。仅仅列出我想到的第一件事:
普通的python在计算机应该擅长的事物上通常很慢。一个例子是索引,所以像
这样的行a[f[i,0]][f[i,1]][f[i,2]]=f[i,3]
让我非常怀疑。当你说“将加载的txt转换为numpy ndarray”需要很长时间时,这是你所指的那个吗?这不会让我感到惊讶,因为每次python看到a[f[i,0]]
时,它必须首先索引f
,确保i
是一个整数,并且你没有跑掉边缘f
;那么它必须确保f[i,0]
是一个整数,并且你没有跑掉a
的边缘。然后它必须重复这两次才能知道你要设置哪个元素。
一个改进是使用a[f[i,0],f[i,1],f[i,2]]
,因为使用这种索引时numpy会更快。
但我认为你的数据实际上是某种顺序。例如,f[i,2]
是否从0循环到256,然后f[i,1]
增加1,f [i,2]从0开始?如果是这样,你真正需要做的就是说出像
a = f[:,3].reshape(ngridx,ngridy,ngridz)
这是一个非常快速的操作,只需要几分之一毫秒。形状可能是错误的,所以你可能不得不改变参数的顺序,用转座做一些事情,但基本的想法肯定存在。您可以在the documentation中了解相关信息。
您不需要复制所有内容,当您需要复制数组(或数组的一部分)时,您应该让numpy为您执行此操作。例如,只需使用Firstdel
,而不是a[1:]
函数。或者,如果你真的需要复制数据(你不只是为了绘图),请使用:
def Firstdel(a):
return numpy.copy(a[1:])
但一般来说,你可以只使用numpy数组的“切片”,而不是复制它们。阅读它here。
循环也是臭名昭着的浪费时间。首先,while
在python中对于简单循环并不常见。而不是
while i < len(f):
# do stuff
i = i+1
你应该使用
for i in range(len(f)):
# do stuff
尽可能多地摆脱循环。要设置kx
,ky
和kz
,此代码比嵌套循环快10倍,但是缩放为N而不是N ^ 3(其中N = ngridx ngridy ngridz):
for row in range(ngridx):
kx[row,:,:] = kValx[row]
for column in range(ngridy):
ky[:,column,:] = kValy[column]
for height in range(ngridz):
kz[:,:,height] = kValz[height]
切片对于设置值也很有用,因为循环进入numpy。而不是这段代码
i = 0
while i < len(q):
q[i][0] = i
i = i + 1
只需使用
q[:,0] = range(len(q))
在这里,我只是设置q
的“切片”等于另一个数组。
循环之后的嵌套循环也可以加速,但它们会更复杂。
但你也想尽可能避免循环。这带给我们......
numpy存在的原因是将这些慢速python循环转换为快速C
代码(我们不需要知道它存在)。所以有很多函数可以做你想要做的事情,已经内置到numpy。
而不是
while i < len(f):
masstot = masstot + f[i,3]
i = i+1
你应该使用像
这样的东西masstot = np.sum(f[:,3])
读取起来比较简单,但也可能方式更快,因为numpy可以直接访问计算机内存中的数据,并且可以使用快速C
函数来查找总和而不是使用慢速python函数。 (同样,您不需要了解C
函数的任何信息;他们只会完成工作。)
而不是那个大的嵌套循环每次通过循环计算K
的值,只需使K
成为具有适当值的数组:
K = np.around(np.sqrt(kx**2+ky**2+kz**2))
K
的大小与kx
等相同。然后,您可以使用advanced indexing来设置q
的值。我就是这样做的最后一节:
# Again, we get rid of nested loops, to get a large improvement in speed and scaling
K = np.around(np.sqrt(kx**2+ky**2+kz**2)).astype(int)
for i in range(qlen):
indices = (K==i) # This will be an array of True/False values,
# which will be used for "advanced indexing" of p
q[i,0] = i
q[i,1] = sum(np.abs(p[indices])**2)
q[i,2] = sum(p[indices])
q[i,3] = sum(indices)
print q
综上所述,与你目前的问题相比,我的代码提高了35倍。
答案 1 :(得分:2)
也可以加快输入文件的创建速度:
size = ngridx*ngridy*ngridz
f = np.zeros((size,4))
a = np.arange(size)
f[:, 0] = np.floor_divide(a, ngridy*ngridz)
f[:, 1] = np.fmod(np.floor_divide(a, ngridz), ngridy)
f[:, 2] = np.fmod(a, ngridz)
f[:, 3] = np.random.rand(size)
要制作kx
,ky
和kz
,您可以使用broadcasting摆脱循环:
kx += kValx[:, np.newaxis, np.newaxis]
ky += kValy[np.newaxis, :, np.newaxis]
kz += kValz[np.newaxis, np.newaxis, :]