我发现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?
最佳答案
文件名长度问题很容易处理:你创建 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
,那么在您进行更改以复制数据之前,数据可能会被覆盖出来。
方法二:保存数据的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
然后,您需要使用 f2py -m _genfromtxt -h _genfromtxt.pyf genfromtxt.f90
生成“签名文件”(假设 genfromtxt.f90
是您的文件) Fortran 代码)。然后,您修改“用户例程 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代码设置并运行回调。我怀疑你关心这个应用程序的线程安全......
关于python - 如何用 f2py 编写 genfromtxt?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30983837/