はじめに
無料で使える高速なグラフライブラリ「ScottPlot」のメジャーアップデートがありました。
しばらくβ版として開発が続いていましたが、ようやく正式版となったようです。
4.0 から 4.1 と2桁目のバージョンアップですが、機能が大幅に追加されたのでご紹介したいと思います。
- バージョンアップ内容
- 個人的に注目の3つの新機能
- ① 高速描画可能な「SignalPlot」の追加
- ② X・Y軸に第2軸 (複合グラフ)を追加する機能
- ③ 直角で表示される「StepPlot」の追加
バージョンアップ内容
主な新機能は以下の通りです。(機械翻訳+意訳)
リリース直後だからなのか、本家サイトにリンクエラーなどがあり全ては調べられていません。
⑤⑥のレンダリングやイベント処理などは、前バージョンにはない機能なので今後調べていきたいと思います。
- 複数のX軸とY軸のサポート
- 軸・目盛り・グリッドの豊富で新しいカスタマイズオプション
- パフォーマンスとスレッドセーフを向上させるステートレスレンダリング
- ユーザー制御用の簡略化された共通構成モジュール
- UIスレッドをブロックしないレンダリングリクエストのサポート
- ユーザーコントロールのカスタマイズを可能にするための改善されたイベント処理
※ 本家サイトからの引用です。
新バージョンの詳細は以下の本家サイトをご覧ください。
https://swharden.com/scottplot/cookbook/
個人的に注目の新機能
個人的にいいなと思う機能は以下の3つです。
- 高速描画可能な「SignalPlot」の新規追加
- X・Y軸に第2軸 (複合グラフ)を追加する機能
- 直角で表示される「StepPlot」の新規追加
① 高速描画可能な「SignalPlot」
4.0までの散布図でも十分に高速でしたが、さらに何百万点ものデータポイントをインタラクティブに表示可能な「SignalPlot」が新たに追加されました。等間隔でのデータを想定しているため、Y軸は固定になっています。
以前記事を書いたリアルタイムでのグラフ描画も、さらに高速に動かせるかもしれません。
深堀して記事かしていきたいと思います。
※ 本家CockBookから引用しています。
旧Ver.のリアルタイム記事は以下を参照ください。
② X・Y軸の第2軸 (複合グラフ)を追加する機能
以前β版の機能として紹介していた、X・Y軸に第2軸を追加する機能が正式リリースされました。
一見地味ですが、「エクセルでいつも見ている、複数データのグラフが作りたい」という時に活躍してくれる機能なので、個人的に非常にありがたいです。
β版の時に紹介した、第2軸を表示する機能の記事は以下をご覧ください。
③ 直角で表示される「StepPlot」の新規追加
個人的に、複数のI/O信号をタイミングチャートのように表示できないか?と思っていたところの新機能でした。うまく使えた場合は記事かしてご紹介したいと思います。
※ 本家CockBookから引用しています
まとめ
無料で使える高速なグラフライブラリ「ScottPlot」のメジャーアップデートについてご紹介しました。
調べきれていない機能・コードの書き方などは、今後掘り下げて別途記事にしていきます。
お知らせ
今月号のSoftware Designは「もっとTypeScriptの力を引き出そう」
JavaScriptの拡張言語でしょ?と思っているかたへ、Union型などTypeScriptの持つ秘めたる力を解説する特集となっています。
個人的には第2特集の「Ubuntuの現代的な使い方」がの方が気になりました。より詳しい内容は以下のリンクからご覧ください。
質問・要望 大歓迎です
「こんな解説記事作って」「こんなことがしたいけど、〇〇で困ってる」など、コメント欄で教えてください。 質問・要望に、中の人ができる限り対応します。
使えたよ・設定できたよの一言コメントも大歓迎。気軽に足跡を残してみてください。記事を紹介したい方はブログ、SNSにバシバシ貼ってもらってOKです。
[ScottPlot]を使ってテキストファイルデータからDataSetにセットしグラフ表示しようとしていますが
y軸は数値なのでList lst6 = dt0.AsEnumerable().Select(row => (double)row[6]).ToList();で変換出来ました。
問題はX軸でDatasetにはds0.Tables[0].Rows[i][0].ToString()←ここに年月日”2021/06/15”
ds0.Tables[0].Rows[i][1].ToString()←ここに時間(時分)”15:47”
double指定出来ず
List lst0 = dt0.AsEnumerable().Select(row => row[0].ToString()).ToList();
List lst1 = dt0.AsEnumerable().Select(row => row[1].ToString()).ToList();
で、しかも年月日と時間を連結してX軸に表示させたいです。
また、対数スケールでしかも複数値(グラフ1、グラフ2、
フラフ3、…)
yanaさん
質問ありがとうございます。記事化を進めていますが、さっと調べてみましたのでお答えします。
・CSVから読み取った文字列型の年月日と時間を結合(「日時の文字列」とします)
・「日時の文字列」を、C#のDateTime型に変換
・DateTimeのToOADate()でDateTime型 -> Double型に変換
・変換したDouble型をScottPlotのX軸のデータ配列にセット。
・ScottPlot.Plot.XAxis.DateTimeFormat(true); でX軸を日付として設定
とすることで、X軸に日時の文字列が表示できるようです(Ver.4.1.16で確認)
※ デフォルトだと、X軸の日付の期間が広いと時間が省略されてしまうようです。
※ 本家サイトCookBook 「Plotting DateTime Data」も参考にしてみてください。
yanaさんへの続き
また、対数スケールで・・(グラフ1、グラフ2、フラフ3、…)以降は文字数制限(?) で見えないようです。
お手数ですが、再度投稿いただけると助かります。
先のX軸「日時の型」変換の回答有難う御座いました。トライしてみます。対数スケール表示での後半の質問です。
double[] dataYsLog = ScottPlot.Tools.Log10(dataYs);
plt.AddScatter(dataXs, dataYsLog, lineWidth: 0);
ここで複数グラフ線(グラフ1,グラフ2,グラフ3,…)と増えた場合のコード記述はどうなりますか?
先程の複数グラフ表示質問は解決しました。
formsPlot1.plt.PlotScatter(x, Y1);
formsPlot1.plt.PlotScatter(x, Y2);と増やせば表示されました。有難うございました。
ただ、以下のようにDouble変換過程でdoubel → double[]を1行でシンプルに記述出来ないかとも思いました。
List lst0 = new List();
for (int i = 0; i < ds0.Tables[0].Rows.Count; i++)//ToOADate → ToDouble
{
string strTime1 = ds0.Tables[0].Rows[i][0] + " " + ds0.Tables[0].Rows[i][1];
DateTime dTime1 = DateTime.Parse(strTime1);
var oadate1 = dTime1.ToOADate();
lst0.Add(Convert.ToDouble(oadate1));
}
List lst6 = dt0.AsEnumerable().Select(row => (double)row[6]).ToList();
List lst7 = dt0.AsEnumerable().Select(row => (double)row[7]).ToList();
List lst8 = dt0.AsEnumerable().Select(row => (double)row[8]).ToList();
List lst9 = dt0.AsEnumerable().Select(row => (double)row[9]).ToList();
double[] myDataY0 = lst0.Select(x => (double)x).ToArray();
double[] myDataY6 = lst6.Select(x => (double)x).ToArray();
double[] myDataY7 = lst7.Select(x => (double)x).ToArray();
double[] myDataY8 = lst8.Select(x => (double)x).ToArray();
double[] myDataY9 = lst9.Select(x => (double)x).ToArray();
formsPlot1.plt.PlotScatter(myDataY0, myDataY6);
formsPlot1.plt.PlotScatter(myDataY0, myDataY7);
formsPlot1.plt.PlotScatter(myDataY0, myDataY8);
formsPlot1.plt.PlotScatter(myDataY0, myDataY9);
formsPlot1.plt.XAxis.DateTimeFormat(true);
logスケールでのy軸のスタート値を0からスタートではなく
1 10 100 1000 10000
と表示したいがスケール変更は可能でしょうか?
yanaさん
「日付の型」と複数グラフの問題は解決したとのことで何よりです。
logスケールについては以下の情報は参考になりませんか?
logスケールは詳しくなく、時間も無くて動作確認もできていないため、的外れな回答をしていたらすみません。
CockBook
LogScale
https://swharden.com/scottplot/cookbooks/4.1.16/category/advanced-axis-features/#log-scale
Manual Tick Label
https://swharden.com/scottplot/cookbooks/4.1.16/category/advanced-axis-features/#manual-tick-labels
Custom Tick Formatter
https://swharden.com/scottplot/cookbooks/4.1.16/category/advanced-axis-features/#custom-tick-formatter
「1始まり」だけについては、AxisLimitsで実現できる(かも)しれません。
https://swharden.com/scottplot/cookbooks/4.1.16/category/quickstart/#axis-labels-and-limits
1)Y軸のLogスケールはScottPlot.Tools.Log10()にて出来ましたがlog10yのY値目盛表示を0 10 100 1000と表現可能でしょうか?
2)X軸をformsPlot1.plt.XAxis.DateTimeFormat(true);日付時間型とすると
X軸値={①7月1日,②7月7日,③7月19日,④8月4日}でのX軸表示は7月1日、7月2日、7月3日 …8月4日と表示されるが
目的のx軸は①、②、③,④と表示されるようにしたい
3)Y軸データで0データをオミット(線形を途切れるようにしたい)は可能ですか?
double[] originalYs = { 3, double.NaN, 5, double.PositiveInfinity, double.NegativeInfinity, 4, 6 };
double[] originalXs = { 1, 2, 3, 4, 5, 6, 7 };
ではうまく行きません。
以下のように個別化すれば可能ですが1万件のデータとなった場合、分別化は難しいです
// first segment
double[] xs1 = { 1, 2, 3 };
double[] ys1 = { 5, 4, 8 };
plt.AddScatter(xs1, ys1, Color.Blue);
// second segment
double[] xs2 = { 5, 6, 7, 8 };
double[] ys2 = { 6, 9, 4, 7 };
plt.AddScatter(xs2, ys2, Color.Blue);
// third segment
double[] xs3 = { 10, 11, 12 };
double[] ys3 = { 8, 3, 7 };
plt.AddScatter(xs3, ys3, Color.Blue);
yanaさん
コメントありがとうございます。
1) 値目盛表示を0 10 100 1000と表現
Logスケールではなく、そのまま 10 100 1000 “だけ” を表示して、中間の値は表示させたくないということかと思います。
動作確認までは見れていないのですが、 2) の質問と同様に Y軸のTickのManual指定でどうでしょうか?
https://swharden.com/scottplot/cookbooks/4.1.16/category/advanced-axis-features/#manual-tick-labels
3)Y軸データで0データをオミット(線形を途切れるようにしたい)は可能ですか?
ScatterPlotでは線を途切れるようにする機能はないようですね。透明化での回避なども試してみましたが、
系列(線)全体での指定しかないため、特定の点のみを無効化する(途切れさせる)のは難しいようです。
Githubで要望をあげてみるのも良いかもしれません。
2)X軸 日付時間型は以下にうまく行きました。すみませんでした。
formsPlot1.plt.XAxis.ManualTickPositions
toolTipを表示する手法はありませんか?
yanaさん、こんにちは。
ScottPlotでは、固定の座標にテキストなどを表示する方法はあるようですが、
カーソルやクリックされた場所に応じて動的に情報を表示する機能はないようです。
MS Chart.HitTestメソッドのようにクリックされたグラフ要素を特定する事が出来るならテキスト表示でもOKかなと思っていますが、ScottPlotでは座標のみしか取得出来ませんよね?
こちらでもトライしてみます。
yanaさん、こんにちは
GetPointNearest()で指定した座標近傍のグラフ要素を取得できるようです。
基本は座標のみですが、pointIndexが取得できるようなので、別途、座標以外を格納したListなどを用意すれば、座標以外の取得もできるかもしれませんね。
クリックではないですが、ScottPlotのFAQに座標の取得方法があったので参考になれば
https://swharden.com/scottplot/faq/mouse-position/#show-value-near-mouse-with-winforms
GetPointNearest()でトライしてみます。
また、コメント頂いたクリックではないポイント座標の取得方法は以前に実施してみましたが線形グラフでは、うまく機能しませんでした。
ScottPlotでの凡例の項目名はWindows Formアプリだと漢字等は表示(英数のみだと表示される)されず、逆にWPFアプリでは凡例項目名は漢字等は表示されます。また、WPFアプリだと画面サイズがWindowsformサイズより小さくなって表示されます。これを同じサイズにする方法もありますか?
すみません!凡例の事象はScottPlotではなくOxyPlotでの事象です。OxyPlotへコメントします。
MS ChartだとYデータの一部に無効値としてDouble.NaNをセットするとその間が飛んでプロットできるのですが
ScottPlotでは、例外になってプロットできませんね。
ScottPlotで欠損値を複数箇所含むようなデータをプロットする方法はサポートされていないのでしょうか?
kazuさん、こんにちは。
コメントありがとうございます。
残念ながら、ScottPlotでは間を空けてプロットする機能はサポートされていません。
次期メジャーバージョンアップ(ScottPlot5?)で再検討とのコメントが開発者からありますが、
大分先の話になりそうですね・・・
https://github.com/ScottPlot/ScottPlot/issues/1161
(最後のコメントの部分)
開発者のコメント読みました。NaNをチェツクしながらのプロットでは速度が落ちるからとの理由のようですね。
ですが、他の人のコメントにもありましたが、長期計測データのプロット時に機器異常によって欠測している箇所が複数存在しているデータを扱うことが多いんですよね。
パフォーマンスが落ちるなら、通常モードとは別にNaN有りデータのプロットモード、ただし速度遅くなる ていうのが追加実装されたら嬉しいんですけど。それまでは、異常値を最低値に入れ替えて連続プロットするか、おとなしくMS Chart使うしかないようですね。
kazuさん
>> 通常モードとは別にNaN有りデータのプロットモード、ただし速度遅くなる ていうのが追加実装されたら嬉しいんですけど。
いいですね。描画が遅くなってもビジュアル的に分かりやすいモードがあってもいい気がします。
(それでもMSChartより十分早くなりそうですし)
コメントを見る限り長期的なToDo(?) には入っていそうなので、GitHubでさらにプッシュしてみるのも良いのかもしれません。
要望も多そうですし、実現するとうれしいですね。