R cpp: ускорение чтения текстового файла с большой матрицей (формат ESRI ASCii) - PullRequest
1 голос
/ 04 апреля 2020

Обычно я хочу выполнить функцию самоопределения для пакета растровых файлов (~ 365 слоев, с форматом ESRI ASCii) для каждой сетки, это займет много времени и большой памяти с растровым пакетом, поэтому я думаю о используя R cpp для чтения каждого текстового файла, а затем выполните расчет и экспортируйте окончательную сетку.

Я написал функцию для чтения текстового файла, но это занимает ~ 15 раз больше, чем readGDAL и базовая функция чтения в растровом пакете, большинство времени в основном тратится на разделение ostream для каждой строки и столбца, я не знаком с C ++, мне интересно, кто-нибудь может помочь мне улучшить этот скрипт.

Файл ESRI ASCii выглядит следующим образом , а код теста приведен ниже.

test.as c

"
ncols 480
nrows 450
xllcorner 378923
yllcorner 4072345
cellsize 30
nodata_value -32768
43 2 45 7 3 56 2 5 23 65 34 6 32 54 57 34 2 2 54 6 
35 45 65 34 2 6 78 4 2 6 89 3 2 7 45 23 5 8 4 1 62 ...
"

тестовый код:

#include <Rcpp.h>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <math.h>
#include <algorithm>

using namespace std;
using namespace Rcpp;


// [[Rcpp::export]]
NumericMatrix cppread_asc(std::string path, const bool maskNA=true, const bool fast=true, const bool debug=false) {
  int ncols;
  int nrows;
  float cellsize;
  float xllcorner;
  float yllcorner;
  float nodata_value;
  std::string tmpstr;

  std::ifstream ascfile(path.c_str()); 
  if(ascfile.fail()){
    perror("Error, file does not exist");
  }else{
    // read the georeferencing data and metadata
    ascfile 
    >> tmpstr >> ncols
    >> tmpstr >> nrows
    >> tmpstr >> xllcorner 
    >> tmpstr >> yllcorner
    >> tmpstr >> cellsize 
    >> tmpstr >> nodata_value;

    Rcout<< "nrows:" << nrows << ", ncols;" << ncols << ";\n";
    NumericMatrix output(nrows,ncols);
    //Rcout<< "fast:" << fast <<"\n";
    if(fast){
      std::string tmpline;
      std::getline(ascfile,tmpline); //the first line is empty
      for(int row=0; row<nrows; ++row){
        if(debug && row<2){
          Rcout<< "start to getline\n";
        }
        std::getline(ascfile,tmpline);
        std::stringstream tmpss(tmpline);
        if(debug && row<2){
          Rcout << "tmpline:" << tmpline <<"\n";
          Rcout<< "start to seperate for column\n";
        }
        for(int col=0; col<ncols; ++col){
          tmpss >> output(row,col);
          if(debug && col<5 && row<5){
            Rcout<< "nrow of" << row <<", column of:" << col <<" value: " << output(row,col) << ";\n";
          }

          if(maskNA){
            if(debug && row<2 && col<2){
              Rcout<< "start to mask NA\n";
            }
            if(output(row,col)<=nodata_value){
              output(row,col)=NA_REAL;
            }                
          }
        }
      }
    }else{
      for(int row=0; row<nrows; ++row){
        for(int col=0; col<ncols; ++col){
          ascfile >> output(row,col); 
          if(maskNA){
            if(output(row,col)<=nodata_value){
              output(row,col)=NA_REAL;
            }                
          }
        }
      }      
    }
    ascfile.close();
    return(output);
  }
}


/*** R
require(raster)
require(microbenchmark)
r=raster(nrows=600, ncols=400, xmn=-10, xmx=10, ymn=-20, ymx=10, vals=0.001*1:(600*400))
writeRaster(r,filename= "test.asc", format = "ascii", overwrite=TRUE)
microbenchmark(
    new1<<-cppread_asc("test.asc",maskNA=TRUE,fast=TRUE,debug=TRUE),
    new2<<-cppread_asc("test.asc",maskNA=TRUE,fast=FALSE,debug=TRUE),
    new3<<-matrix(getValues(raster("test.asc")),nrow=600,ncol=400,byrow=TRUE),
    print(identical(new1,new2)),print(identical(new2,new3)),times=1
)
  */
#benchmark results:
#                     min          lq        mean      median          uq
#Rcpp_read  : 9127185.187 9127185.187 9127185.187 9127185.187 9127185.187
#Rcpp_read  : 9097437.947 9097437.947 9097437.947 9097437.947 9097437.947
#raster_read:  409822.642  409822.642  409822.642  409822.642  409822.642

1 Ответ

3 голосов
/ 04 апреля 2020

Ниже приведена обновленная версия с базовым вводом / выводом в стиле C, которая работает в десять раз быстрее, чем в режиме ostream. Однако есть еще две проблемы:

1: когда я использую fscanf, чтобы получить размерность nrows, ncols из заголовков, целочисленное значение nrows, ncols менялось несколько раз, я не знаю, мне нужно ввести два новых keepnrows, keepncols, чтобы сразу после fscanf сохранить значение, я думаю это должно быть исправлено, но я не знаю, как это исправить.

PS: обновлено 6 апреля. Я нашел, что приводит к ошибке, потому что значение tmpvalue не инициализировано, см. код ниже. Но мне интересно ПОЧЕМУ?

// float tmpvalue;

float tmpvalue = -9998.0;

NCOLS: 400

NROWS: 600

XLLCORNER: -10.000000

YLLCORNER: -20.000000

CELLSIZE: 0,050000

NODATA_value: -9999.000000

nrows: 1635147585, NLS; 141; 141; 141; 141; 1 1023 *

NROWS: 1635147585

NCOLS: 1413563471

keepnrows: 600

keepncols: 400

2: о том, как исправить 'fscanf (fp, "% 10.6f", & tmpvalue) ', с предупреждением: неизвестный символ типа преобразования'. ' в формате [-Wformat]. в настоящее время я получаю значение с плавающей точкой, как 0,001000000584, в то время как оно должно быть 0,001.

#include <Rcpp.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <math.h>
#include <algorithm>

using namespace std;
using namespace Rcpp;


// [[Rcpp::export]]
NumericMatrix cppread_asc(std::string path, const bool maskNA=true, const bool fast=true, const bool debug=false) {
  int ncols, keepncols;
  int nrows, keepnrows;
  float cellsize;
  float xllcorner;
  float yllcorner;
  float nodata_value;
//float tmpvalue; //This will result in serious erros that the ncols,nrows change always, I do not know why?
  float tmpvalue=-9998.0;
  std::string tmpstr;
  char tmpstring;

  if(fast){
    FILE *fp = fopen(path.c_str(), "r");
    if (fp == NULL) {
      perror("error opening file");
    }
    fscanf(fp,"%s",&tmpstring);
    if(debug) printf("%s: ",&tmpstring);
    fscanf(fp,"%i",&ncols);
    keepncols=ncols;
    if(debug) printf("%i\n",ncols);

    fscanf(fp,"%s",&tmpstring);
    if(debug) printf("%s: ",&tmpstring);
    fscanf(fp,"%i",&nrows);
    keepnrows=nrows;
    if(debug) printf("%i\n",nrows);

    fscanf(fp,"%s",&tmpstring);
    if(debug) printf("%s: ",&tmpstring);
    fscanf(fp,"%f",&xllcorner);
    if(debug) printf("%f\n",xllcorner);

    fscanf(fp,"%s",&tmpstring);
    if(debug) printf("%s: ",&tmpstring);
    fscanf(fp,"%f",&yllcorner);
    if(debug) printf("%f\n",yllcorner);

    fscanf(fp,"%s",&tmpstring);
    if(debug) printf("%s: ",&tmpstring);
    fscanf(fp,"%f",&cellsize);
    if(debug) printf("%f\n",cellsize);

    fscanf(fp,"%s",&tmpstring);
    if(debug) printf("%s: ",&tmpstring);
    fscanf(fp,"%f",&nodata_value);
    if(debug) printf("%f\n",nodata_value);
    if(debug){
      Rcout<< "nrows:" << nrows << ", ncols;" << ncols << ";\n";
    }
    printf("NROWS: %i\n",nrows); printf("NCOLS: %i\n",ncols);
    printf("keepnrows: %i\n",keepnrows); printf("keepncols: %i\n",keepncols);
    nrows=keepnrows;
    ncols=keepncols;   
    NumericMatrix output(nrows,ncols);

    for(int row=0; row<nrows; ++row){
      for(int col=0; col<ncols; ++col){
        // fscanf(fp,"%0.20f", &tmpvalue);
        fscanf(fp,"%10f", &tmpvalue);
        output(row,col)=tmpvalue;
        if(debug && col<3 && row<3){
          Rcout<< "row: " << row <<", column: " << col <<" value: " << output(row,col) <<"tmpvalue: " <<tmpvalue<< ";\n";
        }
        if(maskNA){
          if(debug && row<2 && col<2){
            Rcout<< "start to mask NA\n";
          }
          if(output(row,col)<=nodata_value){
            output(row,col)=NA_REAL;
          }                
        }
      }
    }
    fclose(fp);
    return(output);
  }else{
    std::ifstream ascfile(path.c_str()); 
    if(ascfile.fail()){
      perror("Error, file does not exist");
    }else{
      // read the georeferencing data and metadata
      ascfile 
      >> tmpstr >> ncols
      >> tmpstr >> nrows
      >> tmpstr >> xllcorner 
      >> tmpstr >> yllcorner
      >> tmpstr >> cellsize 
      >> tmpstr >> nodata_value;

      // if(debug){
      //  Rcout<< "nrows:" << nrows << ", ncols;" << ncols << ";\n";
      //  }
      NumericMatrix output(nrows,ncols);
      for(int row=0; row<nrows; ++row){
        for(int col=0; col<ncols; ++col){
          ascfile >> output(row,col); 
          if(maskNA){
            if(output(row,col)<=nodata_value){
              output(row,col)=NA_REAL;
            }                
          }
        }
      } 
      ascfile.close();
      return(output);
    }
  }
}


/*** R
require(raster)
  require(microbenchmark)
  r=raster(nrows=600, ncols=400, xmn=-10, xmx=10, ymn=-20, ymx=10, vals=0.001*1:(600*400))
  writeRaster(r,filename= "test.asc", format = "ascii", overwrite=TRUE)
  microbenchmark(
    new1<<-cppread_asc("test.asc",maskNA=TRUE,fast=TRUE,debug=TRUE),
          new2<<-cppread_asc("test.asc",maskNA=TRUE,fast=FALSE,debug=TRUE),
                new3<<-matrix(getValues(raster("test.asc")),nrow=600,ncol=400,byrow=TRUE),
                      print(identical(new1,new2)),print(identical(new2,new3)),times=1
  )
  */
#benchmark results:
#                     min          lq        mean      median          uq
#Rcpp_read  :  329938.034  329938.034  329938.034  329938.034  329938.034
#Rcpp_read  : 9097437.947 9097437.947 9097437.947 9097437.947 9097437.947
#raster_read:  409822.642  409822.642  409822.642  409822.642  409822.642
...