Question

MicroVAX on Thu, 20 Mar 2014 09:53:40


【質問】
R.NETでweighted.mean関数を実行するとエラーが発生する原因が分かりません。
正しいと思われる文字列を渡してもエラーが発生するのが不思議です。

※weighted.meanとは、重みづけ平均のことです。

【追加コメント】

・単なる偶然かもしれませんが、weighted.mean関数の名前にはピリオド"."が含まれています。これが何らかの影響を与えている可能性がありますか?皆さんの環境では、weighted.mean関数もmean関数と同様に問題無くC#から呼び出せていますか?

【現象】
・R言語エンジンにC#から文字列「weighted.mean(c(1,2,3),c(1,1,1),na.rm=TRUE)」を渡すと33行目でエラーが発生します。
・R言語エンジンにC#から文字列「mean(c(1,2,3),na.rm=TRUE)」を渡した場合。こちらは正しい答えである「2」が返される。
・R言語のコマンドライン(対話環境)で「weighted.mean(c(1,2,3),c(1,1,1),na.rm=TRUE)」と入力すると正しい答えである「2」が返される。

【実行環境】
・OS:Windows7 SP1 64bit版
・R言語 3.02
・R.NET 1.5.5
・Net Framework 4.5.1 (Visual Studio 2013)

【エラー情報】

"Input text = weighted.mean(c(1,2,3),c(1,1,1),na.rm=TRUE)
Error = RDotNet.ParseException: アプリケーションでエラーが発生しました。
   場所 RDotNet.REngine.Parse(String statement, StringBuilder incompleteStatement)
   場所 RDotNet.REngine.<Defer>c__Iterator4.MoveNext()
   場所 System.Linq.Enumerable.LastOrDefault[TSource](IEnumerable`1 source)
   場所 RDotNet.REngine.Evaluate(String statement)
   場所 AddinClassLibrary.Rengine.Evaluate(String text) 場所 d:\Users\SO1191\Documents\Visual Studio 2013\Projects\AddinClassLibrary\AddinClassLibrary\Rengine.cs:行 33"

【ソースコード】
※エラー発生行(33行目)は、
太字の 「SymbolicExpression symbol = r.Evaluate(text);」の位置のことです。


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

namespace AddinClassLibrary
{
    internal static class Rengine
    {
        internal static object Evaluate(string text)
        {
            // 環境変数にR.dllのPathを追加します。
            {
                string rhome = System.Environment.GetEnvironmentVariable("R_HOME");
                if (string.IsNullOrEmpty(rhome)) rhome = Path.Combine("C" + Path.VolumeSeparatorChar + Path.DirectorySeparatorChar + "Program Files", "R", "R-3.0.2");
                string rpath = Path.Combine(rhome, "bin", "x64");
                System.Environment.SetEnvironmentVariable("R_HOME", rhome);
                string newpath = System.Environment.GetEnvironmentVariable("PATH") + Path.PathSeparator + rpath;
                System.Environment.SetEnvironmentVariable("PATH", newpath);
            }

            using (REngine r = REngine.CreateInstance("RDotNet"))
            {
                try
                {
                    r.Initialize();
                    SymbolicExpression symbol = r.Evaluate(text);
                    return RvalueToCellvalue.SymbolToNet(symbol);
                }
                catch (ApplicationException e)
                {
                    StringBuilder sb = new StringBuilder();
                    sb.AppendFormat("Input text = {0}"+Environment.NewLine, text);
                    sb.AppendFormat("Error = {0}", e);
                    return sb.ToString();
                }
                finally
                {
                }
            }
        }

    }
}


C#開発者


Sponsored



Replies

gekka on Thu, 20 Mar 2014 19:14:07


Win7 64bit上でx86/x64ともに正常に動作しました。
前の質問でそうだったように書き間違いがないか確認してみては?
アドインの状態でデバッグするよりもコンソールアプリとして最小機能を実装してデバッグした方がいいです。
コンソールアプリとしてデバッグするとR.Net内部でエラーが発生したらエラー内容がコンソールに出力されるので、間違ってる部分を知ることができますよ。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using RDotNet;
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string text = "weighted.mean(c(1, 2,), c(1, 1, 1), na.rm = TRUE);";//わざとエラーになるように書いてみる
            Rengine.Evaluate(text);
            Console.Read();
        }
    }

    internal static class Rengine
    {
        static Rengine()
        {
            string rhome = System.Environment.GetEnvironmentVariable("R_HOME");
            if (string.IsNullOrEmpty(rhome))
            {
                rhome = Path.Combine("C" + Path.VolumeSeparatorChar + Path.DirectorySeparatorChar + "Program Files", "R", "R-3.0.2");
            }
            string rpath = Path.Combine(rhome, "bin", (IntPtr.Size == 8 ? "x64" : "i386"));
            string newpath = System.Environment.GetEnvironmentVariable("PATH") + Path.PathSeparator + rpath;
            System.Environment.SetEnvironmentVariable("R_HOME", rhome);
            System.Environment.SetEnvironmentVariable("PATH", newpath);
        }


        internal static object Evaluate(string text)
        {
            using (REngine r = REngine.CreateInstance("RDotNet"))
            {
                try
                {
                    r.Initialize();
                    SymbolicExpression symbol = r.Evaluate(text);

                    return null;
                    //? return RvalueToCellvalue.SymbolToNet(symbol);
                }
                catch (ApplicationException e)
                {
                    StringBuilder sb = new StringBuilder();
                    sb.AppendFormat("Input text = {0}" + Environment.NewLine, text);
                    sb.AppendFormat("Error = {0}", e);
                    return sb.ToString();
                }
                finally
                {
                }
            }
        }
    }
}

MicroVAX on Tue, 01 Apr 2014 09:32:28


Rエンジンは下記のコードで、weighted.mean(c(1,2,3),c(1,1,1),na.rm=TRUE)を評価させた場合、2回目以降に必ずエラーが発生する。再起動しなければエラーは回復しない。不思議なことにmean(c(1,2,3),na.rm=TRUE)については、何回実行してもノーエラーで、weighted.meanでエラーが発生した後も何回でもノーエラーで実行することができる。

2回目以降にエラーが発生することからエラーが発生する次の行であるreturn RvalueToCellvalue.SymbolToNet(symbol);が悪さをしている可能性があると考え、return 12345;に置き換えてみたが同様のエラーが再現されるのみであった。

宜しくお願いします。

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

namespace AddinClassLibrary
{
    internal static class Rengine
    {
        static Rengine()
        {
            // 環境変数にR.dllのPathを追加します。
            string rhome = System.Environment.GetEnvironmentVariable("R_HOME");
            if (string.IsNullOrEmpty(rhome)) rhome = Path.Combine("C" + Path.VolumeSeparatorChar + Path.DirectorySeparatorChar + "Program Files", "R", "R-3.0.2");
            string rpath = Path.Combine(rhome, "bin", "x64");
            System.Environment.SetEnvironmentVariable("R_HOME", rhome);
            string newpath = System.Environment.GetEnvironmentVariable("PATH") + Path.PathSeparator + rpath;
            System.Environment.SetEnvironmentVariable("PATH", newpath);
        }

        internal static object Evaluate(string text)
        {
            using (REngine r = REngine.CreateInstance("RDotNet"))
            {
                try
                {
                    r.Initialize();
                   
                    // textの統計関数を評価します。
                    // weighted.mean(c(1,2,3),c(1,1,1),na.rm=TRUE) を2回実行すると
                    //   1回目は正しい答えである2を返す。
                    //   2回目は必ずSymbolicExpression symbol = r.Evaluate(text);でエラーが発生する。
                    //   mean(c(1,2,3),na.rm=TRUE) は何回実行してもノーエラーである。
                    //
                    //   Input text = [weighted.mean(c(1,3,3),c(1,1,1),na.rm=TRUE)]
                    //   Error = RDotNet.ParseException: アプリケーションでエラーが発生しました。
                    //   場所 RDotNet.REngine.Parse(String statement, StringBuilder incompleteStatement)
                    //   場所 RDotNet.REngine.<Defer>c__Iterator4.MoveNext()
                    //   場所 System.Linq.Enumerable.LastOrDefault[TSource](IEnumerable`1 source)
                    //   場所 RDotNet.REngine.Evaluate(String statement)
                    //   場所 AddinClassLibrary.Rengine.Evaluate(String text) 場所 d:\Users\SO1191\Documents\Visual Studio 2013\Projects\AddinClassLibrary\AddinClassLibrary\Rengine.cs:行 43"
                    SymbolicExpression symbol = r.Evaluate(text);

                    return RvalueToCellvalue.SymbolToNet(symbol);
                }
                catch (ApplicationException e)
                {
                    StringBuilder sb = new StringBuilder();
                    sb.AppendFormat("Input text = [{0}]" + Environment.NewLine, text);
                    sb.AppendFormat("Error = {0}", e);
                    return sb.ToString();
                }
                finally
                {
                }
            }
        }
    }
}

gekka on Tue, 01 Apr 2014 14:59:39


R.NETのディスカッションページで検索してみると、アプリケーションでREngineのインスタンス生成は1度だけしかできないみたいです。
Evaluate(string text)を呼ぶ度にインスタンスを作り直すことはできず、シングルトンとして保持しておいたインスタンスを再利用するように書き直す必要があるということです。

先のレスと前の回答でも書きましたが、Excelのアドインとしてデバッグしようとすると原因の切り分けが難しくなるので、コンソールアプリでテストすることを強くお勧めします
#コンソールアプリでログをみれば、2回目のInitializeでライブラリ読み込みに失敗している状態を知ることができました。

MicroVAX on Thu, 03 Apr 2014 09:37:32


アドバイスありがとうございます。
アドバイスにしたがい、
①コンソールアプリケーションに変更
②Rエンジンのインスタンス化は最初の1回のみ
にしましたが、同様のエラーが再現されました。
宜しくお願いします。

以下、ソースコード

// テスト用コード

using System;
using System.Diagnostics;
using System.Text;

namespace ConsoleApplication1
{
    internal class Program
    {
        internal static void Main(string[] args)
        {
            Func<string, string> Eval = p =>
            {
                object ret = Rengine.Evaluate(p);
                {
                    StringBuilder sb = new StringBuilder();
                    sb.AppendFormat("Input = {0}" + Environment.NewLine, p);
                    sb.AppendFormat("Output = {0}" + Environment.NewLine, ret);
                    return sb.ToString();
                }
            };

            {
                StringBuilder sb = new StringBuilder();
                sb.AppendLine();
                sb.Append(Eval("weighted.mean(c(1,2,1),c(1,1,1),na.rm=TRUE)"));
                sb.AppendLine();
                sb.Append(Eval("weighted.mean(c(1,2,2),c(1,1,1),na.rm=TRUE)"));
                sb.AppendLine();
                sb.Append(Eval("weighted.mean(c(1,2,3),c(1,1,1),na.rm=TRUE)"));
                sb.AppendLine();
                sb.Append(Eval("mean(c(1,2,1),na.rm=TRUE)"));
                sb.AppendLine();
                sb.Append(Eval("mean(c(1,2,2),na.rm=TRUE)"));
                sb.AppendLine();
                sb.Append(Eval("mean(c(1,2,3),na.rm=TRUE)"));
                sb.AppendLine();
                string ret = sb.ToString();
                Debug.WriteLine(ret);
            }
        }
    }
}

// Rエンジンを実行するクラス

using RDotNet;
using System;
using System.IO;
using System.Linq;

namespace ConsoleApplication1
{
    internal static class Rengine
    {
        static Rengine()
        {
            {
                string rhome = System.Environment.GetEnvironmentVariable("R_HOME");
                if (string.IsNullOrEmpty(rhome))
                {
                    rhome = Path.Combine("C" + Path.VolumeSeparatorChar + Path.DirectorySeparatorChar + "Program Files", "R", "R-3.0.2");
                }
                string rpath = Path.Combine(rhome, "bin", (IntPtr.Size == 8 ? "x64" : "i386"));
                string newpath = System.Environment.GetEnvironmentVariable("PATH") + Path.PathSeparator + rpath;
                System.Environment.SetEnvironmentVariable("R_HOME", rhome);
                System.Environment.SetEnvironmentVariable("PATH", newpath);
            }
            r = REngine.CreateInstance("RDotNet");
        }

        internal static object Evaluate(string text)
        {
            object ret = null;
            try
            {
                r.Initialize();
                SymbolicExpression ans1 = r.Evaluate(text);
                object ans2 = RvalueToCellvalue.SymbolToNet(ans1);
                ret = (ans2 as double[]).First();
            }
            catch (ApplicationException e)
            {
                ret = e.ToString();
            }
            finally
            {
            }
            return ret;
        }
        private static REngine r;
    }
}

// Rエンジンの計算結果をNETの値型に変換するクラス

using RDotNet;
using System;

namespace ConsoleApplication1
{
    internal static class RvalueToCellvalue
    {
        internal static object SymbolToNet(SymbolicExpression source)
        {
            string name = source.Type.ToString();
            switch (name)
            {
                case "LogicalVector":
                    return VectorToNetVector<bool>(source, p => p.AsLogical());
                case "LogicalMatrix":
                    return MatrixToNetMatrix<bool>(source, p => p.AsLogicalMatrix());
                case "IntegerVector":
                    return VectorToNetVector<int>(source, p => p.AsInteger());
                case "IntegerMatrix":
                    return MatrixToNetMatrix<int>(source, p => p.AsIntegerMatrix());
                case "NumericVector":
                    return VectorToNetVector<double>(source, p => p.AsNumeric());
                case "NumericMatrix":
                    return MatrixToNetMatrix<double>(source, p => p.AsNumericMatrix());
                case "CharacterVector":
                    return VectorToNetVector<string>(source, p => p.AsCharacter());
                case "CharacterMatrix":
                    return MatrixToNetMatrix<string>(source, p => p.AsCharacterMatrix());
                default:
                    return null;

            }
        }

        private static T[] VectorToNetVector<T>(SymbolicExpression source, Converter<SymbolicExpression, Vector<T>> symbolToVector)
        {
            Vector<T> vector = symbolToVector(source);
            int length = vector.Length;
            T[] net = new T[length];
            vector.CopyTo(net, length);
            return net;
        }
        private static T[,] MatrixToNetMatrix<T>(SymbolicExpression source, Converter<SymbolicExpression, Matrix<T>> symbolToMatrix)
        {
            Matrix<T> matrix = symbolToMatrix(source);
            int nrow = matrix.RowCount;
            int ncol = matrix.ColumnCount;
            T[,] net = new T[nrow, ncol];
            matrix.CopyTo(net, nrow, ncol);
            return net;
        }
        private static object[] NetVectorToCellVector<T>(T[] source, Converter<T, object> netToCell)
        {
            int length = source.Length;
            object[] cell = Array.ConvertAll(source, p => netToCell(p));
            return cell;
        }
        private static object[,] NetMatrixToCellMatrix<T>(T[,] source, Converter<T, object> netToCell)
        {
            int nrow = source.GetLength(0);
            int ncol = source.GetLength(1);
            object[,] cell = new object[nrow, ncol];
            for (int row = 0; row < nrow; row++)
            {
                for (int col = 0; col < ncol; col++)
                {
                    cell[row, col] = netToCell(source[row, col]);
                }
            }
            return cell;
        }
    }
}


以下、出力結果


Input = weighted.mean(c(1,2,1),c(1,1,1),na.rm=TRUE)
Output = 1.33333333333333

Input = weighted.mean(c(1,2,2),c(1,1,1),na.rm=TRUE)
Output = RDotNet.ParseException: アプリケーションでエラーが発生しました。
   場所 RDotNet.REngine.Parse(String statement, StringBuilder incompleteStatement)
   場所 RDotNet.REngine.<Defer>c__Iterator4.MoveNext()
   場所 System.Linq.Enumerable.LastOrDefault[TSource](IEnumerable`1 source)
   場所 RDotNet.REngine.Evaluate(String statement)
   場所 ConsoleApplication1.Rengine.Evaluate(String text) 場所 d:\Users\SO1191\Documents\Visual Studio 2013\Projects\ConsoleApplication1\ConsoleApplication1\Rengine.cs:行 32

Input = weighted.mean(c(1,2,3),c(1,1,1),na.rm=TRUE)
Output = RDotNet.ParseException: アプリケーションでエラーが発生しました。
   場所 RDotNet.REngine.Parse(String statement, StringBuilder incompleteStatement)
   場所 RDotNet.REngine.<Defer>c__Iterator4.MoveNext()
   場所 System.Linq.Enumerable.LastOrDefault[TSource](IEnumerable`1 source)
   場所 RDotNet.REngine.Evaluate(String statement)
   場所 ConsoleApplication1.Rengine.Evaluate(String text) 場所 d:\Users\SO1191\Documents\Visual Studio 2013\Projects\ConsoleApplication1\ConsoleApplication1\Rengine.cs:行 32

Input = mean(c(1,2,1),na.rm=TRUE)
Output = 1.33333333333333

Input = mean(c(1,2,2),na.rm=TRUE)
Output = 1.66666666666667

Input = mean(c(1,2,3),na.rm=TRUE)
Output = 2

[所見]
・Rエンジンのインスタンス化が1回だけでもエラーが発生した。
・コンソールアプリケーションにおいても、Excel-DNAと同様の現象が再現された。
同様の現象とは、
 ・weighted.mean関数を複数回実行すると2回目以降はエラーが発生する。
 ・mean関数は何回実行してもノーエラーである。

[環境]
・Windows7 SP1 64bit版
・R言語 3.0.2 (64bit版)
・R.NET 1.5.5
・Visual Studio 2013 C# debugモード/ターゲット=x64でコンパイル

以上

MicroVAX on Thu, 03 Apr 2014 10:09:28


Rエンジンの初期化を修正することで解決できました。
アドバイスありがとうございました。

以下、ソースコード(修正版)

using RDotNet;
using System;
using System.IO;
using System.Linq;

namespace ConsoleApplication1
{
    internal static class Rengine
    {
        static Rengine()
        {
            {
                string rhome = System.Environment.GetEnvironmentVariable("R_HOME");
                if (string.IsNullOrEmpty(rhome))
                {
                    rhome = Path.Combine("C" + Path.VolumeSeparatorChar + Path.DirectorySeparatorChar + "Program Files", "R", "R-3.0.2");
                }
                string rpath = Path.Combine(rhome, "bin", (IntPtr.Size == 8 ? "x64" : "i386"));
                string newpath = System.Environment.GetEnvironmentVariable("PATH") + Path.PathSeparator + rpath;
                System.Environment.SetEnvironmentVariable("R_HOME", rhome);
                System.Environment.SetEnvironmentVariable("PATH", newpath);
            }
            r = REngine.CreateInstance("RDotNet");
            r.Initialize();
        }

        internal static object Evaluate(string text)
        {
            object ret = null;
            try
            {
                SymbolicExpression ans1 = r.Evaluate(text);
                object ans2 = RvalueToCellvalue.SymbolToNet(ans1);
                ret = (ans2 as double[]).First();
            }
            catch (ApplicationException e)
            {
                ret = e.ToString();
            }
            finally
            {
            }
            return ret;
        }
        private static REngine r;
    }
}

以下、出力結果

Input = weighted.mean(c(1,2,1),c(1,1,1),na.rm=TRUE)
Output = 1.33333333333333

Input = weighted.mean(c(1,2,2),c(1,1,1),na.rm=TRUE)
Output = 1.66666666666667

Input = weighted.mean(c(1,2,3),c(1,1,1),na.rm=TRUE)
Output = 2

Input = mean(c(1,2,1),na.rm=TRUE)
Output = 1.33333333333333

Input = mean(c(1,2,2),na.rm=TRUE)
Output = 1.66666666666667

Input = mean(c(1,2,3),na.rm=TRUE)
Output = 2

以上