Scilab

时间:2017-10-30 19:51:47

标签: plot scatter-plot scilab

我在几列(table.dat)中有一个大型数据表,我将其作为矩阵导入到Scilab 6.0中

A=fscanfMat('table.dat');

然后将该矩阵的两列作为平面中点的x坐标和y坐标。命令

scatter(A(:,1),A(:,2),0,".")

现在生成一个漂亮的点云,但我想根据平面中数据点的数密度,即附近点的空间密度,对该散点图中的每个点着色。例如,在高密度区域中点应为深蓝色,在较低密度区域中应为红色,其间的所有彩虹色均为平滑过渡。

在这个帖子中,Python回答了这个问题: How can I make a scatter plot colored by density in matplotlib?

但是如何在Scilab中实现这一目标?

1 个答案:

答案 0 :(得分:1)

通过以下方式解决您的问题:

  1. 计算数据的kernel density estimate (KDE)d;
  2. 使用rainbowcolormap(n)创建一个m种颜色的地图n;
  3. 以这样的方式绘制您的数据:scatter(x,y,s,d,"fill"); set(gcf(),"color_map",m);,其中s是绘图中标记的大小。
  4. 由于我无法使用stixbox toolbox for Scilab,我决定针对此问题提出解决方法,因此请为自己做好准备。

    Pure Scilab solution

    首先,我在Scilab宏上实现了kernel_density()。其输入为x,n-by-p数据矩阵,带宽为h。它的作用是计算在每个数据点中心的半径h的圆/球/ n球内有多少点。

    我在这个统计领域不是很有经验,所以我不得不阅读有关KDE的内容。事实证明,我的这个解决方案实际上是一个使用constant and equal weight for the neighbors内核的KDE方法(因此我将h重命名为“带宽”而不仅仅是“半径”,以及为什么我添加了2*h*n计算因素)。

    另外,由于我缺乏知识,我无法实现为给定数据集自动选择最佳h的方法,因此您必须通过反复试验来选择它。但是,阅读我在您在问题中提供的示例中看到的Scipy implementation of gaussian_kde(),以及使用来自this questionthis reference的提示,我提出了一种方法来减少到4可能的数量h(如果您的数据有2个维度)。也许真正的统计学家可以在评论中对其进行验证,或提供更好的方法:

    1. 计算数据集的协方差矩阵;
    2. 将其平方根乘以斯科特的因子:n ^ (-1 / (p+4));
    3. 为所有h绘制图表并选择可提供最佳可视化效果的图片。
    4. 原始kernel_density功能仍然可以找到here,它可以正常工作大约10³点。如果你处理的不止于此,请继续阅读。

      C实施

      如评论部分所述,Scilab实施相当缓慢。为了获得更好的结果,我在C中实现了kdec()并使用ilib_for_link()将其链接到Scilab宏。但是,这种方法仍有问题(见底部的警告说明)。

      要在Scilab上使用此功能,您应该拥有兼容的C编译器:

      • 如果您使用的是UNIX或类UNIX系统,则无需担心。
      • 如果您使用的是Windows,则应执行mingw toolbox的说明,并在执行kde()时将其加载到Scilab环境中。

      首先,您必须将kdec.c放在当前的Scilab目录中。

      //kdec.c
      #include <math.h>
      
      void kdec(double f[], double x[], double *h, int *n, int *p){
          /* x[]: (n*p)-by-1 array of data
           *  *h: bandwitdh
           *  *n: the number of points
           *  *p: the number of dimensions
           * f[]: the output
           *
           *  the local neighborhood density can be defined as (for constant weight):
           *   f(x0) = sum_from i_to n of K(||x_i - x_0|| <= h) / 2hn
           *   where: x0 is the observed point, which can have p-dimensions;
           *          K(a) = {1 if a == True
           *                 {0 if a == False
           */
      
          int n_ = *n; int p_ = *p; double h_ = *h;
      
          int d, j, k;
          double dif, norm;
      
          for(j = 0; j < n_; j++){
              f[j] = 0;
      
              for(k = 0; k < n_; k++){
                  norm = 0;
      
                  for(d = 0; d < p_; d++){
                      dif = x[k + d*n_] - x[j + d*n_];
                      norm = norm + dif * dif;
                  }
                  norm = sqrt(norm);
      
                  if (norm <= h_){
                      f[j] = f[j] + 1;
                  }
              }
      
      
              f[j] = f[j]  / (2 * (h_) * (n_));
          }
      }
      

      然后,设置kde.sci以调用kdec C函数并包装新的Scilab kde函数。

      //kde.sci
      if ~isdef('kde') then
          ilib_for_link('kdec','kdec.c',[],"c") //compile and create the new shared library
          exec('loader.sce',-1);                //load library
      end
      
      //create a wrapper function to improve interface with interface 'kdec'
      function varargout = kde(x,h)
          //x: n-by-p matrix of data, each column is a dimension
          //h: bandwitdh
      
          [n, p] = size(x); //n: number of points
                            //p: number of dimensions
          x = x(1:$);
          if length(h) ~= 1 then
              error("kde(x,h): x should be n-by-p matrx; " +...
                    "h shoud be scalar, positive, and real");
          end
          f = call('kdec'...
                  , x     , 2, 'd'...
                  , abs(h), 3, 'd'...
                  , n     , 4, 'i'...
                  , p     , 5, 'i'...
                  ,'out'...
                  ,[n,1]  , 1, 'd' );
      
          varargout = list(f)
      endfunction
      

      由于我在统计数据方面没有得到任何改善,您仍然需要手动设置h。然而,经过多次测试后,似乎2D数据的最佳结果是:

      scotts_factor = n ^ (-1 / (p+4))
      h = sqrt(abs(cov(A))) .* scotts_factor;
      h = h(2);
      

      以下是一些测试:

      exec('kde.sci',-1);
      
      //create data set
      n = 1d4;
      p = 2;
      A = grand((n/2), 1, "nor", 0, 1);
      A = [A, A * 3 + grand((n/2), 1, "nor", 0, 1)];
      A = [ A ; [ A(:,1) * 0.8 , A(:,2) * 1.3 + 10 ] ];
      
      //calculating bandwidth
      scotts_factor = n ^ (-1 / (p+4))
      h = sqrt(abs(cov(A))) .* scotts_factor;
      h = h(2);
      
      //calculate density
      d = kde(A, h);
      
      [d, idx] = gsort(d); //sorting data to plot higher-density points
      idx = idx($:-1:1);   //over lower-density ones
      d = d($:-1:1);       //(reversing densities matrix)
      A = A(idx,:);        //(reordering data matrix)
      
      //plotting
      scf(); clf();
      scatter(A(:,1), A(:,2), 10, d, "fill");
      
      m = rainbowcolormap(32);  //create the rainbow color map
      m = m($:-1:1,:);          //reverse it to get hotter colors on higher densities
      set(gcf(),'color_map',m); //set the desired color map
      

      输出结果为:

      Local neighborhood density plot for 10^4 random points

      警告说明

      即使在C语言中实现它,它仍然是一个高成本的功能。由于两个嵌套的for循环,它是O(n²)。 我做了一些测量,结果如下:

       n (points)  |   10^3  | 5*10^3 |  10^4  |  10^5
      -------------+---------+--------+--------+---------
       t (seconds) | 0.13751 | 1.2772 | 4.4545 | 323.34 
      

      以100k积分运行kde()需要5分钟以上。既然你说你想评估1M点,我也不会推荐这个解决方案。不过,将它与纯Scilab解决方案进行比较:后者只需要5s即可在10³点(!)上工作。这已经是一个巨大的进步,但我担心我的解决方案不会变得更好。也许您应该尝试减少样本数量,或者寻找其他计算工具,例如R