将数组从 .npy 文件读入 Fortran 90

Thu*_*ash 5 python arrays fortran

我使用 Python 以二维数组(例如“X”)的形式生成一些初始数据,然后使用 Fortran 对它们进行一些计算。最初,当数组大小约为 10,000 x 10,000 时,np.savetxt 在速度方面表现良好。但是一旦我开始增加数组的维度,savetxt 的速度就会显着减慢。所以我尝试了 np.save ,这导致保存速度更快,但文件以 .npy 格式保存。我将如何在 Fortran 中读取这样的文件来重建原始数组?据我所知,二进制通常会带来最低的空间消耗和最快的速度。

在 Fortran 90 中,

open(10,file='/home/X.npy')

read(10,*)X
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:Fortran 运行时错误:列表输入的第 1 项中的实数错误

编辑:在Python中我这样做,

import numpy as np 
x = np.linspace(-50,50,10)
y = np.linspace(-50,50,10) 
X,Y = np.meshgrid(x,y) 
np.save('X',X) 
Run Code Online (Sandbox Code Playgroud)

然后在 Fortran 中我这样做,

program read_unformatted_binary_from_python
    use iso_c_binding
    implicit none

    integer, parameter :: DPR = selected_real_kind(p=15)
    character(len=*), parameter :: filename = 'X.npy'

    integer, parameter :: N = 10
    real(DPR), allocatable, dimension(:, :) :: X

    allocate(X(N, N))


    open(40, file=filename, status='old', access='stream', form='unformatted')
    read(40) X
    close(40)

    write(*,*) X

end program read_unformatted_binary_from_python
Run Code Online (Sandbox Code Playgroud)

输出以 1.8758506894003703E-309 1.1711999948023422E+171 5.2274167985502976E-037 8.4474009688929314E+252 2.6514123210345660E+180 开头9.9215260506210473E+247 2.1620996769994603E+233 7.5805790251297605E-096 3.4756671925988047E-152 6.5549091408576423E-260 -50.0 00000000000000 -38.888888888888886 -27.777777777777779等等

使用 .bin 格式时不会出现此错误,仅在使用 np.save 时会出现此错误,从而生成 npy。

CAZ*_*AZT 3

Vladimir F 是正确的,您希望在 Fortran 中“流”访问“原始二进制”文件。这是一个 MWE:

Python

import numpy as np
A = np.random.rand(10000, 10000)
print(A.sum())
A.tofile('data.bin')
Run Code Online (Sandbox Code Playgroud)

福特兰语言

program read_unformatted_binary_from_python
    use iso_c_binding
    implicit none

    integer, parameter :: DPR = selected_real_kind(p=15)
    character(len=*), parameter :: filename = 'data.bin'

    integer, parameter :: N = 10000
    real(DPR), allocatable, dimension(:, :) :: dat

    allocate(dat(N, N))

    open(40, file=filename, status='old', access='stream', form='unformatted')
    read(40) dat
    close(40)

    write(*,*) sum(dat)

end program read_unformatted_binary_from_python
Run Code Online (Sandbox Code Playgroud)

我的 Fortran 示例可能比必要的要长一些,因为我使用许多不同的系统和编译器套件,并且也讨厌大型静态数组(毕竟我是 Fortran 用户)。

我在 MacBook Pro 上使用 Homebrew GCC 6.3.0_1 中的 Python 2.7.x、Numpy 13.x 和 gfortran 快速编写了此代码,但这应该适用于所有系统。

更新:需要特别注意此处的数组形状和大小。如果dat分配的大小大于文件中的大小,则流read应尝试填充整个数组,命中EOF符号并发出错误。在 Python 中,该np.fromfile()方法将读取并EOF返回一个具有适当长度的一维数组,即使A最初是多维的。这是因为原始二进制文件没有元数据,只是 RAM 中的连续字节字符串。

结果,Python 中的以下几行生成相同的文件:

A = np.random.rand(10000, 10000)
A.tofile('file.whatever')
A.ravel().tofile('file.whatever')
A.reshape((100, 1000, 1000)).tofile('file.whatever')
Run Code Online (Sandbox Code Playgroud)

该文件可以被读入并重塑为:

B = np.fromfile('file.whatever').reshape(A.shape)
B = np.fromfile('file.whatever').reshape((100, 1000, 100, 10))
# or something like
B = np.fromfile('file.whatever') # just a 1D array
B.resize(A.shape)  # resized in-place
Run Code Online (Sandbox Code Playgroud)

在 Fortran 中,使用流访问很容易读取整个原始文件,而无需提前知道其大小,尽管您显然需要某种用户输入来重塑数据:

program read_unformatted_binary_from_python
    use iso_c_binding
    implicit none

    integer, parameter :: DPR = selected_real_kind(p=15)
    character(len=*), parameter :: filename = 'data.bin'
    integer :: N = 10000, bytes, reals, M
    real(DPR), allocatable :: A(:,:), D(:, :), zeros(:)
    real(DPR), allocatable, target :: B(:)
    real(DPR), pointer :: C(:, :)

    allocate(A(N, N))

    open(40, file=filename, status='old', access='stream', form='unformatted')

    read(40) A
    write(*,*) 'sum of A', sum(A)

    inquire(unit=40, size=bytes)
    reals = bytes/8
    allocate(B(reals))

    read(40, pos=1) B
    write(*,*) 'sum of B', sum(B)

    ! now reshape B in-place assuming the user wants the first dimension 
    ! (which would be the outer dimension in Python) to be length 100
    N = 100
    if(mod(reals, N) == 0) then
         M = reals/N
         call C_F_POINTER (C_LOC(B), C, [N, M])
         write(*, *) 'sum of C', sum(C)
         write(*, *) 'shape of C', shape(C)
    else
         write(*,*) 'file size is not divisible by N!, did not point C to B'
    end if

    ! now reshape B out-of-place with first dimension length 9900, and
    ! pad the result so that there is no size mismatch error  
    N = 9900
    M = reals/N
    if(mod(reals, N) > 0) M=M+1

    allocate(D(N, M))
    allocate(zeros(N), source=real(0.0, DPR))
    D = reshape(B, [N, M], pad=zeros)

    write(*,*) 'sum of D', sum(D)
    write(*,*) 'shape of D', shape(D)

    ! obviously you can also work with shape(A) in fortran the same way you
    ! would use A.shape() in Python, if you already knew that A was the
    ! correct shape of the data
    deallocate(D)
    allocate(D, mold=A)
    D = reshape(B, shape(A))
    write(*,*) 'sum of D', sum(D)
    write(*,*) 'shape of D', shape(D)

    ! or, just directly read it in, skipping the whole reshape B part
    read(40, pos=1) D
    write(*,*) 'sum of D', sum(D)

    close(40)

end program read_unformatted_binary_from_python
Run Code Online (Sandbox Code Playgroud)

  • 显然还有一个 numpy 库可在 Fortran 中使用来读取 .npy 文件。请参阅本说明书的底部,其中包含 libnpy 的下载链接:http://scipy-cookbook.readthedocs.io/items/InputOutput.html (2认同)
  • 请参阅上面的更新,但是 TL;DR:原始二进制文件没有元数据,因此 `fromfile()` 只是将数据读取到连续的一维数组中,但您始终可以使用 `reshape()` 或 `resize()`大批 (2认同)