crossingorder

or crossing order

2021.8.13

数値データからn個のグラフを描く際に,曲線が滑らかになるようにデータを並び替えるMathematica関数

問題の説明

パラメタxに依存するn個の値 y(x) を,m個のxの値 x=x₁, x₂, …, x に対して計算して,xy面にプロットする場合を考える。下図は,そのようなデータ(n=3, m=16)を,(x, y(x))と(x₊₁, y(x₊₁))を線分で結んでプロットした例である。丸点が(x, y(x)),青橙緑の順にn=1,2,3である。この図では,y(x)は値の昇順,y(x)<yₖ₊₁(x) に並んでいる

xで,このようなデータy(x)を並べかえて,(x, y(x))と(x₊₁, y(x₊₁))を線分で結んだときに,次の図のように,折れ線が滑らかになるようにするには,どうしたらよいか。例えば,x=14.73付近で,青と橙の線は,上の図では交差せずに急カーブで反発しているが,下の図では,滑らかに交差している

アルゴリズム

試行錯誤の結果,2次の補間多項式で補外を行うと,安定してそのような滑らかな結び方ができるようである。手順は次の通り:

  1. 最初の3点 (x, y(x)), l=1,2,3 は,滑らかにつながる順に並んでいるとする。

  2. fₗₖ(x)を3点,(x, y(x)), s=l-2, l-1, lを通るxの2次式とする。これにx=x₊₁を代入した値とy(x₊₁)の差の二乗,(y(x₊₁)-fₗₖ(x₊₁))², t=k, k+1, k+2, …, n を最小にするtを探索し,y(x₊₁)を新たにy(x₊₁)とする。すなわち,y(x₊₁)とy(x₊₁)の値を交換する。これを,t=1, 2, …, nの順に繰り返す。

  3. l=3, 4, …, n-1の順に2を行う。

ただし,データ間隔 xₗ₊₁-xがあまり広いと,補間関数の値が暴れるので,線の結び方が変な場合は,データを適宜追加する必要がある。

プログラムリスト

以下は,これをMathematicaの関数で書いたものである。

crossingorder[o_] := Module[

{n = Length[o], ni = Length[o[[1]]], ja, jn, p, f, nf = 2},

ja = Table[Table[j, {j, n}], ni];

jn = Table[j, {j, n}];

Print["data=", ni, ", function=", n, "];

Do[

Do[

f = InterpolatingPolynomial[

Table[o[[ja[[i + k, j]], i + k]], {k, -nf, 0}], o[[1, i + 1, 1]]];

p = OrderingBy[

Table[o[[jn[[k]], i + 1]], {k, j, n}], (# - f)^2 &, 1][[1]] + j - 1;

{jn[[j]], jn[[p]]} = {jn[[p]], jn[[j]]};

, {j, n}

];

ja[[i + 1]] = jn;

, {i, nf + 1, ni - 1}

];

Table[o[[ja[[i, j]], i]], {j, n}, {i, ni}]

]

引数oは,データ(x, y(x))を表すリスト

{ {{x₁, y₁(x₁)}, {x, y₁(x₂)}, …, {{xₘ, y₁(xₘ)}}, {{x₁, y₂(x₁)}, {x₂, y₂(x₂)}, …, {{xₘ, y₂(xₘ)}}, …, {{x₁, yₙ(x_1)}, {x_2, yₙ(x_2)}, …, {{xₘ, yₙ(xₘ)}} }

である。

サンプルコード

oms = {

{{14.62, 0.046773}, {14.64, 0.079457}, {14.66, 0.108559}, {14.68, 0.134723}, {14.7, 0.158429}, {14.72, 0.180047}, {14.74, 0.190402}, {14.76, 0.192134}, {14.78, 0.193817}, {14.8, 0.195453}, {14.85, 0.199346}, {14.9, 0.202971}, {14.95, 0.206341}, {15., 0.209464}, {15.05, 0.212351}, {15.1, 0.215013}},

{{14.62, 0.178733}, {14.64, 0.180867}, {14.66, 0.182911}, {14.68, 0.184878}, {14.7, 0.186778}, {14.72, 0.188618}, {14.74, 0.199868}, {14.76, 0.218126}, {14.78, 0.235012}, {14.8, 0.250685}, {14.85, 0.285374}, {14.9, 0.314832}, {14.95, 0.331745}, {15., 0.335138}, {15.05, 0.337924}, {15.1, 0.340201}},

{{14.62, 0.277765}, {14.64, 0.284036}, {14.66, 0.289629}, {14.68, 0.294649}, {14.7, 0.299179}, {14.72, 0.303285}, {14.74, 0.30702}, {14.76, 0.310427}, {14.78, 0.313544}, {14.8, 0.316402}, {14.85, 0.322577}, {14.9, 0.327615}, {14.95, 0.340157}, {15., 0.362137}, {15.05, 0.381362}, {15.1, 0.398282}}

};

ListLinePlot[oms, Mesh -> Full, MeshStyle -> PointSize[Medium]]

oma = crossingorder[oms];

ListLinePlot[oma, Mesh -> Full, MeshStyle -> PointSize[Medium]]