0

私のデータ マイニング プロジェクトでは、処理する前に「修正」を実行する必要があることを除いて、必要なすべての情報を含む複雑で巨大な多次元配列の配列が与えられます。この問題を処理するコードをいくつか書きましたが、「修正」しなければならない膨大な量のデータには時間がかかりすぎており、誰かがより効率的な解決策を見つけるのを手伝ってくれることを願っています.

基本的に、私が扱っている配列のタイプは、通常の配列と同じように、最初に整数でインデックス付けされます。つまり$x[0], $x[1], $x[2]、各要素は、必要なキーペアの値を含む連想配列です (など$x[0]['item'], $x[0]['price'])、ただし、1 つのキーは、ID というもう少し深い場所に格納されます。

ID 番号は として配列に存在し、$x[0]['@attributes']['id']この情報を のような他のキー ペアと一緒に複製して構造を簡素化したいと考えています$x[0]['id']

私が扱っているデータ セットは大きいですが、ここに私の状況の簡単な例を示します。

$attrib1 = array('id'=>'101');
$item1 = array('@attributes'=>$attrib1, 'item'=>'milk', 'price'=>'3.50');
$attrib2 = array('id'=>'102');
$item2 = array('@attributes'=>$attrib2, 'item'=>'butter', 'price'=>'2.45');
$attrib3 = array('id'=>'103');
$item3 = array('@attributes'=>$attrib3, 'item'=>'bread', 'price'=>'1.19');
$items = array($item1, $item2, $item3);
echo "Starting data - items using itemid as attribute:\n";
print_r($items);

# set item numbers by key instead of attribute
$i=0;
while(isset($items[$i]['@attributes']['id'])) {
   $items[$i]['itemid'] = $items[$i]['@attributes']['id'];
   #unset($items[$i]['@attributes']);
   $i++;
} # while
echo "\nDesired result - items using itemid as key:\n";
print_r($items);

上記の例からの出力は次のとおりです。

Starting data - items using itemid as attribute:
Array
(
    [0] => Array
        (
            [@attributes] => Array
                (
                    [id] => 101
                )

            [item] => milk
            [price] => 3.50
        )

    [1] => Array
        (
            [@attributes] => Array
                (
                    [id] => 102
                )

            [item] => butter
            [price] => 2.45
        )

    [2] => Array
        (
            [@attributes] => Array
                (
                    [id] => 103
                )

            [item] => bread
            [price] => 1.19
        )

)

Desired result - items using itemid as key:
Array
(
    [0] => Array
        (
            [@attributes] => Array
                (
                    [id] => 101
                )

            [item] => milk
            [price] => 3.50
            [itemid] => 101
        )

    [1] => Array
        (
            [@attributes] => Array
                (
                    [id] => 102
                )

            [item] => butter
            [price] => 2.45
            [itemid] => 102
        )

    [2] => Array
        (
            [@attributes] => Array
                (
                    [id] => 103
                )

            [item] => bread
            [price] => 1.19
            [itemid] => 103
        )

)

目的の結果に追加された [itemid] キーと値のペアに注意してください。これを達成するためのより高速でエレガントな方法はありますか? PHP の派手な配列関数をいくつか見てきましたが、このより複雑な状況に頭を悩ませてそれらを利用することはできません。何か案は?

4

3 に答える 3

2

メモリ効率

PHP DOC コメント: のメモリ フットプリントはsplFixedArray37%同じサイズの通常の「配列」とほぼ同じです。

splFixedArrayIteratorまた、リストをカプセル化し、一度に 1 つの要素に可視性を公開して、はるかに効率的にすることを意味する実装も実装します。

foreachループは、渡された配列のコピーを作成します。大量のデータを処理している場合、配列で直接使用するとパフォーマンスの問題が発生する可能性があります

また、PHP 配列 (および値) は実際にはどのくらいの大きさですか? も参照してください。(ヒント:大きい!)

あなたが試すことができます

$it = SplFixedArray::fromArray($items);
foreach ( $it as $value ) {
    // Play with big array
}

スピード

ここに簡単なベンチマークがあります

set_time_limit(0);
echo "<pre>";

$total = 10000;
$item = array("milk","butter","bread");
$items = array();

// Generating Random Data
for($i = 0; $i < $total; $i ++) {
    $att = array('id' => $i);
    $items[] = array('@attributes' => $att,'item' => $item[$i % 3],'price' => mt_rand(100, 5000) / 100);
}
// Pure array no copy
function m1($array) {
    foreach ( $array as $k => $v ) {
        isset($v['@attributes']) and $array[$k]['id'] = $v['@attributes']['id'];
        unset($array[$k]['@attributes']);
    }
    return $array;
}

// Array clean copy
function m2($array) {
    $items = array();
    foreach ( $array as $k => $v ) {
        isset($v['@attributes']) and $items[$k]['id'] = $v['@attributes']['id'];
        $items[$k]['item'] = $v['item'];
        $items[$k]['price'] = $v['price'];
    }
    return $items;
}

// Array Iterator
function m3($array) {
    $it = new ArrayIterator($array);
    $items = array();
    foreach ( $it as $k => $v ) {
        isset($v['@attributes']) and $items[$k]['id'] = $v['@attributes']['id'];
        $items[$k]['item'] = $v['item'];
        $items[$k]['price'] = $v['price'];
    }
    return $items;
}

// SplFixedArray Array
function m4($array) {
    $it = SplFixedArray::fromArray($array);
    $items = array();
    foreach ( $it as $k => $v ) {
        isset($v['@attributes']) and $items[$k]['id'] = $v['@attributes']['id'];
        $items[$k]['item'] = $v['item'];
        $items[$k]['price'] = $v['price'];
    }
    return $items;
}

// Array Map
function m5($array) {
    $items = array_map(function ($v) {
        isset($v['@attributes']) and $v['id'] = $v['@attributes']['id'];
        unset($v['@attributes']);
        return $v;
    }, $array);
    return $items;
}

// Array Walk
function m6($array) {
    array_walk($array, function (&$v, $k) {
        isset($v['@attributes']) and $v['id'] = $v['@attributes']['id'];
        unset($v['@attributes']);
        return $v;
    });
    return $array;
}

$result = array('m1' => 0,'m2' => 0,'m3' => 0,'m4' => 0,'m5' => 0,'m6' => 0);

for($i = 0; $i < 1; ++ $i) {
    foreach ( array_keys($result) as $key ) {
        $alpha = microtime(true);
        $key($items);
        $result[$key] += microtime(true) - $alpha;
    }
}

echo '<pre>';
echo "Single Run\n";
print_r($result);
echo '</pre>';

$result = array('m1' => 0,'m2' => 0,'m3' => 0,'m4' => 0,'m5' => 0,'m6' => 0);

for($i = 0; $i < 2; ++ $i) {
    foreach ( array_keys($result) as $key ) {
        $alpha = microtime(true);
        $key($items);
        $result[$key] += microtime(true) - $alpha;
    }
}

echo '<pre>';
echo "Dual Run\n";
print_r($result);
echo '</pre>';

それは非常に興味深い結果をもたらします

PHP 5.3.10

Single Run
Array
(
    [m1] => 0.029280185699463 <--------------- fastest
    [m2] => 0.038463115692139
    [m3] => 0.049274921417236
    [m4] => 0.03856086730957
    [m5] => 0.032699823379517
    [m6] => 0.032186985015869
)

Dual Run
Array
(
    [m1] => 0.068470001220703
    [m2] => 0.077174663543701
    [m3] => 0.085768938064575
    [m4] => 0.07695198059082
    [m5] => 0.073209047317505
    [m6] => 0.065080165863037 <--------------- Fastest after in 2 loops
)

PHP 5.4.1

Single Run
Array
(
    [m1] => 0.029529094696045
    [m2] => 0.035377979278564
    [m3] => 0.03830099105835
    [m4] => 0.034613132476807
    [m5] => 0.031363010406494
    [m6] => 0.028403043746948  <---------- fastest
)

Dual Run
Array
(
    [m1] => 0.072367191314697
    [m2] => 0.071731090545654
    [m3] => 0.078131914138794
    [m4] => 0.075049877166748
    [m5] => 0.065959930419922
    [m6] => 0.060923099517822  <---------- Fastest
)
于 2012-10-25T21:37:16.470 に答える
1

それは XML から来ているように見えるので、@attributes には ID 以外のものを含めることが可能であると付け加えておきます..しかし、それが起こらないと仮定すると、代わりに foreach を使用してみてください。速度が向上します。

ループしているのと同じ配列を変更しているため、影響がある可能性があります(ただし、これについての証拠は見つからないため、実験が必要です)

$cleanedArray = array();
foreach($bigArray as $subArray)
{
  if(isset($subArray['@attributes']))
  {
     $subArray['itemid'] = $subArray['@attributes']['id'];
    unset($subArray['@attributes']); //Optional
    $cleanedArray[] = $subArray;
  }
}

遅くなってしまったらごめんなさい

編集:不足しているインデックスが追加されました

于 2012-10-25T21:33:52.643 に答える
0

これは、提供されたアプローチの比較であるため、答えではありません。

このスクリプトを使用して、アルゴリズムにかかった時間を平均化しました。

<?php
//base data
$attrib1 = array('id'=>'101');
$item1 = array('@attributes'=>$attrib1, 'item'=>'milk', 'price'=>'3.50');
$attrib2 = array('id'=>'102');
$item2 = array('@attributes'=>$attrib2, 'item'=>'butter', 'price'=>'2.45');
$attrib3 = array('id'=>'103');
$item3 = array('@attributes'=>$attrib3, 'item'=>'bread', 'price'=>'1.19');
$results = array('test1'=>array(),'test2'=>array(),'test3'=>array());

//set trials
$trials=1000;

//test 1
for($count=0;$count<$trials;$count++){
unset($items);
$items = array($item1, $item2, $item3);
$timer1=microtime();
$i=0;
while(isset($items[$i]['@attributes']['id'])) {
   $items[$i]['itemid'] = $items[$i]['@attributes']['id'];
   $i++;
}
$timer1=microtime()-$timer1;
$results['test1'][$count]=$timer1;
}

//test 2
for($count=0;$count<$trials;$count++){
unset($items);
unset($cleanedArray);
$items = array($item1, $item2, $item3);
$cleanedArray = array();
$timer2=microtime();
foreach($items as $subArray)
{
  if(isset($subArray['@attributes']))
  {
    unset($subArray['@attributes']);
    $cleanedArray[] = $subArray;
  }
}
$timer2=microtime()-$timer2;
$results['test2'][$count]=$timer2;
}

//test 3
for($count=0;$count<$trials;$count++){
unset($items);
unset($it);
$items = array($item1, $item2, $item3);
$it = SplFixedArray::fromArray($items);
$timer3=microtime();
foreach($it as $subArray)
{
  if(isset($subArray['@attributes']))
  {
    unset($subArray['@attributes']);
    $cleanedArray[] = $subArray;
  }
}
$timer3=microtime()-$timer3;
$results['test3'][$count]=$timer3;
}

//results
$factor=pow(10,-6);
echo "Test 1 averaged " . round(array_sum($results['test1']) / count($results['test1'])/$factor,1) . " µs, with range: " . round((max($results['test1'])-min($results['test1']))/$factor,1) . " µs - (min: " . (min($results['test1'])/$factor) . ", max: " . (max($results['test1'])/$factor) . ")<br/>";

echo "Test 2 averaged " . round(array_sum($results['test2']) / count($results['test2'])/$factor,1) . " µs, with range: " . round((max($results['test2'])-min($results['test2']))/$factor,1) . " µs - (min: " . (min($results['test2'])/$factor) . ", max: " . (max($results['test2'])/$factor) . ")<br/>";

echo "Test 3 averaged " . round(array_sum($results['test3']) / count($results['test3'])/$factor,1) . " µs, with range: " . round((max($results['test3'])-min($results['test3']))/$factor,1) . " µs - (min: " . (min($results['test3'])/$factor) . ", max: " . (max($results['test3'])/$factor) . ")<br/>";

echo "<pre>";
var_dump($results);
echo "</pre>";

ここでの結果は、試行回数が少ないと非常に変動しますが、基本配列が大きくなり、試行回数が増えると、より歪むはずです。

于 2012-10-25T22:54:09.147 に答える