首页 > 解决方案 > 如何从 csv 数据中对大型多维数组进行排序?

问题描述

我正在导入一个可能有大约 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;
}

标签: phpcsv

解决方案


正如评论中提到的那样,解决方案应该是足够增加内存限制(如果可能的话),或者减少数据重复并切换到一次处理 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 和更高版本)在对象的内存效率方面非常出色。根据您可能拥有的其他属性和代码,面向对象的方法可以提供很多不错的好处(例如,通过引用自动传递、更易于阅读和维护代码、内存效率、清晰的默认值、数据管理、 ETC...)。


推荐阅读