【问题标题】:How to sort large multidimensional array from csv data?如何从 csv 数据中对大型多维数组进行排序?
【发布时间】:2020-01-02 23:36:05
【问题描述】:

我正在导入一个可能有大约 100,000 行的 csv。每行有 5 列。每行的第一列将有一个句子,其他 4 列有数值。我需要遍历 csv 数据并从每一行中获取每个单词并将其作为它自己的行添加到新数组中。比如:

| big red truck   | 5 | 2 | 5 | 1 |
| small red truck | 4 | 2 | 0 | 0 |
| big fast truck  | 3 | 2 | 4 | 1 |

变成

| truck | 12 | 6 | 9 | 2 |
| red   | 9  | 4 | 5 | 1 |
| fast  | 3  | 2 | 4 | 1 |
| small | 4  | 2 | 0 | 0 |

这就是我目前正在做的事情。它适用于较小的文件,但在大约 50,000 行时我遇到了问题并开始恢复服务器错误。

function get_csv_terms($csvdata){
    $terms = array();
    $csv_rows = count($csvdata);
    $x = 0;
    //get terms
    while($x <= $csv_rows){
        $groupTerm = explode(' ', $csvdata[$x][0]);
        foreach( $groupTerm as $term ){
            if($term != NULL){
                if(!in_array($term, $terms)){
                    $terms[] = $term;
                }
            }
        }
        $x++;
    }

    return $terms;
}

//filter csv and create data for table output
function filter_csv($csvdata){
    $sortedData = array();
    $csv_rows = count($csvdata);
    $terms = get_csv_terms($csvdata); 
    $terms_count = count($terms);
    $x = 0;

    while($x <= $terms_count){
        $y = 0;
        while($y <= $csv_rows){
            $termWords = explode(" ", $csvdata[$y][0]);
            $termWordCount = count($termWords);
            $z = 0;
            while($z <= $termWordCount){
                if($terms[$x] != NULL){
                    if($termWords[$z] == $terms[$x]){
                        $sortedData[$terms[$x]][0] +=  intval($csvdata[$y][1]);
                        $sortedData[$terms[$x]][1] +=  floatval($csvdata[$y][2]);
                        $sortedData[$terms[$x]][2] +=  floatval($csvdata[$y][3]);value
                        $sortedData[$terms[$x]][3] +=  floatval($csvdata[$y][4]);
                    }
                }
                $z++;
            }
            $y++;
        }
        $x++;
    }

    return $sortedData;
}

【问题讨论】:

  • 什么错误?内存分配错误?
  • 另外,您的示例数据表明您在最后 4 列中有常规的 int 值,但您的代码尝试将它们转换为浮点数。
  • 是的,我尝试在我的 php.ini 文件中同时增加 max_execution_time 和 memory_limit,但没有成功
  • 最后三个是真实数据中的浮点数,只是上面的小例子中没有
  • 作为一般优化,尝试一次读取 CSV 文件一条记录并以这种方式聚合数据(而不是将整个内容读入内存并循环访问)。

标签: php csv


【解决方案1】:

正如评论中提到的那样,解决方案应该是足够增加内存限制(如果可能的话),或者减少数据重复并切换到一次处理一条 CSV 记录以减少内存使用量。

还有一种可能是,使用已定义的对象类可以为您提供更好的内存效率,如果不仅仅是更好的代码可读性的话。例如,你可以试试这个:

class Term
{
  // Instance properties
  public $UsageCount = 0;
  public $Weight     = 0.00;
  public $Value      = 0.00;
  public $OtherFloat = 0.00;

  // -----------------------

  // Term dictionary
  private static $_terms = array();

  public static Get($term)
  {
    if(!isset($_terms[$term]))
    {
      $_terms[$term] = new Term();
    }
    return $_terms[$term];
  }

  public static GetAll()
  {
    return $_terms;
  }
}

在您的 CSV 循环中:

$termWords = explode(" ", $csvdata[$y][0]);
...
foreach($termWords as $termWord)
{
  $Term = Term::Get($termWord);
  $Term->UsageCount += intval($csvdata[$y][1]);
  $Term->Weight     += floatval($csvdata[$y][2]);
  $Term->Value      += floatval($csvdata[$y][3]);
  $Term->OtherFloat += floatval($csvdata[$y][4]);
}

最新版本的 PHP(7 及更高版本)在对象的内存效率方面非常出色。根据您可能拥有的其他属性和代码,面向对象的方法可以提供很多不错的好处(例如,通过引用自动传递、更易于阅读和维护代码、内存效率、清晰的默认值、数据管理、等等……)。

【讨论】:

  • 这篇较早的帖子可能有用:stackoverflow.com/questions/17520093/… 从根本上说,您需要一种“一次处理一块”的算法,这样无论您有多少输入数据......只有您在该数据中有多少键。 不要一开始就把它全部“吸”进一个内存数组中。
猜你喜欢
  • 2012-05-17
  • 2019-10-22
  • 1970-01-01
  • 1970-01-01
  • 2017-07-03
  • 2019-06-18
  • 2010-10-13
相关资源
最近更新 更多