R.NETとExcel-DNAでRで動くExcelアドインを作る

手元にあるVisual StudioがExpress版だと中々作るのが難しいExcelアドインですが、CodePlex Archiveというものを使うと驚くほど簡単にExcelアドインを.NET言語を使って作成できます。またさらに、.NET言語からR言語を呼び出すことを可能にするDLLを、我らが偉大なる@kos59125氏が作成されているので、それらを組み合わせて「Rで動くExcelアドイン」を作ってみましょうというお話です。相互の依存関係をポンチ絵で示すとこんな感じです。

もちろん、Rには既にRExcelというExcelとの連携を強力にサポートするものがあるのですが、こちらインストールの手間が結構面倒なのと、更にインストールの際に管理者権限が必要となる一方、今回紹介する組み合わせだと簡単&管理者権限必要ないので、セキュリティにうるさい会社でも使えるし、何より第三者に渡す場合の配布物が少なく、zipかなんかで固めて渡せるので楽というメリットもあります。

各種必要な物のダウンロード

お手元に既にR言語Excelがインストールされているものとして、まずは以下のサイトからR.NETとExcel-DNAをダウンロードします。

双方共にLINK先にある「DownLoad」ボタンを素直に押下すればOKです。

Excel-DNAの方はzipファイルが、R.NETの方は1つのDLLが取得できると思います。Excel-DNAは適当なフォルダに解凍して「Distribution」フォルダに入っているExcelDna.xllとExcelDna.dna2つのファイルをコピーします*1

コピーしたExcelDna.xllとExcelDna.dnaはR.NETと一緒に適当なフォルダを作って、その中にまとめておいてください。その後、コピーしたExcelDna.xllとExcelDna.dnaのファイル名は自分のやりたい事に合わせたそれっぽい名前(SumArray.xll等)に変えておきます。ここではそれぞれSimpleExample.xllとSimpleExample.dnaとしておきました。

初めの第一歩

まずは指定した個数分だけ標準正規乱数を返却する関数を作成したいと思います。
そしてメモ帳/vim等適当なテキストエディタでSimpleExample.dnaを開いた後、以下のコードをコピーして既存のコードを削除した上で貼り付けてください。

<DnaLibrary RuntimeVersion="v4.0" Name="My First XLL" Language="CS">
<ExternalLibrary Path="R.NET.dll" />
<Reference Name="R.NET" />
<![CDATA[

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RDotNet;

namespace CSLib
{
    public class CSLib
    {
        static REngine rengine = null;
        static CSLib()
        {
            // Set the folder in which R.dll locates.
            REngine.SetDllDirectory(@"C:\R\R-2.14.1\bin\i386");
            rengine = REngine.CreateInstance("RDotNet", new[] { "-q" });
        }          
        public static double [] MyRnorm(int number)
        {
            return (rengine.EagerEvaluate("rnorm(" + number + ")").AsNumeric().ToArray<double>());
        }
    }
}
]]>
</DnaLibrary>

ここではC#を使って関数を作成してみましたが、Language="CS"」という箇所を「Language="VB"」に変更すれば、VB.NETで記述することも可能です。上記のコードでのR言語/.NET frameworkのバージョンについては、ご自身の環境に合わせて

  • .NETのバージョン:「RuntimeVersion="v4.0"」
  • R言語のインストール先:「REngine.SetDllDirectory(@"C:\R\R-2.14.1\bin\i386");」

の箇所を修正してください*2。これで完成です。簡単ですね。

作成したExcelアドインを使用するには

  • SimpleExample.xllをダブルクリック後、「新規ブックの作成」を実施する
  • 適当に作成しておいたExcelファイルにSimpleExample.xllをドラッグ&ドロップ
  • Excelを起動してアドインの追加(.xllファイル)を行う

のいずれかを行ってください。今作成した関数は「MyRnorm」という名前にしてあるので、それをCell数式として打ち込むと・・・

のように計算させることができます。なお、複数の乱数(nを1以上とした場合)は配列数式(Ctrl + Shift + Enter)として入力してください。結果は行ベクトル(横方向)として返却されます。

もうちょっとアドインぽいものを作る

先程の例では.dnaファイルを直接いじってアドインの開発を行いましたが、開発過程でデバッガを使えるととても効率よく開発できるので、今度はVisual C# 2010を用いてアドインを作成してみます。
まず、Visual Studio*3を起動して「クラスライブラリ」の作成を選択します。

そして、メインとなるファイルに以下のコードを記述しました。上の例から新しく追加されているものは

  • アドインが開かれた・閉じられた際の処理(AutoOpen ,AutoClose))
  • メニューバーに"Hello, world"ボタンを追加(HelloWorld)
  • 回帰分析を実施して、回帰係数を返却する関数(RLinearRegression)

の3つです。コンパイルするためにはRDotNet*4・ExcelDna.Integration*5への【参照の追加】を行ってください。これをコンパイルすれば完成です。そして完成したDLLをアドインから呼び出すためにdnaファイルに

<DnaLibrary RuntimeVersion="v4.0" Name="Sample For TokyoR21" Language="CS">
<ExternalLibrary Path="Sample_TokyoR21.dll"/>
</DnaLibrary>

と記述します。「」の箇所は自分の作成したDLLの名に応じて適宜変更してください。これを使ってみると以下のようになことができます。

デバッグについてですが、Visual C# 2010 Express版の場合Excelから関数を呼んだ状態でデバッグをするためには「おまじない」が必要になります。参考にしてみてください。このおまじないを行うと「デバッグ開始(F5)」をした場合Excelが起動し、Excelで関数を呼んだ際にデバッグできるようになります。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using RDotNet;
using ExcelDna.Integration;

namespace Sample_TokyoR21
{
    //IExcelAddInインターフェイスを継承することで起動・終了時の処理も行える
    public class Sample : IExcelAddIn
    {
        //R言語で計算をさせる際に使用す計算エンジン
        static REngine rengine = null;
        //Staticコンストラクターで計算エンジンの初期化
        static Sample()
        {   
            //R.dllのあるフォルダを指定
            REngine.SetDllDirectory(@"C:\R\R-2.14.1\bin\i386");
            //計算エンジンの設定
            rengine = REngine.CreateInstance("RDotNet", new[] { "-q" });
        }
        //number個だけ標準正規乱数を発生させる関数
        [ExcelFunction(Description="Generate standard normal distribution", Category="R Language Addin")]
        public static double[] MyRnorm(int number)
        {
            return (rengine.EagerEvaluate("rnorm(" + number + ")").AsNumeric().ToArray<double>());
        }
        //回帰分析を実行して結果を返す関数
        [ExcelFunction(Description="Execute Linear Regression", Category="R Language Addin")]
        public static double[] RLinearRegression(double [,] data)
        {
            int size = data.GetLength(0);
            double[] x = new double[size];
            double[] y = new double[size];
            for (int i = 0; i < size; i++)
            {
                x[i] = data[i, 0];
                y[i] = data[i, 1];
            }
            //データをR上へ転送
            rengine.SetSymbol("x", rengine.CreateNumericVector(x));
            rengine.SetSymbol("y", rengine.CreateNumericVector(y));
            //計算実行&返却
            NumericVector result = rengine.EagerEvaluate("coef(lm(y ~ x))").AsNumeric();
            return result.ToArray();
        }
        //メニューバーに追加
        [ExcelCommand(MenuName = "&TokyoR", MenuText = "Hello, World")]
        public static void HelloWorld()
        {
            MessageBox.Show("Hello, World");
        }
        //IExcelAddin Interfaceのための実装
        public void AutoOpen()
        {
            MessageBox.Show("Call AutoOpen");
        }
        public void AutoClose()
        {
            MessageBox.Show("Call AutoClose");
        }
    }
}

【おまけ1】昔作成したRの遺産を活用したい場合

既にRでの分析プログラムが手元にある場合は、それをsource関数で読みこませればOK。読み込ませる時の文字列の指定の仕方に注意(ファイル名も"や'で囲む、フォルダの区切りは\を2つ)
↓こんな感じ。

rengine.EagerEvaluate(@"source('C:\\Users\\teramonagi\\hoge.R')");
double x = rengine.EagerEvaluate("hogefunction(10)").AsNumeric().First();

【おまけ2】実はF#でも書ける(Excel DNA)

「実は」ってほどの話でもないのですが、Excel-DNAを使うためには.NET系の言語ならなんでも良いっぽいので、実はF#でもアドイン開発できますよというお話。以下を適当な.dnaファイルに貼り付けてアドインを作ると・・・

こんな感じでF#で計算させた答えが簡単にExcelに返ってくる!!!!これはすごい!!!!夢が広がる!!!*6

<DnaLibrary Name="FSharp Sample" Language="F#">
<![CDATA[
#light
module fsExcelSample
open ExcelDna.Integration
[<ExcelFunction(Category="FSharp", Description="Add two numbers")>]
let FSarpAdd x y = x + y
]]>
</DnaLibrary>

*1:手元にないので試せないのですが、64bit環境の方は”64”と命名されている方のファイルを使用した方がよいと思います

*2:R.NETは.NETのバージョンが4.0じゃないと動かないかも?

*3:Visual Studio 2010 C# Express Edtionを使用。VB.NETの場合も同様に出来ると思います

*4:R.NET.dll

*5:Excel DNAのDistributionフォルダに入っているExcelDna.Integration.dll

*6:後、そもそもなんでこんなことが実現できるのかにも興味が出てきたのでソースコードでも漁ってみるか・・・