OxyPlotのインストール方法や使い方については、こちらの記事に記載しましたが、使用頻度が高そうなグラフについて簡単に描画が出来るように関数化(メソッド化)してみました。
引数にOxyPlotのコントロールと描画したいデータを渡すと描画してくれます。
後々に拡張や修正がしなすいよう、必要最小限のプロパティを使っていますので、ご自身の用途に応じてカスタマイズして下さい。
グラフ(チャート)の種類
この関数(メソッド)を使えば、次のグラフ(チャート)が簡単に描画できます。
サンプルプログラム
サンプルプログラムを例に、OxyPlotの使い方を解説していきたいと思います。
画面レイアウト
チャート1~チャート3までタブで分かれていて、合計9個のOxyPlotコントロールを張り付けています。
描画ボタンをクリックすると、全てのチャートが描画される仕様です。
サンプルのダウンロード場所
サンプルプログラムのソースは下記からダウンロードできるようになっています。
今回は Nuget で取得した OxyPlot.Wpf.2.0.0 のライブラリも同梱していますので、任意のフォルダに解凍の上、Visual Stduio 2019 でプロジェクトを開けば、ビルドできると思います。
OxyPlotTestプロジェクト一式
関数(メソッド)の使い方
ScottPlotを関数化した時と同様、グラフ描画はDrowから始まるメソッド名になっています。
チャート(グラフ)名 | 関数(メソッド)名 | 内容 |
---|---|---|
折れ線グラフ | DrawLine | 単一の折れ線グラフを描画 |
折れ線グラフ(複数) | DrawLine | 複数の折れ線グラフを描画 |
散布図 | DrawScatt | 散布図を描画 |
円グラフ | DrawPie | 円グラフを描画 |
縦棒グラフ | DrawColumn | 縦棒グラフを描画 |
横棒グラフ | DrawBar | 横棒グラフを描画 |
棒グラフ(グルーピング) | DrawGroupColumn | 複数の棒グラフをグルーピングして描画 |
積み上げグラフ | DrawStack | 積み上げグラフを描画 |
箱ひげ図 | DrawPopulations | 箱ひげ図を描画 |
第一引数にコントロール、第2引数にタイトル、第三引数以降に描画データ(プロットデータ)を渡します。
コントロール名が "uxChart1" で、xs にX方向のデータ、ysにY方向のデータが格納されている場合は次の様になります。
//グラフの描画
DrawLine(uxChart1,"折れ線グラフ",xs, ys);
第三引数であるプロットデータには、大きく分けて2種類の形式があります。
1つは、プロットデータの配列を渡す形式で、折れ線や棒グラフ、円グラフ、散布図の時に使用します。
もう1つは、複数の折れ線グラフ、グルーピングした棒グラフ、積み上げグラフなどのように1つのチャートコントロールに複数のグラフを描画する場合で、こちらはタプルをリスト形式で渡すようになっています。
詳しくはソースコードを見て頂くとして、ここではポイント説明しておきます。
単独の折れ線グラフ、散布図
これらはプロットデータを配列形式で引数に渡します。
例えば、折れ線グラフの場合は次の様になります。
DrawLine(uxChart1,"折れ線グラフ",xs, ys);
箱ひげ図
箱ひげ図については、単純に1次元配列のデータを引数に渡すだけで描画するようになっています。
DrawPopulations(uxChart1,"箱ひげ図",ys);
OxyPlotでは箱ひげ図に必要な統計情報を計算するメソッドが存在しないため、Webサイトに公開されている統計情報計算用のソースコードをクラス化し、DrawPopulations の中で計算しています。
縦棒グラフ、横棒グラフ
縦棒グラフ、横棒グラフともプロットデータとして2つ渡すのは折れ線や散布図と同じですが、横軸がdouble型配列ではなく、文字列配列になっているところが異なる点です。
//棒グラフ
DrawBar(uxChart6, "横棒グラフ", labels, values);
円グラフ
円グラフは、ラベルと対応する値の配列を描画データとして渡します。
//円グラフのデータ
double[] values = { 778, 43, 283, 76, 184 };
string[] labels = { "C#", "JAVA", "Python", "F#", "PHP" };
//円グラフの描画
DrawPie(uxChart4, "円グラフ", labels, values);
複数の折れ線グラフ
タプルのリスト形式で描画データを指定します。
タプルで、凡例タイトル(legend)、X座標の配列、y座標の配列を1まとまりにして、リストに追加していき、そのリストを引数に渡します。
List<(string legend, double[] xs, double[] ys)> linedata = new List<(string legend, double[] xs, double[] ys)>();
linedata.Add(("legend1", xs, ys1));
linedata.Add(("legend2", xs, ys2));
linedata.Add(("legend3", xs, ys3));
DrawLine(uxChart2, "折れ線グラフ(複数)", linedata);
縦棒グラフ(グルーピング)、積み上げグラフ
これらは複数の値をグループ化して、まとめて表示するチャートであるため、タプルのリスト形式でプロットデータを設定します。
グループ化が少し分かりづらいかもしれませんが、項目名と内訳と考えてもらえれば分かりやすいかと思います。
OxyChartもLiveChartsやScottPlotと同様、項目名と内訳という考え方でプロットデータをセットすれば良いようにしています。
//グルーピングのデータ作成
string[] group = { "コア数", "クロック数", "スレッド数", "キャッシュ", "価格" };
List<(string, double[])> datas = new List<(string, double[])>();
datas.Add(("Core-i7", new double[] { 8, 3.6, 16, 64, 70 }));
datas.Add(("Core-i5", new double[] { 6, 2.8, 12, 32, 50 }));
datas.Add(("Core-i3", new double[] { 4, 2.0, 8, 16, 30 }));
datas.Add(("Pentium", new double[] { 2, 1.8, 4, 8, 10 }));
datas.Add(("Celeron", new double[] { 2, 1.5, 4, 8, 5 }));
//棒グラフ(グルーピング)
DrawGroupColumn(uxChart1, "グループ", group, datas);
このサンプルソース上では group という変数名を使っていますが、明細(detail) とか、内訳(breakdown) という変数名の方が分かりやすかったかもしれませんね。
ソースコード
サンプルプロジェクトをダウンロード&解凍したあと、プロジェクトをVisual Studioで開くと、次のレイアウトが表示されます。
タブページが3つあり、1ページ目と2ページ目には4つ、最後の3ページ目には1つ、合計9個のOxyPlotのコントロールを張り付けていますが、デザイナー上では特に何も表示されず、GirdやStackPanelの様に描画エリアだけが確保された状態になっています。
サンプルプログラムのXAMLのソースコードは次の様になっています。
<Window x:Class="OxyPlotTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:OxyPlotTest"
xmlns:oxy="http://oxyplot.org/wpf"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="1320">
<Grid>
<TabControl>
<TabItem Header="チャート1">
<Grid>
<oxy:PlotView x:Name="uxChart1" Width="300" Margin="20,53,986,23" />
<oxy:PlotView x:Name="uxChart2" Width="300" Margin="344,53,662,23" />
<oxy:PlotView x:Name="uxChart3" Width="300" Margin="668,53,338,23" />
<oxy:PlotView x:Name="uxChart4" Width="300" Margin="991,50,15,26" />
</Grid>
</TabItem>
<TabItem Header="チャート2">
<Grid>
<oxy:PlotView x:Name="uxChart5" Width="300" Margin="20,53,986,23"/>
<oxy:PlotView x:Name="uxChart6" Width="300" Margin="344,53,662,23"/>
<oxy:PlotView x:Name="uxChart7" Width="300" Margin="668,53,338,23"/>
<oxy:PlotView x:Name="uxChart8" Width="300" Margin="991,50,15,26"/>
</Grid>
</TabItem>
<TabItem Header="チャート3">
<Grid>
<oxy:PlotView x:Name="uxChart9" Width="300" Margin="20,53,986,23"/>
</Grid>
</TabItem>
</TabControl>
<Button Content="描画" HorizontalAlignment="Left" Margin="1212,32,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</Grid>
</Window>
次にC#のソースコードを見てみましょう。
ソースを読み解く上で、ScottPlotの描画手順の全体像を知っていると理解が深まりますので、もしScottPlotが初めての方は、こちらの記事も是非お読み下さい。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using OxyPlot;
using OxyPlot.Series;
using OxyPlot.Axes;
namespace OxyPlotTest
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Random rand = new Random();
//折れ線グラフのデータ作成
double[] xs = Enumerable.Range(0, 100).Select(i => (double)i).ToArray();
double[] ys = Enumerable.Range(0, 100).Select(i => (double)(i + rand.Next(0, 100) * ((rand.Next(1, 2) == 1) ? 1 : -1))).ToArray();
double[] ys1 = Enumerable.Range(0, 100).Select(i => Math.Sin(i * 0.1)).ToArray();
double[] ys2 = Enumerable.Range(0, 100).Select(i => 2 + Math.Cos(i * 0.1)).ToArray();
double[] ys3 = Enumerable.Range(0, 100).Select(i => 4 + Math.Sin(i * 0.1) * Math.Cos(i * 0.1)).ToArray();
//グラフの描画
DrawLine(uxChart1, "折れ線グラフ", xs, ys);
List<(string legend, double[] xs, double[] ys)> linedata = new List<(string legend, double[] xs, double[] ys)>();
linedata.Add(("legend1", xs, ys1));
linedata.Add(("legend2", xs, ys2));
linedata.Add(("legend3", xs, ys3));
DrawLine(uxChart2, "折れ線グラフ(複数)", linedata);
//散布図
DrawScatt(uxChart3, "散布図", xs, ys);
//円グラフ
double[] values = { 778, 43, 283, 76, 184 };
string[] labels = { "C#", "JAVA", "Python", "F#", "PHP" };
DrawPie(uxChart4, "円グラフ", labels, values);
//縦棒グラフ
DrawColumn(uxChart5, "縦棒グラフ", labels, values);
//横棒グラフ
DrawBar(uxChart6, "横棒グラフ", labels, values);
//グルーピングのデータ作成
string[] items = { "コア数", "クロック数", "スレッド数", "キャッシュ", "価格" };
List<(string, double[])> datas = new List<(string, double[])>();
datas.Add(("i7", new double[] { 8, 3.6, 16, 64, 70 }));
datas.Add(("i5", new double[] { 6, 2.8, 12, 32, 50 }));
datas.Add(("i3", new double[] { 4, 2.0, 8, 16, 30 }));
datas.Add(("Pen", new double[] { 2, 1.8, 4, 8, 10 }));
datas.Add(("Cel", new double[] { 2, 1.5, 4, 8, 5 }));
//棒グラフ(グルーピング)
DrawGroupColumn(uxChart7, "グループ", items, datas);
//積み上げグラフ
DrawStack(uxChart8, "積み上げ", items, datas);
//箱ひげ図
DrawPopulations(uxChart9, "箱ひげ", ys);
}
/// <summary>
/// 折れ線グラフ
/// </summary>
/// <param name="chart"></param>
/// <param name="title"></param>
/// <param name="xs"></param>
/// <param name="ys"></param>
private void DrawLine(OxyPlot.Wpf.PlotView chart, string title, double[] xs, double[] ys)
{
PlotModel model = new PlotModel() { Title = title };
model.Axes.Add(new OxyPlot.Axes.LinearAxis() { Position = AxisPosition.Bottom, MajorGridlineStyle = LineStyle.Automatic, MinorGridlineStyle = LineStyle.Dash });
model.Axes.Add(new OxyPlot.Axes.LinearAxis() { Position = AxisPosition.Left, MajorGridlineStyle = LineStyle.Automatic, MinorGridlineStyle = LineStyle.Dash });
LineSeries seri = new LineSeries();
seri.Points.AddRange(Enumerable.Range(0, ys.Length).Select(i => new DataPoint(xs[i], ys[i])));
model.Series.Add(seri);
chart.Model = model;
}
/// <summary>
/// 折れ線グラフ(複数)
/// </summary>
/// <param name="chart"></param>
/// <param name="title"></param>
/// <param name="linedata"></param>
private void DrawLine(OxyPlot.Wpf.PlotView chart, string title, List<(string legend, double[] xs, double[] ys)> linedata)
{
PlotModel model = new PlotModel() { Title = title };
model.LegendPosition = LegendPosition.RightMiddle;
model.LegendPlacement = LegendPlacement.Outside;
model.Axes.Add(new OxyPlot.Axes.LinearAxis() { Position = AxisPosition.Bottom, MajorGridlineStyle = LineStyle.Automatic, MinorGridlineStyle = LineStyle.Dash });
model.Axes.Add(new OxyPlot.Axes.LinearAxis() { Position = AxisPosition.Left, MajorGridlineStyle = LineStyle.Automatic, MinorGridlineStyle = LineStyle.Dash });
foreach (var data in linedata)
{
LineSeries seri = new LineSeries();
seri.Title = data.legend;
seri.Points.AddRange(Enumerable.Range(0, data.ys.Length).Select(i => new DataPoint(data.xs[i], data.ys[i])));
model.Series.Add(seri);
}
chart.Model = model;
}
/// <summary>
/// 散布図
/// </summary>
/// <param name="chart"></param>
/// <param name="title"></param>
/// <param name="xs"></param>
/// <param name="ys"></param>
private void DrawScatt(OxyPlot.Wpf.PlotView chart, string title, double[] xs, double[] ys)
{
PlotModel model = new PlotModel() { Title = title };
model.Axes.Add(new OxyPlot.Axes.LinearAxis() { Position = AxisPosition.Bottom, MajorGridlineStyle = LineStyle.Automatic, MinorGridlineStyle = LineStyle.Dash });
model.Axes.Add(new OxyPlot.Axes.LinearAxis() { Position = AxisPosition.Left, MajorGridlineStyle = LineStyle.Automatic, MinorGridlineStyle = LineStyle.Dash });
ScatterSeries seri = new ScatterSeries();
seri.Points.AddRange(Enumerable.Range(0, ys.Length).Select(i => new ScatterPoint(xs[i], ys[i])));
seri.MarkerSize = 3;
seri.MarkerType = MarkerType.Circle;
model.Series.Add(seri);
chart.Model = model;
}
/// <summary>
/// 円グラフ
/// </summary>
/// <param name="chart"></param>
/// <param name="title"></param>
/// <param name="labels"></param>
/// <param name="values"></param>
private void DrawPie(OxyPlot.Wpf.PlotView chart, string title, string[] labels, double[] values)
{
PlotModel model = new PlotModel() { Title = title };
PieSeries seri = new PieSeries();
for (int i = 0; i < labels.Length; i++)
{
seri.Slices.Add(new PieSlice(labels[i], values[i]));
}
model.Series.Add(seri);
chart.Model = model;
}
/// <summary>
/// 縦棒グラフ
/// </summary>
/// <param name="chart"></param>
/// <param name="title"></param>
/// <param name="labels"></param>
/// <param name="ys"></param>
private void DrawColumn(OxyPlot.Wpf.PlotView chart, string title, string[] labels, double[] ys)
{
PlotModel model = new PlotModel() { Title = title };
model.Axes.Add(new OxyPlot.Axes.LinearAxis() { Position = AxisPosition.Left, MajorGridlineStyle = LineStyle.Automatic, MinorGridlineStyle = LineStyle.Dash });
model.Axes.Add(new CategoryAxis() { ItemsSource = labels, Position = AxisPosition.Bottom, MajorGridlineStyle = LineStyle.Automatic, MinorGridlineStyle = LineStyle.Dash });
ColumnSeries seri = new ColumnSeries();
seri.ItemsSource = ys.Select(i=>new ColumnItem(i));
model.Series.Add(seri);
chart.Model = model;
}
/// <summary>
/// 横棒グラフ
/// </summary>
/// <param name="chart"></param>
/// <param name="title"></param>
/// <param name="labels"></param>
/// <param name="ys"></param>
private void DrawBar(OxyPlot.Wpf.PlotView chart, string title, string[] labels, double[] ys)
{
PlotModel model = new PlotModel() { Title = title };
model.Axes.Add(new OxyPlot.Axes.LinearAxis() { Position = AxisPosition.Bottom, MajorGridlineStyle = LineStyle.Automatic, MinorGridlineStyle = LineStyle.Dash });
model.Axes.Add(new CategoryAxis()
{
ItemsSource = labels,
Position = AxisPosition.Left,
MajorGridlineStyle = LineStyle.Automatic,
MinorGridlineStyle = LineStyle.Dash
});
BarSeries seri = new BarSeries();
seri.ItemsSource = ys.Select(i => new BarItem(i));
model.Series.Add(seri);
chart.Model = model;
}
/// <summary>
/// 箱ひげ図
/// </summary>
/// <param name="chart"></param>
/// <param name="title"></param>
/// <param name="ys"></param>
private void DrawPopulations(OxyPlot.Wpf.PlotView chart, string title, double[] ys)
{
var inf = new statistics(ys);
PlotModel model = new PlotModel() { Title = title };
model.LegendPosition = LegendPosition.RightMiddle;
model.LegendPlacement = LegendPlacement.Outside;
model.Axes.Add(new OxyPlot.Axes.LinearAxis() { Position = AxisPosition.Left, MajorGridlineStyle = LineStyle.Automatic, MinorGridlineStyle = LineStyle.Dash });
model.Axes.Add(new OxyPlot.Axes.LinearAxis() { IsAxisVisible = false, Position = AxisPosition.Bottom });
BoxPlotSeries seri = new BoxPlotSeries();
var plot = new BoxPlotItem(0, inf.LowerWhisker, inf.FirstQuartil, inf.Median, inf.ThirdQuartil, inf.UpperWhisker);
seri.Items.Add(plot);
model.Series.Add(seri);
chart.Model = model;
}
/// <summary>
/// 縦棒グラフ(グループ)
/// </summary>
/// <param name="chart"></param>
/// <param name="title"></param>
/// <param name="labels"></param>
/// <param name="values"></param>
private void DrawGroupColumn(OxyPlot.Wpf.PlotView chart, string title, string[] labels, List<(string legend, double[] ys)> values)
{
PlotModel model = new PlotModel() { Title = title };
model.LegendPosition = LegendPosition.RightMiddle;
model.LegendPlacement = LegendPlacement.Outside;
model.Axes.Add(new OxyPlot.Axes.LinearAxis() { Position = AxisPosition.Left, MajorGridlineStyle = LineStyle.Automatic, MinorGridlineStyle = LineStyle.Dash });
model.Axes.Add(new CategoryAxis()
{
ItemsSource = values.Select(i => i.legend).ToArray(),
Position = AxisPosition.Bottom,
MajorGridlineStyle = LineStyle.Automatic,
MinorGridlineStyle = LineStyle.Dash
});
for (int n = 0; n < values[0].ys.Length; n++)
{
ColumnSeries seri = new ColumnSeries();
seri.Title = labels[n];
seri.ItemsSource = Enumerable.Range(0, values.Count).Select(i => new ColumnItem(values[i].ys[n])).ToArray();
model.Series.Add(seri);
}
chart.Model = model;
}
/// <summary>
/// 積み上げグラフ
/// </summary>
/// <param name="chart"></param>
/// <param name="title"></param>
/// <param name="labels"></param>
/// <param name="values"></param>
private void DrawStack(OxyPlot.Wpf.PlotView chart, string title, string[] labels, List<(string legend, double[] ys)> values)
{
PlotModel model = new PlotModel() { Title = title };
model.LegendPosition = LegendPosition.RightMiddle;
model.LegendPlacement = LegendPlacement.Outside;
model.LegendItemOrder = LegendItemOrder.Reverse;
model.Axes.Add(new OxyPlot.Axes.LinearAxis() { Position = AxisPosition.Left, MajorGridlineStyle = LineStyle.Automatic, MinorGridlineStyle = LineStyle.Dash });
model.Axes.Add(new CategoryAxis()
{
ItemsSource = values.Select(i => i.legend).ToArray(),
Position = AxisPosition.Bottom,
MajorGridlineStyle = LineStyle.Automatic,
MinorGridlineStyle = LineStyle.Dash
});
var len = values[0].ys.Length;
for (int n = 0; n < len; n++)
{
ColumnSeries seri = new ColumnSeries() { IsStacked = true };
seri.Title = labels[len - n - 1];
seri.ItemsSource = Enumerable.Range(0, values.Count).Select(i => new ColumnItem(values[i].ys[len - n - 1])).ToArray();
model.Series.Add(seri);
}
chart.Model = model;
}
}
/// <summary>
/// 統計情報の取得クラス
/// </summary>
public class statistics
{
public double Median { get; set; }
public double Mean { get; set; }
public double FirstQuartil { get; set; }
public double ThirdQuartil { get; set; }
public double LowerWhisker { get; set; }
public double UpperWhisker { get; set; }
public double[] Outliers { get; set; }
/// <summary>
/// コンストラクタ
/// https://github.com/ylatuya/oxyplot/blob/master/Source/Examples/ExampleLibrary/Examples/BoxPlotSeriesExamples.cs
/// </summary>
/// <param name="values"></param>
public statistics(double[] values)
{
Array.Sort(values);
Median = CalcMedian(values);
Mean = values.Average();
int r = values.Length % 2;
FirstQuartil = CalcMedian(values.Take((values.Length + r) / 2).ToArray());
ThirdQuartil = CalcMedian(values.Skip((values.Length - r) / 2).ToArray());
var step = (ThirdQuartil - FirstQuartil) * 1.5;
UpperWhisker = ThirdQuartil + step;
UpperWhisker = values.Where(i => i <= UpperWhisker).Max();
var LowerWhisker = FirstQuartil - step;
LowerWhisker = values.Where(i => i >= LowerWhisker).Min();
Outliers = values.Where(i => i > UpperWhisker || i < LowerWhisker).ToArray();
double CalcMedian(double[] v)
{
Array.Sort(v);
return (v.Length % 2 == 1) ? v[(v.Length - 1) / 2] : 0.5 * v[v.Length / 2] + 0.5 * v[(v.Length / 2) - 1];
}
}
}
}
ソースの解説(ポイント)
ソースコードを見て頂くと使い方は分かると思いますので、ポイントだけ解説しておきます。
OxyPlotのグラフ描画で一番簡単なサンプルは次の様になります。
//PlotModelのインスタンスを生成
PlotModel model = new PlotModel();
//タイトルを設定
model.Title(title);
//シリーズの生成
LineSeries seri = new LineSeries();
//データを渡して折れ線グラフを作成
seri.Points.AddRange(Enumerable.Range(0, ys.Length).Select(i => new DataPoint(xs[i], ys[i])));
//PlotModelにシリーズを登録
model.Series.Add(seri);
//OxyChartコントロールのModelメソッドにPlotModelを登録
uxChart1.Model = model;
このサンプルは折れ線グラフを描画する場合のものですが、目的に応じたSeriesクラスを使い分けることで、色々なグラフが描画できるようになっています。
縦軸・横軸の設定について
縦軸、横軸はAxisクラスのPositionプロパティで設定します。
例えば、縦軸をグラフの左に、横軸をグラフの下に表示する場合、次の様に指定します。
model.Axes.Add(new OxyPlot.Axes.LinearAxis() { Position = AxisPosition.Left });
model.Axes.Add(new OxyPlot.Axes.LinearAxis() { Position = AxisPosition.Bottom });
LinearAxis() は連続した数値を扱う場合のクラスですが、棒グラフでラベルを表示したい場合は、CategoryAxisを使います。
この時、CategoryAxisのItemSourceプロパティにラベルの配列をセットすると、軸にラベルを表示することができます。
例えば、string[] labels という文字列配列に入ったラベルを縦棒グラフの横軸に表示したい場合、次の様に指定します。
model.Axes.Add(new OxyPlot.Axes.LinearAxis() { Position = AxisPosition.Left });
model.Axes.Add(new OxyPlot.Axes.CategoryAxis() { Position = AxisPosition.Bottom,ItemSource=labels});
補助線についても Axis クラスの MajorGridlineStyle、MinorGridlineStyle で設定します。
OxyPlotでは、デフォルトで縦軸、横軸の補助線を引いてくれませんので、補助線が必要なら次の様に指定して下さい。
//メジャー補助線を自動(=細い実線が引かれる)、マイナー補助線にダッシュ(破線)を指定する例
model.Axes.Add(new OxyPlot.Axes.LinearAxis() { Position = AxisPosition.Left, MajorGridlineStyle = LineStyle.Automatic, MinorGridlineStyle = LineStyle.Dash });
積み上げグラフについて
OxyPlotの場合、積み上げグラフ専用のSeries は用意されておらず、縦棒グラフと同じ ColumnSeriesを使います。
ColumnSeriesの場合と異なるのは、IsStacked プロパティに true を設定する部分です。
また、特になにも考慮せずデータを与えていくと、グラフの下から順に積みあがってしまいます。
凡例の表記を、縦棒グラフ(グループ)と合わせようとすると、下から順に積みあがると逆になってしまうので、PlotModelのLegendItemOrderにLegendItemOrder.Reverse を設定すると同時に、積み上げ部分を少し工夫することで、縦棒グラフ(グループ)の凡例に合わせています。
private void DrawStack(OxyPlot.Wpf.PlotView chart, string title, string[] labels, List<(string legend, double[] ys)> values)
{
PlotModel model = new PlotModel() { Title = title };
model.LegendPosition = LegendPosition.RightMiddle;
model.LegendPlacement = LegendPlacement.Outside;
model.LegendItemOrder = LegendItemOrder.Reverse;
model.Axes.Add(new OxyPlot.Axes.LinearAxis() { Position = AxisPosition.Left, MajorGridlineStyle = LineStyle.Automatic, MinorGridlineStyle = LineStyle.Dash });
model.Axes.Add(new CategoryAxis()
{
ItemsSource = values.Select(i => i.legend).ToArray(),
Position = AxisPosition.Bottom,
MajorGridlineStyle = LineStyle.Automatic,
MinorGridlineStyle = LineStyle.Dash
});
var len = values[0].ys.Length;
for (int n = 0; n < len; n++)
{
ColumnSeries seri = new ColumnSeries() { IsStacked = true };
seri.Title = labels[len - n - 1];
seri.ItemsSource = Enumerable.Range(0, values.Count).Select(i => new ColumnItem(values[i].ys[len - n - 1])).ToArray();
model.Series.Add(seri);
}
chart.Model = model;
}
箱ひげ図用の統計情報の計算
OxyPlotで箱ひげ図を描画するには、BoxPlotSeries を生成し、 BoxPlotItem に値をセットする必要があります。
BoxPlotの最初の引数は連番で、1つのグラフに複数の箱ひげ図を描画する際に使用するのですが、今回は1つだけ描画する仕様のため、0をセットしています。
BoxPlotSeries seri = new BoxPlotSeries();
var plot = new BoxPlotItem(0, inf.LowerWhisker, inf.FirstQuartil, inf.Median, inf.ThirdQuartil, inf.UpperWhisker);
引数の内容と箱ひげ図の対応は以下の通りです。
まとめ
今回はOyxPlotを使って、よく使うチャート(グラフ)が簡単に描画できる関数(メソッド)について解説しました。
OxyPlotは、LiveChartsやScottPlotに比べ、細かな部分までカスタマイズできる印象がありますので、手間さえ掛ければ結構高度なグラフも書けるのではないかと思います。
ただ、公式ドキュメントが整備されていないので、色々なサイトから情報を集める苦労は覚悟しなければなりません。
また、マルチプラットフォームに対応していますので、スマートフォンやタブレット用のグラフ描画も考えるなら、OxyPlotを採用するメリットは高いと思います。
コメント