2009-08-25 17 views
2

は、私は、次のスパース行列Aキャプチャ非ゼロ要素、カウントおよびインデックス

2 3 0 0 0 
    3 0 4 0 6 
    0 -1 -3 2 0 
    0 0 1 0 0 
    0 4 2 0 1 

を持ってそれから私は、そこから次の情報をキャプチャしたいと思います:

  1. 累積行列が列方向に走査されるので、エントリの数。 収穫:

    Ap = [0,2,5,9,10,12];

  2. 行のインデックスは、行列が列方向にスキャンされるためです。 収量:

    Ai = [0,1,0,2,4,1,2,3,4,2,1,4];

  3. 行列が列方向にスキャンされるため、ゼロでない行列エントリ。 降伏:

    Ax = [2,3,3、-1,4,4、-3,1,2,2,6,1];

実行列Aが大きいvery2潜在的であるので、これらの要素を取り込むことができるPerlで任意の効率的な方法 はありますか?特に、すべての行列AをRAMにスラピングすることなく、 をRAMに入れます。

次のコードが付いています。それは私が望むものを与えない。

use strict; 
use warnings; 

my (@Ax, @Ai, @Ap) =(); 
while (<>) { 
    chomp; 
    my @elements = split /\s+/; 
    my $i = 0; 
    my $new_line = 1; 
    while (defined(my $element = shift @elements)) { 
     $i++; 
     if ($element) { 
      push @Ax, 0 + $element; 
      if ($new_line) { 
       push @Ai, scalar @Ax; 
       $new_line = 0; 
      } 

      push @Ap, $i; 
     } 
    } 
} 
push @Ai, 1 + @Ax; 
print('@Ax = [', join(" ", @Ax), "]\n"); 
print('@Ai = [', join(" ", @Ai), "]\n"); 
print('@Ap = [', join(" ", @Ap), "]\n"); 
+0

@foolishbratところで、 '印刷 "\ @Ax = [@Ax]を\ n" を比較します。あなたが持っているものに。 –

答えて

1

まばらなデータを格納するための一般的な戦略は、あなたが(ゼロ)を気にしないと行を格納する値をドロップすることであり、従って、それらの位置情報を保持し、あなたが気にしない、それぞれの値を持つ列のインデックス、:ニーズのすべてがデータ列ごとに列を処理することによって満たすことができるので、あなたの場合は

[VALUE, ROW, COLUMN] 

、あなたはさらに節約することができ、つまり、すべての値に対してCOLUMNを繰り返す必要はありません。

use strict; 
use warnings; 
use Data::Dumper; 

my ($r, $c, @dataC, @Ap, @Ai, @Ax, $cumul); 

# Read data row by row, storing non-zero values by column. 
# $dataC[COLUMN] = [ 
#  [VALUE, ROW], 
#  [VALUE, ROW], 
#  etc. 
# ] 
$r = -1; 
while (<DATA>) { 
    chomp; 
    $r ++; 
    $c = -1; 
    for my $v (split '\s+', $_){ 
     $C++; 
     push @{$dataC[$c]}, [$v, $r] if $v; 
    } 
} 

# Iterate through the data column by column 
# to compute the three result arrays. 
$cumul = 0; 
@Ap = ($cumul); 
$c = -1; 
for my $column (@dataC){ 
    $C++; 
    $cumul += @$column; 
    push @Ap, $cumul; 
    for my $value (@$column){ 
     push @Ax, $value->[0]; 
     push @Ai, $value->[1]; 
    } 
} 

__DATA__ 
2 3 0 0 0 
3 0 4 0 6 
0 -1 -3 2 0 
0 0 1 0 0 
0 4 2 0 1 
0

Apは簡単です。簡単にゼロで始まり、0以外の数字になるたびに増分します。あなたが@Apに何かを書こうとしているのを見ていないので、あなたが望むようには終わらないのは驚きではありません。

AiとAxがトリッキーです:行単位でスキャンしている間に列方向の並べ替えが必要です。列がどれくらいの数の要素が得られるかまだ分からないので、インプレースで何もできないので、要素の位置を事前に知ることはできません。

明らかに、代わりに行ごとに順序付けする必要がある場合は、もっと簡単になります。それができなければ、あなたは複雑になり、(i、j、x)の三つ子を集めることができます。収集中、彼らは当然(i、j)によって注文されるだろう。コレクション後、(j、i)で並べ替えるだけです。

0

提供したコードは、行単位で動作します。あなたが別の配列に自分の価値観を蓄積する必要があり、列によって結果のシーケンシャルを取得するには、各列ごとに1:

# will look like ([], [], [] ...), one [] for each column. 
my @columns; 

while (<MATRIX>) { 
    my @row = split qr'\s+'; 
    for (my $col = 0; $col < @row; $col++) { 

     # push each non-zero value into its column 
     push @{$columns[$col]}, $row[$col] if $row[$col] > 0; 

    } 
} 

# now you only need to flatten it to get the desired kind of output: 
use List::Flatten; 
@non_zero = flat @columns; 

List::Flattenを参照してください。

1

これは、あなたが探しているものを、私は推測している:

#!/usr/bin/perl 

use strict; 
use warnings; 

use Data::Dumper::Simple; 

my @matrix; 

# Populate @matrix 
while (<>) { 
    push @matrix, [ split /\s+/ ]; 
} 

my $columns = @{ $matrix[0] }; 
my $rows = @matrix; 

my (@Ap, @Ai, @Ax); 
my $ap = 0; 

for (my $j = 0 ; $j <= $rows ; $j++) { 
    for (my $i = 0 ; $i <= $columns ; $i++) { 
     if ($matrix[$i]->[$j]) { 
      $ap++; 
      push @Ai, $i; 
      push @Ax, $matrix[$i]->[$j]; 
     } 
    } 
    push @Ap, $ap; 
} 

print Dumper @Ap; 
print Dumper @Ai; 
print Dumper @Ax; 
+0

@AHA:ありがとう。しかし、あなたのアプローチでは、マトリックス全体が@matrixに落とされます。実際には、行列の次元は10^6です。私は私のRAMがそれを買う余裕がないと思う。 – neversaint

+0

@foolishbrat:そうですね。 @Inshalla:申し訳ありませんが、わかりませんでした。 –

1

FMさんのコメントに基づいて更新。あなたは、元のデータのいずれかを保存したくない場合は、次の

#!/usr/bin/perl 

use strict; 
use warnings; 

my %matrix_info; 

while (<DATA>) { 
    chomp; 
    last unless /[0-9]/; 
    my @v = map {0 + $_ } split; 
    for (my $i = 0; $i < @v; ++$i) { 
     if ($v[$i]) { 
      push @{ $matrix_info{$i}->{indices} }, $. - 1; 
      push @{ $matrix_info{$i}->{nonzero} }, $v[$i]; 
     } 
    } 
} 

my @cum_count = (0); 
my @row_indices; 
my @nonzero; 

for my $i (sort {$a <=> $b } keys %matrix_info) { 
    my $mi = $matrix_info{$i}; 
    push @nonzero, @{ $mi->{nonzero} }; 

    my @i = @{ $mi->{indices} }; 

    push @cum_count, $cum_count[-1] + @i; 
    push @row_indices, @i; 
} 

print(
    "\@Ap = [@cum_count]\n", 
    "\@Ai = [@row_indices]\n", 
    "\@Ax = [@nonzero]\n", 
); 

__DATA__ 
2 3 0 0 0 
3 0 4 0 6 
0 -1 -3 2 0 
0 0 1 0 0 
0 4 2 0 1 

出力:

 
C:\Temp> m 
@Ap = [0 2 5 9 10 12] 
@Ai = [0 1 0 2 4 1 2 3 4 2 1 4] 
@Ax = [2 3 3 -1 4 4 -3 1 2 2 6 1] 
+2

おそらく私は何か誤解していますが、最初の答えは '@ cum_count'がOPの累積カウント(' @ Ap')に似ているという印象を与えますが、そうではありません。あなたのカウントは列方向の集計です。彼は列方向を望んでいる。つまり、OPが必要とする集計は '%row_indices'(たとえば、' scalar @ {$ row_indices {0}} ')で利用できるので、答えはまだ良いです。 – FMc

+0

@FMああ!いいキャッチ! –

関連する問題