如何用PHPExcel改进公式计算?
How to improve formula calculation with PHPExcel?
我是 PHPExcel 的新手,我已经搜索过 Google,但在这里没有找到适合我的具体问题的信息。我发现 updating/evaluating 我的 sheet 中的公式真的 很慢。
我有一个很小的 excel 文件,我在上面做一些非常基本的目标搜索(目标搜索在 PHP 中完成,最终结果计算在 [=35= 中完成) ] sheet)。我已经让它准确地工作了,但速度绝对让我窒息。看来公式计算是罪魁祸首——公式calculations/updates如何加速?
遗憾的是,我无法 post 一份 excel 文件的副本,因为内容是我公司的商业机密,但这没什么特别的。公式中非常简单的算术运算。我能想到的唯一可能对这里产生影响的是一些单元格依赖链可能有点长(15-ish 依赖)。
从下面的输出中可以看出,我们只执行了 11 次迭代来寻找目标,总共花费了 4-5 秒。由于这将是一项 AJAX 服务,我真的需要它比那更快。
代码
这是非常快速和肮脏的概念验证代码,请耐心等待:
<?php
Stopwatch::start();
$inputFileType = PHPExcel_IOFactory::identify( './example.xlsx' );
var_dump( 'FileType: '.$inputFileType );
Stopwatch::rel( 'identify filetype' );
$objReader = PHPExcel_IOFactory::createReader( $inputFileType );
$objReader->setReadDataOnly( true );
$filterSubset = new ReadFilter( 1, 35, range( 'A', 'J' ));
$objReader->setReadFilter( $filterSubset );
Stopwatch::rel( 'create reader' );
$objPHPExcel = $objReader->load( $inputFileName );
Stopwatch::rel( 'load file' );
$data = $objPHPExcel->getSheetByName( 'Data' );
$inputCell = $data->getCell( 'B9' );
$outputCell = $data->getCell( 'B35' );
Stopwatch::rel( 'get cells' );
goalSeek( $inputCell, $outputCell, '0.10', 1, 5 );
function goalSeek( $inputCell, $outputCell, $targetValue ) {
$cellValue = function() use ( &$outputCell, $precision ) {
return round( $outputCell->getCalculatedValue(), $precision );
};
$setValue = function( $value ) use ( &$inputCell, &$objPHPExcel, $cellValue ) {
$inputCell->setValue( $value );
PHPExcel_Calculation::getInstance( $objPHPExcel )->clearCalculationCache(); // -- clear cache so updates are calculated
Stopwatch::rel( 'goal-seek' );
};
// -- very basic goal seeking psuedo-code
while( $stillHunting ) { // -- outside tolerance
$setValue( $newInputValue );
}
};
class ReadFilter implements PHPExcel_Reader_IReadFilter {
private $_startRow = 0;
private $_endRow = 0;
private $_columns = [];
public function __construct( $startRow, $endRow, $columns ) {
$this->_startRow = $startRow;
$this->_endRow = $endRow;
$this->_columns = $columns;
}
public function readCell( $column, $row, $worksheetName = '' ) {
if( $row >= $this->_startRow && $row <= $this->_endRow ) { // -- valid row
if( in_array( $column, $this->_columns )) { // -- valid column
return true;
}
}
// else (implicit)
return false;
}
}
输出
string 'FileType: Excel2007' (length=19)
array (size=2)
'rel' =>
array (size=17)
'identify' => float 0.008597135543823242
'create reader' => float 0.0001199245452880859
'load file' => float 0.387645959854126
'get cells' => float 5.292892456054688E-5
'goal-seek' => float 0.4194750785827637
'goal-seek2' => float 0.3829901218414307
'goal-seek3' => float 0.3478608131408691
'goal-seek4' => float 0.3471150398254395
'goal-seek5' => float 0.3569440841674805
'goal-seek6' => float 0.378180980682373
'goal-seek7' => float 0.3683559894561768
'goal-seek8' => float 0.3778479099273682
'goal-seek9' => float 0.3664979934692383
'goal-seek10' => float 0.4503841400146484
'_avg' => float 0.2794940630594889
'_untilStop' => float 0.5339441299438477
'total' => float 4.726345062255859
好的,如果您重新计算相同的公式,但相关单元格中的值不同,一种可能的解决方案可能会加快速度,即只解析一次公式,但执行多次。
getCalculatedValue()
调用了两个方法;第一个是 parseFormula()
,它将公式作为字符串接受,并为执行该公式构建步骤的解析器堆栈(作为数组);第二个(私有方法,所以你需要在 Calculation.php 中将其更改为 public)是 processTokenStack()
,它接受 3 个参数,调用 [=11 生成的令牌堆栈=]、单元格 ID(作为字符串)和单元格对象。
您可能只执行一次 parseFormula() 步骤,然后在每次迭代中调用 processTokenStack()
,这将消除除了第一次迭代之外的所有步骤的解析步骤
我是 PHPExcel 的新手,我已经搜索过 Google,但在这里没有找到适合我的具体问题的信息。我发现 updating/evaluating 我的 sheet 中的公式真的 很慢。
我有一个很小的 excel 文件,我在上面做一些非常基本的目标搜索(目标搜索在 PHP 中完成,最终结果计算在 [=35= 中完成) ] sheet)。我已经让它准确地工作了,但速度绝对让我窒息。看来公式计算是罪魁祸首——公式calculations/updates如何加速?
遗憾的是,我无法 post 一份 excel 文件的副本,因为内容是我公司的商业机密,但这没什么特别的。公式中非常简单的算术运算。我能想到的唯一可能对这里产生影响的是一些单元格依赖链可能有点长(15-ish 依赖)。
从下面的输出中可以看出,我们只执行了 11 次迭代来寻找目标,总共花费了 4-5 秒。由于这将是一项 AJAX 服务,我真的需要它比那更快。
代码
这是非常快速和肮脏的概念验证代码,请耐心等待:
<?php
Stopwatch::start();
$inputFileType = PHPExcel_IOFactory::identify( './example.xlsx' );
var_dump( 'FileType: '.$inputFileType );
Stopwatch::rel( 'identify filetype' );
$objReader = PHPExcel_IOFactory::createReader( $inputFileType );
$objReader->setReadDataOnly( true );
$filterSubset = new ReadFilter( 1, 35, range( 'A', 'J' ));
$objReader->setReadFilter( $filterSubset );
Stopwatch::rel( 'create reader' );
$objPHPExcel = $objReader->load( $inputFileName );
Stopwatch::rel( 'load file' );
$data = $objPHPExcel->getSheetByName( 'Data' );
$inputCell = $data->getCell( 'B9' );
$outputCell = $data->getCell( 'B35' );
Stopwatch::rel( 'get cells' );
goalSeek( $inputCell, $outputCell, '0.10', 1, 5 );
function goalSeek( $inputCell, $outputCell, $targetValue ) {
$cellValue = function() use ( &$outputCell, $precision ) {
return round( $outputCell->getCalculatedValue(), $precision );
};
$setValue = function( $value ) use ( &$inputCell, &$objPHPExcel, $cellValue ) {
$inputCell->setValue( $value );
PHPExcel_Calculation::getInstance( $objPHPExcel )->clearCalculationCache(); // -- clear cache so updates are calculated
Stopwatch::rel( 'goal-seek' );
};
// -- very basic goal seeking psuedo-code
while( $stillHunting ) { // -- outside tolerance
$setValue( $newInputValue );
}
};
class ReadFilter implements PHPExcel_Reader_IReadFilter {
private $_startRow = 0;
private $_endRow = 0;
private $_columns = [];
public function __construct( $startRow, $endRow, $columns ) {
$this->_startRow = $startRow;
$this->_endRow = $endRow;
$this->_columns = $columns;
}
public function readCell( $column, $row, $worksheetName = '' ) {
if( $row >= $this->_startRow && $row <= $this->_endRow ) { // -- valid row
if( in_array( $column, $this->_columns )) { // -- valid column
return true;
}
}
// else (implicit)
return false;
}
}
输出
string 'FileType: Excel2007' (length=19)
array (size=2)
'rel' =>
array (size=17)
'identify' => float 0.008597135543823242
'create reader' => float 0.0001199245452880859
'load file' => float 0.387645959854126
'get cells' => float 5.292892456054688E-5
'goal-seek' => float 0.4194750785827637
'goal-seek2' => float 0.3829901218414307
'goal-seek3' => float 0.3478608131408691
'goal-seek4' => float 0.3471150398254395
'goal-seek5' => float 0.3569440841674805
'goal-seek6' => float 0.378180980682373
'goal-seek7' => float 0.3683559894561768
'goal-seek8' => float 0.3778479099273682
'goal-seek9' => float 0.3664979934692383
'goal-seek10' => float 0.4503841400146484
'_avg' => float 0.2794940630594889
'_untilStop' => float 0.5339441299438477
'total' => float 4.726345062255859
好的,如果您重新计算相同的公式,但相关单元格中的值不同,一种可能的解决方案可能会加快速度,即只解析一次公式,但执行多次。
getCalculatedValue()
调用了两个方法;第一个是 parseFormula()
,它将公式作为字符串接受,并为执行该公式构建步骤的解析器堆栈(作为数组);第二个(私有方法,所以你需要在 Calculation.php 中将其更改为 public)是 processTokenStack()
,它接受 3 个参数,调用 [=11 生成的令牌堆栈=]、单元格 ID(作为字符串)和单元格对象。
您可能只执行一次 parseFormula() 步骤,然后在每次迭代中调用 processTokenStack()
,这将消除除了第一次迭代之外的所有步骤的解析步骤