C# LINQで移動平均

LINQを使った,移動平均を計算する関数.

ソースコード

/// <summary>
/// 移動平均を格納した配列を返す
/// </summary>
/// <param name="data">入力データ</param>
/// <param name="num">何個分の平均をとるか</param>
/// <returns>移動平均をとったデータ</returns>
List<double> MovingAverage(
    List<double> data,
    int num)
{
    return Enumerable.Range(0, data.Count - num)
        .Select(i => data.Skip(i).Take(num).Average())
        .ToList();
}

LINQの練習用なので効率が良いかとかは知らない.

Enumerable.Range(0, data.Count - num)

で,0~総データ数-平均を求めるときのデータの個数までの整数のシーケンスを生成する.

data.Skip(i).Take(num).Average()

で,入力データのi番目のデータからnum個データを抽出して平均をとる.

その他

FIRフィルタは以下の記事.
C# LINQでFIRフィルタ - whoopsidaisies's diary

C#でExcelファイルを作成・グラフを挿入する NetOfficeによる方法

C#からExcelファイルを作って,グラフも挿入したい.

以前,以下の記事でClosedXmlというライブラリを使ってC#から簡単にExcelファイルを作る方法を紹介したが,グラフの挿入が出来ないのがグラフの挿入が出来ないのが気になっていた.
C#でExcelファイルを作成する ClosedXmlを使用 - whoopsidaisies's diary

NetOfficeというライブラリを使えば,ClosedXmlと同じくらい簡単にExcelファイルが作成でき,さらにグラフの挿入も行えるので,今回はその方法を紹介する.

NetOfficeのインストール

NetOfficeはMicrosoft Officeにアクセスするための.NETのライブラリで,今回紹介するExcel以外にも,Word, PowerPoint等にもアクセスできるらしい.

インストールはNuget経由で可能.プロジェクトを右クリックして「NuGetパッケージの管理」を選択する.オンラインから検索て「NetOffice.Excel」をインストールする.

f:id:whoopsidaisies:20140619095605p:plain

動作確認

以下,動作確認用のソースコードと生成されたExcelファイルの中身.実行環境ではExcel2010がインストール済み.

class Program
{
    static void Main(string[] args)
    {
        // Excelを開く
        using (var excelApplication = new NetOffice.ExcelApi.Application())
        {
            // ワークブックを追加
            var workBook = excelApplication.Workbooks.Add();
            // ワークシートを取得
            var workSheet = (NetOffice.ExcelApi.Worksheet)workBook.Worksheets[1];

            // データの入力
            workSheet.Cells[1, 2].Value = "ブラウザシェア";
            workSheet.Cells[2, 1].Value = "IE";
            workSheet.Cells[2, 2].Value = "52.56%";
            workSheet.Cells[3, 1].Value = "Chrome";
            workSheet.Cells[3, 2].Value = "24.7%";
            workSheet.Cells[4, 1].Value = "Firefox";
            workSheet.Cells[4, 2].Value = "12.92%";
            workSheet.Cells[5, 1].Value = "その他";
            workSheet.Cells[5, 2].Value = "=1-B2-B3-B4";
                
            // グラフオブジェクトの追加
            var chart = ((NetOffice.ExcelApi.ChartObjects)workSheet.ChartObjects()).Add(70, 100, 375, 225);
            // 円グラフに設定
            chart.Chart.ChartType = NetOffice.ExcelApi.Enums.XlChartType.xlPie;
            // データ範囲を指定
            chart.Chart.SetSourceData(workSheet.Range("A1:B5"));

            // 保存(環境に合わせて拡張子xlsとかでも大丈夫みたい)
            workBook.SaveAs(@"d:\test.xlsx");

            // Excelを終了する
            excelApplication.Quit();
        }
    }
}

出来上がったExcelファイルの中身はこんな感じ.

f:id:whoopsidaisies:20140619094620p:plain

その他

以下のページにグラフ以外のサンプルも多数ある.
NetOffice - MS Office in .NET - Home
【NetOffice】【Excel】NetOfficeのまとめ | 創造的プログラミングと粘土細工(こっちは日本語)

C#でグラフを描く OxyPlotのFunctionSeriesによる方法

.NETフレームワーク用のグラフ描画ライブラリであるOxyPlotを使ってグラフを描く.
その中でもFunctionSeriesクラスを使って与えられた関数のグラフを描く方法を記す.

OxyPlotはWindows Formアプリケーションだけでなく,WPF,Silverlight,Windows8等様々な環境で使える.

Chartコントロールとの比較

Chartコントロールによるグラフの描画方法は以下の別の記事で紹介したことがあるが,比較してOxyPlotの方が良いなと思っている点をあげる.

  • 関数を渡してこの範囲を表示するとかが楽(今回の記事のメイン.詳細は後述.たとえば

y=x^3+2x^2-3-2\le x\le 2の範囲のグラフを描画とか)

  • マウス操作によるグラフの拡大・縮小,移動等の機能が最初からついている
  • ベクター画像できれいにグラフを保存できる(ChartコントロールでもEMFで保存できるがグラフの種類によっては汚い)
  • 配列データ等にバインディングさせて,複数のグラフを描画するとき便利な気がする(今度記事にまとめようと思います)

OxyPlotのインストール

NuGet経由でインストール可能.プロジェクトを右クリックして「NuGetパッケージの管理」を選択する.オンラインから「OxyPlot」を検索.自分に合った環境のパッケージをインストール.(この記事ではOxyPlot.WindowsFormsを使った)

f:id:whoopsidaisies:20140210110630p:plain

Plotコントロールの追加

グラフ描画用のPlotというコントロールが用意されているので使う.

ツールボックスの適当なところで右クリックして「アイテムの選択(I)...」を選択.「.NET Framework コンポーネント」タブの「参照(B)...」ボタンを押して,「(ソリューションフォルダ)\packages\OxyPlot.WindowsForms.yyyy.x.xxx.x\lib\NET45\OxyPlot.WindowsForms.dll」を選択したらOK.

すると,ツールボックスに「Plot」ができるので,Formに配置する.

f:id:whoopsidaisies:20140210111336p:plain

FunctionSeries

OxyPlotには,Func<double, double>のメソッドのグラフを描画してくれるFunctionSeriesクラスがあるのでその使い方を記す.
Func<double, double>という書き方になじみがない人もいるかもしれないので,とりあえず標準正規分布
f\(x\)=\frac{1}{\sqrt{2\pi}}exp\(-\frac{x^{2}}{2}\)
を描くサンプルを以下に示す.

private static double StandardNormalDistribution(double x)
{
    return 1 / Math.Sqrt(2 * Math.PI) * Math.Exp(-x * x / 2);
}

private void Form1_Load(object sender, EventArgs e)
{
    plot1.Model = new OxyPlot.PlotModel { PlotType = OxyPlot.PlotType.XY };
    var series = new OxyPlot.Series.FunctionSeries(
        StandardNormalDistribution,     // 引数double,戻り値doubleの関数
        -3,                             // x座標の最小値
        3,                              // x座標の最大値
        0.01,                           // x座標の刻み幅
        "標準正規分布");                // グラフタイトル
    plot1.Model.Series.Add(series);
}

実行すると以下のようなグラフが表示される.

f:id:whoopsidaisies:20140210114639p:plain

StandardNormalDistributionという引数がdouble型,戻り値もdouble型の関数を定義している.これがFunc<double, double>のメソッドである.
これと,グラフ描画をしたい範囲をFunctionSeriesクラスのコンストラクタに渡してやるだけでグラフを描画してくれて非常に便利である.

ラムダ式による方法

メソッドの部分をラムダ式で表現することも当然出来るので,StandardNormalDistributionメソッドをわざわざ定義しないでも以下のように書くこともできる.(ここではラムダ式の説明は省略.わからない人はここらのへんのページを参照)

    var series = new OxyPlot.Series.FunctionSeries(
        x => 1 / Math.Sqrt(2 * Math.PI) * Math.Exp(-x * x / 2),     // 引数double,戻り値doubleの関数
        -3,                             // x座標の最小値
        3,                              // x座標の最大値
        0.01,                           // x座標の刻み幅
        "標準正規分布");                // グラフタイトル

媒介変数表示された関数のグラフ描画

これも簡単に出来るので例を示す.例として以下のカージオイドを表示させてみる.
x=a\(2\cos\theta - \cos2\theta\)
y=a\(2\sin\theta - \sin2\theta\)

ソースコードは以下のようになる.(ラムダ式使用)

= new OxyPlot.PlotModel { PlotType = OxyPlot.PlotType.XY };
var series = new OxyPlot.Series.FunctionSeries(
    t => 2 * Math.Cos(t) - Math.Cos(2 * t), // x座標の関数
    t => 2 * Math.Sin(t) - Math.Sin(2 * t), // y座標の関数
    0,                                      // tの最小値
    2 * Math.PI,                            // tの最大値
    Math.PI / 32,                           // tの刻み幅
    "カージオイド");                        // グラフタイトル
plot1.Model.Series.Add(series);

実行すると以下のようなグラフが表示される.

f:id:whoopsidaisies:20140210122606p:plain

その他

PlotTypeの設定によって極座標表示とかもできるがそれはまたの機会に.

C#で地球を表示する(vtkTexturedSphereSourceによる方法)

VTKのvtkTexturedSphereSourceで球に世界地図のテクスチャを貼り付けることで地球を表示させる.

テクスチャを使わずにvtkEarthSourceによって表示する方法は以下のページを参照.
C#で地球を表示する(VTKのEarthSourceによる方法) - whoopsidaisies's diary

ActiViz

ActiVizは,3Dコンピュータグラフィックスの可視化ライブラリVTK(The Visualization ToolKit)のC#ラッパである.NuGetからインストールできる.簡単な解説は以下のページを参照.
C#で3Dモデル表示(VTKのC#ラッパActiVizによる方法) - whoopsidaisies's diary

RenderWindowControl

NuGetからActiVizをインストールし,RenderWindowControlをフォームに追加する.

vtkTexturedSphereSource

VTKにはvtkTexturedSphereSourceという,テクスチャを貼り付けることが出来る球オブジェクトのクラスがあるためそれを利用する.ソースコードは以下のようになる.

sing (var sphere = new Kitware.VTK.vtkTexturedSphereSource())
using (var texture = new Kitware.VTK.vtkTexture())
using (var image = new Kitware.VTK.vtkPNGReader())
using (var mapper = new Kitware.VTK.vtkCompositePolyDataMapper())
using (var actor = new Kitware.VTK.vtkActor())
{
    // 球の滑らかさ(数字が多いほど分割数が多く滑らかになる)
    sphere.SetThetaResolution(64);
    sphere.SetPhiResolution(64);
    mapper.SetInputConnection(sphere.GetOutputPort());
    actor.SetMapper(mapper);

    // 世界地図画像の読み込みとテクスチャの設定
    image.SetFileName(@"WorldMap_ja.png");
    texture.SetInputConnection(image.GetOutputPort());                
    actor.SetTexture(texture);

    renderWindowControl1.RenderWindow.GetRenderers().GetFirstRenderer().AddActor(actor);
}

実行すると以下のように地球が表示される.世界地図画像はウィキペディアにあったものを使った.

f:id:whoopsidaisies:20140209202615p:plain

以下のように拡大表示もできるが,この方法だとベクターグラフィックの読み込みが(たぶん)できないので拡大表示する際は何かしら工夫が必要そう.

f:id:whoopsidaisies:20140209203055p:plain

C#で地球を表示する(VTKのEarthSourceによる方法)

C#でVTKのvtkEarthSourceクラスによって地球を表示させる.

ActiViz

ActiVizは,3Dコンピュータグラフィックスの可視化ライブラリVTK(The Visualization ToolKit)のC#ラッパである.NuGetからインストールできる.簡単な解説は以下のページを参照.
C#で3Dモデル表示(VTKのC#ラッパActiVizによる方法) - whoopsidaisies's diary

RenderWindowControl

NuGetからActiVizをインストールし,RenderWindowControlをフォームに追加する.

vtkEarthSource

VTKにはvtkEarthSourceという,地球が表示されるオブジェクトがあるので使う.ソースコードは以下のようになる.

using (var earth = new Kitware.VTK.vtkEarthSource())
using (var mapper = new Kitware.VTK.vtkCompositePolyDataMapper())
using (var actor = new Kitware.VTK.vtkActor())
{
    mapper.SetInputConnection(earth.GetOutputPort());
    actor.SetMapper(mapper);
    renderWindowControl1.RenderWindow.GetRenderers().GetFirstRenderer().AddActor(actor);
}

実行すると以下のように地球が表示される.ドラッグで視点変更が出来る.
f:id:whoopsidaisies:20140209185406p:plain

C++でLINQライクな処理(LINQ for C++による方法)

LINQ for C++

LINQは.NET Framework3.5から組み込まれた機能で,C#等の言語でSQLのデータベース操作のようなことを可能とする.「C# is LINQ」と言う方までいほどに便利な機能.

しかし,C++/CLIはLINQに対応する予定はないとのこと.そこで,LINQに似た機能をC++に提供してくれるのがLINQ for C++である.というわけでLINQ for C++を使ってみる.

導入

LINQ for C++はヘッダファイルひとつで実装されているので,以下のページからダウンロードしてきてインクルードするだけで使用できる.NuGetからのインストールも取得可能である.

LINQ for C++ - Home

使用例1

以下に使用例を示す.LINQ for C++を使って,テストの成績のうち国語と英語がともに50点以上の人のリストを取得している.

普通の配列に対して使うことが出来る.コード補完も微妙だし,型明記しなくちゃいけないし,メソッド形式ないしでC#のLINQほど軽快には書けないものの,便利ではある.

#include <cpplinq.hpp>

void main()
{
	using namespace cpplinq;

	struct Seiseki
	{
		char Name[256];
		int Kokugo;
		int Sansuu;
		int Eigo;
		int Rika;
		int Shakai;
	};

	Seiseki seisekiList[] = 
	{
		{"Yamada",98,78,61,83,70},
		{"Tanaka",20,33,16,39,48},
		{"Yoshida",72,65,92,13,26},
		{"Suzuki",49,82,55,11,96},
		{"Sasaki",83,47,61,23,60}
	};
	
	auto result = from_array(seisekiList)
		>> where([](Seiseki s) {return s.Kokugo > 50 && s.Eigo > 50;})
		>> to_vector();

	for (int i = 0; i < result.size();  ++ i)
		printf("%s : 国語%d 英語%d\n", result[i].Name, result[i].Kokugo, result[i].Eigo);
}

結果は以下のように表示される.

Yamada : 国語98 英語61
Yoshida : 国語72 英語92
Sasaki : 国語83 英語61

使用例2

無理やりOpenCVで使ってみる.BRISKで特徴点抽出し,オリエンテーションの向きによって表示する際の色を変えている.

#include <opencv2/opencv.hpp>
#include <cpplinq.hpp>

void main()
{
	using namespace cpplinq;

	auto image = cv::imread("Penguins.jpg");

	auto detector = cv::FeatureDetector::create("BRISK");
	std::vector<cv::KeyPoint> keyPoints;
	detector->detect(image, keyPoints);
	auto key1 = from(keyPoints)
		>> where([](cv::KeyPoint k) { return 0 <= k.angle && k.angle < 90;})
		>> to_vector();
	auto key2 = from(keyPoints)
		>> where([](cv::KeyPoint k) { return 90 <= k.angle && k.angle < 180;})
		>> to_vector();
	auto key3 = from(keyPoints)
		>> where([](cv::KeyPoint k) { return 180 <= k.angle && k.angle < 270;})
		>> to_vector();
	auto key4 = from(keyPoints)
		>> where([](cv::KeyPoint k) { return 270 <= k.angle && k.angle < 360;})
		>> to_vector();

	cv::drawKeypoints(image, key1, image, cv::Scalar(255,0,0), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
	cv::drawKeypoints(image, key2, image, cv::Scalar(0,255,0), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
	cv::drawKeypoints(image, key3, image, cv::Scalar(0,0,255), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
	cv::drawKeypoints(image, key4, image, cv::Scalar(255,255,0), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
	cv::imshow("Key Point", image);
	while(cv::waitKey(1) == -1);
}

f:id:whoopsidaisies:20140117010914p:plain

C# 名前付きの色一覧を配列に取得してRGB値をDataGridViewに表示する

Color構造体

.NET FrameworkのColor構造体はRGB値で色の指定もできるが,あらかじめRGB値が指定されていて名前を指定して使える色がある.「Color.Red」とか「Color.Green」といった感じで使うことが出来る.

本記事では,名前が付いた色の一覧を取得してColor構造体の配列に取得,さらにRGB値と色をDataGridViewに表示する方法を紹介する.

名前付きの色の取得

名前付きの色は,Color構造体の静的プロパティとして定義されている.色の一覧をColor構造体の配列に格納するコードは以下のようになる.

var colors = typeof(Color).GetProperties(BindingFlags.Static | BindingFlags.Public)
    .Select(p => Color.FromName(p.Name))
    .ToList();

1行目では,GetPropertiesメソッドを使って,Color構造体のパブリックな静的プロパティの型情報の一覧を取得している.BindingFlags構造体を使って取得するプロパティの検索方法を指定できるが,StaticとPublicを指定することでパブリックな静的プロパティを取得している.

2行目では,LINQSelectメソッドとhttp://msdn.microsoft.com/ja-jp/library/system.drawing.color.fromname(v=vs.110).aspx:Color.FromaNameメソッドを使って色の名前から,その色のColorオブジェクトを生成している.

3行目で,結果をListに変換している.(ListとかArrayとかに変換したほうが後の処理が楽なので)

DataGridViewで表示

取得した色の一覧をDataGridViewコントロールに表示する.

dataGridView1.DataSource = colors;

上記の一行を追加すると以下のように色の名前とRGB値が表示される.
f:id:whoopsidaisies:20140115195242p:plain

表示するプロパティの選択

上の画像だと「IsKnownColor」とかの余計なプロパティも表示されているので,表示しないようにする.

foreach (DataGridViewColumn col in dataGridView1.Columns)
    if (col.Name != "R" && col.Name != "G" && col.Name != "B" && col.Name != "Name")
        col.Visible = false;

DataGridViewの各列はVisibleプロパティによって表示・非表示を切り替えられるので,RGBと名前以外の列を非表示に設定している.

f:id:whoopsidaisies:20140115200155p:plain

セルの色付け

名前とRGB値だけだとどんな色か想像がつきにくいので,セルに色を付ける.ついでに名前が表示されている列を先頭にする.

for (int i = 0; i < colors.Count; ++i)
    dataGridView1["Name", i].Style.BackColor = colors[i];
dataGridView1.Columns["Name"].DisplayIndex = 0;

DataGridViewの各セルStyleプロパティによって色やフォント等の書式を指定出来る.ここではBackColorプロパティによって背景色を指定している.

また,DisplayIndexプロパティによって名前の表示列を先頭にしている.

実行すると以下のように名前のセルに色がついて表示される.(Transparentのところは後ろのウィンドウが表示されている.)

f:id:whoopsidaisies:20140115201854p:plain

ソースコード

上記コードをまとめると以下のようになる.

var colors = typeof(Color).GetProperties(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public)
    .Select(p => Color.FromName(p.Name))
    .ToList();

dataGridView1.DataSource = colors;

foreach (DataGridViewColumn col in dataGridView1.Columns)
    if (col.Name != "R" && col.Name != "G" && col.Name != "B" && col.Name != "Name")
        col.Visible = false;

for (int i = 0; i < colors.Count; ++i)
    dataGridView1["Name", i].Style.BackColor = colors[i];
dataGridView1.Columns["Name"].DisplayIndex = 0;