+ -
当前位置:首页 → 问答吧 → 请大家帮忙看看这个问题,关于矩阵计算

请大家帮忙看看这个问题,关于矩阵计算

时间:2010-09-08

来源:互联网

求教一个算法,上次问过,但是还是没有搞清楚。

比如有这样一个矩阵:

sox    7.2  3.8  6.8  9.2  5.6
sox    5.4  2.3  4.6  8.9  9.0
sox    6.7  NA  7.8  9.0  3.1
goo    2.4  6.7  NA  9.0  2.1
goo    2.1  5.6  7.8  9.7  1.2
pkk    2.5  4.3  6.5  4.9  0.2
pkk    2.1  3.4  3.2  NA  4.6
pkk    3.2  5.6  6.7  9.1  2.2
...
...
...


这个矩阵很简单,就是有一些同名的行,现在我要做的是,把这些同名的行的数据按每一列合并起来,按什么合并,按照每一列的平均值来合并

就是说,比如行名为sox的行,第一列数据为: 7.2, 5.4, 6.7 那么第一列的平均值就是(7.2+5.4+6.7)/3=6.4,
第二列数据为3.8, 2.3, NA, 那么平均值就是  (3.8+2.3)/2=3, 以此类推,得到每一列的平均值,作为最后的值,那么行名为sox的行最后就合并为:
sox  6.4  3  ....

就是要写这样一个程序,原理很简单吧,但是鄙人的水平有限,写的程序效率上不去。

我是这样写的,

先用一个hash存下所有的行名:sox, goo, pkk.....

然后遍历这个hash, foreach (%hash) { }

每一次,把相同行名的行取出来放到一个数组@num中

这个矩阵有一个值是确定的,那就是列数是确定的,所以对每一个数组@num, 我用for($i=0;$i<列数;$i++) 循环,把每一列取出来,放到一个新的数组@haha中

然后for($j=0;$j<@haha;$j++)  把@haha的平均值算出来,作为这一列的最后的值

就是这样。。。

程序我是写出来了,对于行数和列数都比较少的情况,程序没有什么问题。但是如果我有大量的行和列,比如有40000行,500列,这种情况下,这个程序就不是一两天能跑完的了。我跑了三天,还没有跑完。工作情况是不允许我跑这么久的。

恳请高手们帮忙指教。非常感谢!

作者: duziteng   发布时间: 2010-09-08

本帖最后由 iamlimeng 于 2010-09-08 06:55 编辑

按你的思路,内存开销会很大,效率也较低。实际上,可以读一行数据,马上就计算,而不是存储进数组后再处理。

我按你的数据结构随机生成了一个数据文件,40000行,每行501列(0-10之间的随机数,一位小数),共300个关键字,以制表符分隔列,数据文件大小76.2M,在我的计算机上跑,需要146秒(CPU: P4 2.4G),内存占用非常小,主要是占用CPU用于计算。

代码如下,供你参考:
  1. #!/usr/bin/perl

  2. use strict;
  3. use warnings;
  4. use Benchmark;
  5. my $TT0 = new Benchmark;

  6. local $| = 1;
  7. my $x;
  8. print " Calculating...\n\n";
  9. my %data;
  10. open (FH,"<data.txt");
  11. while (<FH>) {
  12.         chomp;
  13.         my @rows = split /\t/;
  14.         foreach my $n (1..$#rows) {
  15.                  next if (uc($rows[$n]) eq 'NA');
  16.                  $data{$rows[0]}{$n}{'total'} += $rows[$n];
  17.                  $data{$rows[0]}{$n}{'count'}++;
  18.          }
  19.          $x++;
  20.          print "\r $x lines calculated OK!";
  21. }
  22. close FH;

  23. open (FH,">data_caculated.txt");
  24. foreach my $str (sort keys %data) {
  25.         my $line .= $str;
  26.          my @rows = keys %{$data{$str}};
  27.         foreach my $n (1..$#rows+1) {
  28.                 $line .= "\t".sprintf("%.2f",$data{$str}{$n}{'total'}/$data{$str}{$n}{'count'});
  29.          }
  30.          print FH "$line\n";
  31. }
  32. close FH;

  33. # print benchmark result
  34. my $TT1 = new Benchmark;
  35. my $td = Benchmark::timediff($TT1, $TT0);
  36. $td = Benchmark::timestr($td);
  37. print "\n\n Time expend: $td\n\n Press Enter to close me ... \7";

  38. <STDIN>;
复制代码
如果去掉进度显示,能快10秒左右,但有进度显示,心里更有底。

另:本程序允许不同关键字,有不同的列宽。

作者: iamlimeng   发布时间: 2010-09-08

用数据库会不会快点?

作者: yybmsrs   发布时间: 2010-09-08