python - 如何用 f2py 编写 genfromtxt?

标签 python numpy fortran95 f2py genfromtxt

我发现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)

如何动态执行此操作,而不将 nxny 作为参数传递,也不将 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/

相关文章:

Fortran 自然对数误差

python - 在 linux SUSE 或 RedHat 上,如何加载 Python 2.7

python - 在python中连接多个数组

python - 结合 plt.plot(x,y) 与 plt.boxplot()

python - 在 python 中追加 2d 时间序列数组

python - 具有强制数据类型和维度的输入 numpy 数组的文档字符串格式

python - pip 卸载时出现权限错误

python - CPLEX Python API 性能开销?

Fortran 中的随机数组/矩阵初始化

algorithm - 需要有关二进制搜索算法错误的帮助