2015-06-22 1 views
2

Я обнаружил, что функция genfromtxt от numpy в python очень медленная.Как написать genfromtxt с f2py?

Поэтому я решил обернуть модуль с 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 падает.

Это называется в питоне, как:

import Fmodules as modules 
w_map=modules.genfromtxt(filename,100, 50) 

Как я могу это сделать динамически без прохождения nx, ny в качестве параметров, ни фиксаций filename длины до 100?

+0

Читатель Pandas csv должен быть немного быстрее, чем 'numpy'. – hpaulj

ответ

0

Я думаю, вы можете просто использовать:

open(5, file=trim(filename)) 

иметь дело с именами файлов короче, чем длина filename. (Т.е. вы могли бы сделать filename гораздо дольше, чем это должно быть, и просто обрезать его здесь)

Я не знаю, какой-либо хороший чистый способ удалить необходимость передачи nx и ny Фортрана подпрограммой. Возможно, если вы сможете определить размер и форму файла данных программно (например, прочитайте первую строку, чтобы найти nx, вызовите некоторую функцию или получите первый проход по файлу, чтобы определить количество строк в файле), тогда вы могли бы allocate ваш массив a после нахождения этих значений. Это замедлит все вниз, хотя, это может быть контрпродуктивным

+0

Спасибо, в деле я могу выделить 'a', посчитав количество строк и строк в файле. Проблема в том, как взаимодействовать с python. 'f2py' должен знать размер до руки. – ilciavo

+0

можете ли вы сделать это с помощью python (или вызывая что-то вроде 'wc')? – tom

+0

Да, я мог бы это сделать, но мне все равно нужно пройти 'nx' и' ny'. Функция будет работать только для n-й матрицы, а не для более общих данных. – ilciavo

3

Вопрос Длина имени файла легко расправился с: вы делаете имя файла: character(len_filename):: filename, имеют len_filename в качестве параметра фортрановской функции и использовать 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 

Затем нужно произвести «подписи файла» с f2py -m _genfromtxt -h _genfromtxt.pyf genfromtxt.f90 (при условии, genfromtxt.f90 это файл с Fortran кода). Затем измените «подпрограммы пользователя блок», чтобы уточнить подпись обратного вызова:

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 

(т.е. вы указываете размеры). Остальная часть файла остается неизменной. Compile с 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 не может быть запущен между установленным глобальным и выполняемым обратным вызовом. Я сомневаюсь, что вы заботитесь о безопасности потоков для этого приложения, хотя ...