我发现python中来自numpy的函数genfromtxt非常慢。
因此,我决定用f2py包装一个模块来读取我的数据。数据是一个矩阵。
subroutine genfromtxt(filename, nx, ny, a)
implicit none
character(100):: filename
real, dimension(ny,nx) :: a
integer :: row, col, ny, nx
!f2py character(100), intent(in) ::filename
!f2py integer, intent(in) :: nx
!f2py integer, intent(in) :: ny
!f2py real, intent(out), dimension(nx,ny) :: a
!Opening file
open(5, file=filename)
!read data again
do row = 1, ny
read(5,*) (a(row,col), col =1,nx) !reading line by line
end do
close (5)
end subroutine genfromtxt文件名的长度固定为100,因为如果f2py不能处理动态大小。代码适用于小于100的大小,否则python中的代码会崩溃。
在python中,这被称为:
import Fmodules as modules
w_map=modules.genfromtxt(filename,100, 50)如何在不传递nx、ny作为参数,也不将filename长度固定为100的情况下动态完成此操作?
发布于 2015-06-25 15:21:15
文件名长度问题很容易处理:设置filename:character(len_filename):: filename,将len_filename作为fortran函数的参数,并使用f2py intent(hide)将其隐藏在您的Python接口中(参见下面的代码)。
您不希望必须传入nx和ny的问题更为复杂,本质上是如何从f2py返回可分配数组的问题。我可以看到两种方法,这两种方法都需要一个(很小很轻)的Python包装器来为您提供一个很好的接口。
我实际上还没有解决如何读取csv文件的问题-我只是生成并返回了一些虚拟数据。我假设你对此有一个很好的实现。
方法1:模块级可分配数组
这似乎是一个相当标准的方法--我在least one newsgroup post上找到了推荐的方法。实际上,您有一个可分配的全局变量,将其初始化为正确的大小,并写入其中。
module mod
real, allocatable, dimension(:,:) :: genfromtxt_output
contains
subroutine genfromtxt_v1(filename, len_filename)
implicit none
character(len_filename), intent(in):: filename
integer, intent(in) :: len_filename
!f2py intent(hide) :: len_filename
integer :: row, col
! for the sake of a quick demo, assume 5*6
! and make it all 2
if (allocated(genfromtxt_output)) deallocate(genfromtxt_output)
allocate(genfromtxt_output(1:5,1:6))
do row = 1,5
do col = 1,6
genfromtxt_output(row,col) = 2
end do
end do
end subroutine genfromtxt_v1
end module mod然后,简短的Python包装器如下所示:
import _genfromtxt
def genfromtxt_v1(filename):
_genfromtxt.mod.genfromtxt_v1(filename)
return _genfromtxt.mod.genfromtxt_output.copy()
# copy is needed, otherwise subsequent calls overwrite the data这样做的主要问题是它不是线程安全的:如果两个Python线程在非常相似的时间调用genfromtxt_v1,那么在您进行复制之前,数据可能会被覆盖。
方法2:保存数据的Python回调函数
这稍微有点复杂,而且是我自己发明的可怕的黑客。将数组传递给回调函数,然后由回调函数保存数组。Fortran代码如下所示:
subroutine genfromtxt_v2(filename,len_filename,callable)
implicit none
character(len_filename), intent(in):: filename
integer, intent(in) :: len_filename
!f2py intent(hide) :: len_filename
external callable
real, allocatable, dimension(:,:) :: result
integer :: row, col
integer :: rows,cols
! for the sake of a quick demo, assume 5*6
! and make it all 2
rows = 5
cols = 6
allocate(result(1:rows,1:cols))
do row = 1,rows
do col = 1,cols
result(row,col) = 2
end do
end do
call callable(result,rows,cols)
deallocate(result)
end subroutine genfromtxt_v2然后,您需要使用Fortran生成“签名文件”(假设genfromtxt.f90是包含Fortran代码的文件)。然后修改"user routines block“以澄清回调的签名:
python module genfromtxt_v2__user__routines
interface genfromtxt_v2_user_interface
subroutine callable(result,rows,cols)
real, dimension(rows,cols) :: result
integer :: rows
integer :: cols
end subroutine callable
end interface genfromtxt_v2_user_interface
end python module genfromtxt_v2__user__routines(即指定尺寸)。文件的其余部分保持不变。使用f2py -c genfromtxt.f90 _genfromtxt.pyf进行编译
小的Python包装器是
import _genfromtxt
def genfromtxt_v2(filename):
class SaveArrayCallable(object):
def __call__(self,array):
self.array = array.copy() # needed to avoid data corruption
f = SaveArrayCallable()
_genfromtxt.genfromtxt_v2(filename,f)
return f.array我认为这应该是线程安全的:虽然我认为Python回调函数是作为全局变量实现的,但是默认的f2py不会释放GIL,所以在设置的全局变量和运行的回调函数之间不能运行Python代码。我怀疑你是否关心这个应用程序的线程安全性……
发布于 2015-06-23 00:16:55
我认为你可以直接使用:
open(5, file=trim(filename))处理比filename长度短的文件名。(例如,您可以使filename比需要的更长,并在此处对其进行修剪)
我不知道有什么好的、干净的方法来消除将nx和ny传递给Fortran子例程的需要。也许,如果您可以通过编程确定数据文件的大小和形状(例如,读取第一行以查找nx,调用某个函数或第一次遍历文件以确定文件中的行数),那么您可以在找到这些值后对a数组执行allocate操作。然而,这会减慢一切,因此可能会适得其反
https://stackoverflow.com/questions/30983837
复制相似问题