Back

# 可视化

Owl 是一个 OCaml 数值库。除了对矩阵操作提供广泛支持之外，它还具有灵活的绘图模块。Owl 的 Plot 模块旨在通过最小的编码工作帮助您创建相当复杂的图。它构建在 [Plplot 之上，但将其复杂性隐藏在用户视野之外。

## 创建图


val create : ?m:int -> ?n:int -> string -> handle


let f x = Maths.sin x /. x in
let h = Plot.create "plot_001.png" in

Plot.set_title h "Function: f(x) = sine x / x";
Plot.set_xlabel h "x-axis";
Plot.set_ylabel h "y-axis";
Plot.set_font_size h 8.;
Plot.set_pen_size h 3.;
Plot.plot_fun ~h f 1. 15.;

Plot.output h
>- : unit = ()


## 规格

let x, y = Mat.meshgrid (-2.5) 2.5 (-2.5) 2.5 50 50 in
let z = Mat.(sin ((x * x) + (y * y))) in
let h = Plot.create ~m:2 ~n:3 "plot_020.png" in

Plot.subplot h 0 0;
Plot.(mesh ~h ~spec:[ ZLine XY ] x y z);

Plot.subplot h 0 1;
Plot.(mesh ~h ~spec:[ ZLine X ] x y z);

Plot.subplot h 0 2;
Plot.(mesh ~h ~spec:[ ZLine Y ] x y z);

Plot.subplot h 1 0;
Plot.(mesh ~h ~spec:[ ZLine Y; NoMagColor ] x y z);

Plot.subplot h 1 1;
Plot.(mesh ~h ~spec:[ ZLine Y; Contour ] x y z);

Plot.subplot h 1 2;
Plot.(mesh ~h ~spec:[ ZLine XY; Curtain ] x y z);

Plot.output h
>- : unit = ()


let x, y = Mat.meshgrid (-1.) 1. (-1.) 1. 50 50 in
let z = Mat.(tanh ((x * x) + (y * y))) in
let h = Plot.create ~m:2 ~n:3 "plot_021.png" in

Plot.subplot h 0 0;
Plot.(surf ~h ~spec:[ ] x y z);

Plot.subplot h 0 1;
Plot.(surf ~h ~spec:[ Faceted ] x y z);

Plot.subplot h 0 2;
Plot.(surf ~h ~spec:[ NoMagColor ] x y z);

Plot.subplot h 1 0;
Plot.(surf ~h ~spec:[ Contour ] x y z);

Plot.subplot h 1 1;
Plot.(surf ~h ~spec:[ Curtain ] x y z);

Plot.subplot h 1 2;
Plot.(surf ~h ~spec:[ Altitude 10.; Azimuth 125. ] x y z);

Plot.output h
>- : unit = ()


## 子图

let f p i = match i with
| 0 -> Stats.gaussian_rvs ~mu:0. ~sigma:0.5 +. p.(1)
| _ -> Stats.gaussian_rvs ~mu:0. ~sigma:0.1 *. p.(0)
in
let y = Stats.gibbs_sampling f [|0.1;0.1|] 5_000 |> Mat.of_arrays in
let h = Plot.create ~m:2 ~n:2 "plot_002.png" in
Plot.set_background_color h 255 255 255;

(* focus on the subplot at 0,0 *)
Plot.subplot h 0 0;
Plot.set_title h "Bivariate model";
Plot.scatter ~h (Mat.col y 0) (Mat.col y 1);

(* focus on the subplot at 0,1 *)
Plot.subplot h 0 1;
Plot.set_title h "Distribution of y";
Plot.set_xlabel h "y";
Plot.set_ylabel h "Frequency";
Plot.histogram ~h ~bin:50 (Mat.col y 1);

(* focus on the subplot at 1,0 *)
Plot.subplot h 1 0;
Plot.set_title h "Distribution of x";
Plot.set_ylabel h "Frequency";
Plot.histogram ~h ~bin:50 (Mat.col y 0);

(* focus on the subplot at 1,1 *)
Plot.subplot h 1 1;
Plot.set_foreground_color h 255 0 0;
Plot.set_title h "Sine function";
Plot.(plot_fun ~h ~spec:[ LineStyle 2 ] Maths.sin 0. 28.);
Plot.autocorr ~h (Mat.sequential 1 28);

(* output your final plot *)
Plot.output h
>- : unit = ()


## 多条线

let h = Plot.create "plot_024.png" in
Plot.(plot_fun ~h ~spec:[ RGB (0,0,255); Marker "#[0x2299]"; MarkerSize 8. ] Maths.sin 0. 9.);
Plot.(plot_fun ~h ~spec:[ RGB (255,0,0); Marker "#[0x0394]"; MarkerSize 8. ] Maths.cos 0. 9.);
Plot.legend_on h [|"正弦函数"; "余弦函数"|];
Plot.output h
>- : unit = ()


(* 生成数据 *)
let g x = (Stats.gaussian_pdf x ~mu:0. ~sigma:1.) *. 100. in
let y = Mat.gaussian ~mu:0. ~sigma:1. 1 1000 in

(* 绘制多个数据集 *)
let h = Plot.create "plot_025.png" in
Plot.set_background_color h 255 255 255;
Plot.(histogram ~h ~spec:[ RGB (255,0,50) ] ~bin:100 y);
Plot.(plot_fun ~h ~spec:[ RGB (0,0,255); LineWidth 2. ] g (-4.) 4.);
Plot.legend_on h [|"数据"; "模型"|];

Plot.output h
>- : unit = ()


## 图例


type legend_position =
North | South | West | East | NorthWest | NorthEast | SouthWest | SouthEast


(* 生成数据 *)
let x = Mat.(uniform 1 20 *$10.) in let y = Mat.(uniform 1 20) in let z = Mat.gaussian 1 20 in (* 绘制多个数据集 *) let h = Plot.create "plot_026.png" in Plot.(plot_fun ~h ~spec:[ RGB (0,0,255); LineStyle 1; Marker "*" ] Maths.sin 1. 8.); Plot.(plot_fun ~h ~spec:[ RGB (0,255,0); LineStyle 2; Marker "+" ] Maths.cos 1. 8.); Plot.scatter ~h x y; Plot.stem ~h x z; let u = Mat.(abs(gaussian 1 10 *$ 0.3)) in
Plot.(bar ~h ~spec:[ RGB (255,255,0); FillPattern 3 ] u);

let v = Mat.(neg u *$0.3) in let u = Mat.sequential 1 10 in Plot.(area ~h ~spec:[ RGB (0,255,0); FillPattern 4 ] u v); (* 设置图例 *) Plot.(legend_on h ~position:NorthEast [|"测试 1"; "测试 2"; "散点"; "柱状"; "直方图"; "区域"|]); Plot.output h >- : unit = ()  ## 绘制图案 绘图模块支持多种线型，如下所示： let h = Plot.create "plot_004.png" in Plot.set_background_color h 255 255 255; Plot.set_pen_size h 2.; Plot.(draw_line ~h ~spec:[ LineStyle 1 ] 1. 1. 9. 1.); Plot.(draw_line ~h ~spec:[ LineStyle 2 ] 1. 2. 9. 2.); Plot.(draw_line ~h ~spec:[ LineStyle 3 ] 1. 3. 9. 3.); Plot.(draw_line ~h ~spec:[ LineStyle 4 ] 1. 4. 9. 4.); Plot.(draw_line ~h ~spec:[ LineStyle 5 ] 1. 5. 9. 5.); Plot.(draw_line ~h ~spec:[ LineStyle 6 ] 1. 6. 9. 6.); Plot.(draw_line ~h ~spec:[ LineStyle 7 ] 1. 7. 9. 7.); Plot.(draw_line ~h ~spec:[ LineStyle 8 ] 1. 8. 9. 8.); Plot.set_xrange h 0. 10.; Plot.set_yrange h 0. 9.; Plot.output h >- : unit = ()  同样，我们还可以使用不同的图案填充矩形，如下例所示。 let h = Plot.create "plot_005.png" in Array.init 9 (fun i -> let x0, y0 = 0.5, float_of_int i +. 1.0 in let x1, y1 = 4.5, float_of_int i +. 0.5 in Plot.(draw_rect ~h ~spec:[ FillPattern i ] x0 y0 x1 y1); Plot.(text ~h ~spec:[ RGB (0,255,0) ] 2.3 (y0-.0.2) ("pattern: " ^ (string_of_int i))) ) |> ignore; Plot.output h >- : unit = ()  ## 折线图 在了解了这些绘图元素之后，在本章的其余部分中，我们将演示一些支持的不同类型的绘图。折线图是最基本的功能。您可以在函数中指定颜色、标记和线型。 let x = Mat.linspace 0. 2. 100 in let y0 = Mat.sigmoid x in let y1 = Mat.map Maths.sin x in let h = Plot.create "plot_022.png" in Plot.(plot ~h ~spec:[ RGB (255,0,0); LineStyle 1; Marker "#[0x2299]"; MarkerSize 8. ] x y0); Plot.(plot ~h ~spec:[ RGB (0,255,0); LineStyle 2; Marker "#[0x0394]"; MarkerSize 8. ] x y1); Plot.(legend_on h ~position:SouthEast [|"sigmoid"; "sine"|]); Plot.output h >- : unit = ()  ## 散点图 接下来是散点图。与折线图类似，您可以指定标记类型和标记大小。下面的示例实际上显示了各种标记的模式。它们由不同的 id 引用。 let x = Mat.uniform 1 30 in let y = Mat.uniform 1 30 in let h = Plot.create ~m:3 ~n:3 "plot_006.png" in Plot.set_background_color h 255 255 255; Plot.subplot h 0 0; Plot.(scatter ~h ~spec:[ Marker "#[0x2295]"; MarkerSize 5. ] x y); Plot.subplot h 0 1; Plot.(scatter ~h ~spec:[ Marker "#[0x229a]"; MarkerSize 5. ] x y); Plot.subplot h 0 2; Plot.(scatter ~h ~spec:[ Marker "#[0x2206]"; MarkerSize 5. ] x y); Plot.subplot h 1 0; Plot.(scatter ~h ~spec:[ Marker "#[0x229e]"; MarkerSize 5. ] x y); Plot.subplot h 1 1; Plot.(scatter ~h ~spec:[ Marker "#[0x2217]"; MarkerSize 5. ] x y); Plot.subplot h 1 2; Plot.(scatter ~h ~spec:[ Marker "#[0x2296]"; MarkerSize 5. ] x y); Plot.subplot h 2 0; Plot.(scatter ~h ~spec:[ Marker "#[0x2666]"; MarkerSize 5. ] x y); Plot.subplot h 2 1; Plot.(scatter ~h ~spec:[ Marker "#[0x22a1]"; MarkerSize 5. ] x y); Plot.subplot h 2 2; Plot.(scatter ~h ~spec:[ Marker "#[0x22b9]"; MarkerSize 5. ] x y); Plot.output h >- : unit = ()  ## 阶梯图 阶梯图也称为“阶梯图”，因为它以类似阶梯的曲线绘制给定 ndarray 中的元素。 let x = Mat.linspace 0. 6.5 20 in let y = Mat.map Maths.sin x in let h = Plot.create ~m:1 ~n:2 "plot_007.png" in Plot.set_background_color h 255 255 255; Plot.subplot h 0 0; Plot.plot_fun ~h Maths.sin 0. 6.5; Plot.(stairs ~h ~spec:[ RGB (0,128,255) ] x y); Plot.subplot h 0 1; Plot.(plot ~h ~spec:[ RGB (0,0,0) ] x y); Plot.(stairs ~h ~spec:[ RGB (0,128,255) ] x y); Plot.output h >- : unit = ()  ## 箱线图 箱线图通过四分位数图形地展示数值数据的分组。它常用于描述统计学。 let y1 = Mat.uniform 1 10 in let y2 = Mat.uniform 10 100 in let h = Plot.create ~m:1 ~n:2 "plot_008.png" in Plot.subplot h 0 0; Plot.(bar ~h ~spec:[ RGB (0,153,51); FillPattern 3 ] y1); Plot.subplot h 0 1; Plot.(boxplot ~h ~spec:[ RGB (0,153,51) ] y2); Plot.output h >- : unit = ()  ## 茎叶图 茎叶图很简单，如下代码所示。 let x = Mat.linspace 0.5 2.5 25 in let y = Mat.map (Stats.exponential_pdf ~lambda:0.1) x in let h = Plot.create ~m:1 ~n:2 "plot_009.png" in Plot.set_background_color h 255 255 255; Plot.subplot h 0 0; Plot.set_foreground_color h 0 0 0; Plot.stem ~h x y; Plot.subplot h 0 1; Plot.(stem ~h ~spec:[ Marker "#[0x2295]"; MarkerSize 5.; LineStyle 1 ] x y); Plot.output h >- : unit = ()  茎叶图常用于显示变量的自相关性，因此 Plot 模块已经包含了方便的 autocorr let x = Mat.linspace 0. 8. 30 in let y0 = Mat.map Maths.sin x in let y1 = Mat.uniform 1 30 in let h = Plot.create ~m:1 ~n:2 "plot_010.png" in Plot.subplot h 0 0; Plot.set_title h "Sine"; Plot.autocorr ~h y0; Plot.subplot h 0 1; Plot.set_title h "Gaussian"; Plot.autocorr ~h y1; Plot.output h >- : unit = ()  ## 面积图 面积图类似于线图，但它填充了线和 x 轴之间的空间，如下所示。 let x = Mat.linspace 0. 8. 100 in let y = Mat.map Maths.atan x in let h = Plot.create ~m:1 ~n:2 "plot_011.png" in Plot.subplot h 0 0; Plot.(area ~h ~spec:[ FillPattern 1 ] x y); let x = Mat.linspace 0. (2. *. 3.1416) 100 in let y = Mat.map Maths.sin x in Plot.subplot h 0 1; Plot.(area ~h ~spec:[ FillPattern 2 ] x y); Plot.output h >- : unit = ()  ## 直方图和累积分布函数图 直方图是数据可视化中最常用的图之一。给定一系列测量值，您可以使用 histogramecdf 绘图函数轻松绘制数据的直方图和经验累积分布。 let x = Mat.gaussian 200 1 in let h = Plot.create ~m:1 ~n:2 "plot_012.png" in Plot.subplot h 0 0; Plot.set_title h "直方图"; Plot.histogram ~h ~bin:25 x; Plot.subplot h 0 1; Plot.set_title h "经验累积分布函数"; Plot.ecdf ~h x; Plot.output h >- : unit = ()  ## 对数图 在 Owl 绘图中，您可以选择在 x 轴和/或 y 轴上使用对数刻度。 let x = Mat.logspace (-1.5) 2. 50 in let y = Mat.map Maths.exp x in let h = Plot.create ~m:2 ~n:2 "plot_013.png" in Plot.subplot h 0 0; Plot.set_xlabel h "Input Data X"; Plot.set_ylabel h "Input Data Y"; Plot.(loglog ~h ~spec:[ RGB (0,255,0); LineStyle 2; Marker "+" ] ~x:x y); Plot.subplot h 0 1; Plot.set_xlabel h "Index of Input Data Y"; Plot.set_ylabel h "Input Data Y"; Plot.(loglog ~h ~spec:[ RGB (0,0,255); LineStyle 1; Marker "*" ] y); Plot.subplot h 1 0; Plot.set_xlabel h "Input Data X"; Plot.set_ylabel h "Input Data Y"; Plot.semilogx ~h ~x:x y; Plot.subplot h 1 1; Plot.set_xlabel h "Index of Input Data Y"; Plot.set_ylabel h "Input Data Y"; Plot.semilogy ~h y; Plot.output h >- : unit = ()  ## 3D图 我们已经看到了上面的3D图的示例。在Plot模块中有四个与3D图相关的函数。它们是surfmeshheatmapcontour函数。首先，让我们看一下meshsurf函数。 let x, y = Mat.meshgrid (-2.5) 2.5 (-2.5) 2.5 100 100 in let z0 = Mat.(sin ((x **$ 2.) + (y **$2.))) in let z1 = Mat.(cos ((x **$ 2.) + (y **$2.))) in let h = Plot.create ~m:2 ~n:2 "plot_014.png" in Plot.subplot h 0 0; Plot.surf ~h x y z0; Plot.subplot h 0 1; Plot.mesh ~h x y z0; Plot.subplot h 1 0; Plot.surf ~h x y z1; Plot.subplot h 1 1; Plot.mesh ~h x y z1; Plot.output h >- : unit = ()  使用altitudeazimuth参数轻松控制视角。下面是一个例子。 let x, y = Mat.meshgrid (-2.5) 2.5 (-2.5) 2.5 100 100 in let z = Mat.(sin ((x * x) + (y * y))) in let h = Plot.create ~m:1 ~n:3 "plot_015.png" in Plot.subplot h 0 0; Plot.(mesh ~h ~spec:[ Altitude 50.; Azimuth 120. ] x y z); Plot.subplot h 0 1; Plot.(mesh ~h ~spec:[ Altitude 65.; Azimuth 120. ] x y z); Plot.subplot h 0 2; Plot.(mesh ~h ~spec:[ Altitude 80.; Azimuth 120. ] x y z); Plot.output h >- : unit = ()  生成的图如下所示。 这里是另一个类似的例子，使用不同的函数。 let x, y = Mat.meshgrid (-3.) 3. (-3.) 3. 50 50 in let z = Mat.( 3.$* ((1. $- x) **$ 2.) * exp (neg (x **$2.) - ((y +$ 1.) **$2.)) - (10.$* (x /$5. - (x **$ 3.) - (y **$5.)) * (exp (neg (x **$ 2.) - (y **$2.)))) - ((1./.3.)$* exp (neg ((x +$1.) **$ 2.) - (y **$2.))) ) in let h = Plot.create ~m:2 ~n:3 "plot_016.png" in Plot.subplot h 0 0; Plot.surf ~h x y z; Plot.subplot h 0 1; Plot.mesh ~h x y z; Plot.subplot h 0 2; Plot.(surf ~h ~spec:[ Contour ] x y z); Plot.subplot h 1 0; Plot.(mesh ~h ~spec:[ Contour; Azimuth 115.; NoMagColor ] x y z); Plot.subplot h 1 1; Plot.(mesh ~h ~spec:[ Azimuth 115.; ZLine X; NoMagColor; RGB (61,129,255) ] x y z); Plot.subplot h 1 2; Plot.(mesh ~h ~spec:[ Azimuth 115.; ZLine Y; NoMagColor; RGB (130,255,40) ] x y z); Plot.output h >- : unit = ()  ## 热图和等高线图 最后，让我们看一下使用与之前相同的函数的热图和等高线图的外观。 let x, y = Mat.meshgrid (-3.) 3. (-3.) 3. 100 100 in let z = Mat.( 3.$* ((1. $- x) **$ 2.) * exp (neg (x **$2.) - ((y +$ 1.) **$2.)) - (10.$* (x /$5. - (x **$ 3.) - (y **$5.)) * (exp (neg (x **$ 2.) - (y **$2.)))) - ((1./.3.)$* exp (neg ((x +$1.) **$ 2.) - (y **$2.))) ) in let h = Plot.create ~m:2 ~n:2 "plot_017.png" in Plot.subplot h 0 0; Plot.(mesh ~h ~spec:[ Contour ] x y z); Plot.subplot h 0 1; Plot.heatmap ~h x y z; Plot.subplot h 1 0; Plot.mesh ~h x y z; Plot.subplot h 1 1; Plot.contour ~h x y z; Plot.output h >- : unit = ()  ## 高级统计图 除了这些常用的基本图类型外，我们还支持几种高级统计图。例如，qqplotprobplot都是用于确定数据集是否来自某个特定分布的简单图形测试。 qqplot显示样本数据y的分位数与给定分布的理论分位数值，或样本数据x的分位数的量子-量子图。以下是一个例子。 let y = Mat.(gaussian 100 1 *$ 10.) in
let x = Mat.gaussian 200 1 in
let h = Plot.create ~m:2 ~n:2 "plot_018.png" in

Plot.subplot h 0 0;
Plot.set_title h "Gaussian vs. Gaussian Sample";
Plot.set_ylabel h "Quantiles of Input Sample";
Plot.set_xlabel h "Normal Distribution Quantiles";
Plot.qqplot ~h y ~x:x;

Plot.subplot h 0 1;
Plot.set_title h "Gaussian vs. Default Dist";
Plot.set_ylabel h "Quantiles of Input Sample";
Plot.set_xlabel h "Normal Distribution Quantiles";
Plot.(qqplot ~h y ~spec:[RGB (0,128,255)]);

Plot.subplot h 1 0;
Plot.set_title h "Gaussian vs. Rayleigh Dist";
Plot.set_ylabel h "Quantiles of Input Sample";
Plot.set_xlabel h "Rayleigh Distribution (sigma=0.5) Quantiles";
Plot.qqplot ~h y ~pd:(fun p -> Stats.rayleigh_ppf p 0.5);

Plot.subplot h 1 1;
Plot.set_title h "Gaussian vs. Chi-Square Dist";
Plot.set_ylabel h "Quantiles of Input Sample";
Plot.set_xlabel h "Chi-Square Distribution (k=10) Quantiles";
Plot.qqplot ~h y ~pd:(fun p -> Stats.chi2_ppf p 10.);

Plot.output h
>- : unit = ()


probplot类似于qqplot。它包含两种特殊情况：normplot用于给定的理论分布为正态分布，以及wblplot用于威布尔分布。以下是它们的示例。

let x = Mat.empty 200 1 |> Mat.map (fun _ -> Stats.weibull_rvs 1.2 1.5) in
let h = Plot.create ~m:1 ~n:2 "plot_019.png" in

Plot.subplot h 0 0;
Plot.set_title h "随机威布尔样本 vs. 标准正态分布";
Plot.set_xlabel h "样本数据";
Plot.set_ylabel h "理论正态分布";
Plot.normplot ~h x;

Plot.subplot h 0 1;
Plot.set_title h "随机威布尔样本 vs. 威布尔分布";
Plot.set_xlabel h "样本数据";
Plot.set_ylabel h "理论威布尔分布";
Plot.wblplot ~h ~lambda:1.2 ~k:1.5 x;
Plot.output h
>- : unit = ()