Tags

, , ,

In this post, I’ll take a look at using R.NET with the creation of an application written in C#. As this is a first, basic step, I’ll provide the code to make a calculator. Not exciting I know, but it demonstrates the basics!

I provide full code for the C# solution at the end: through the post here, I’ll highlight the bits that I assume readers will be most interested in – specifically, bits that actually get C# and R to talk to one another.

Anyway, here is what the calculator ended up looking like:

It may be simple, but it does have at least one answer!

Background

I’ve recently been tinkering with making GUI interface-type-things for R. I previously posted a guide on working with RApache – which is a webserver that can run R scripts. This time around, I’m using C# and .NET.

I began my attempts with making a GUI that can run R in some shape or form by taking a look at the various options on offer. Aside from RApache on the web front, you have Rook as well. Then there is this, but it doesn’t have an open source license (and appears to not work with the latest version of R), so I haven’t tried it. There is also Rcpp and RInside, which I believe can be used for this sort of purpose as well, but I haven’t got around to trying them out yet.

Getting Started

You’ll need to download the necessary file from the R.NET page.

You’ll also need to add a reference to the R.NET assembly into your solution.

Connecting to R

Here’s the basic code you’ll need:

using RDotNet;
...
REngine.SetDllDirectory(@"C:\Program Files\R\R-2.13.0\bin\i386");
REngine.CreateInstance("RDotNet");

Note that here it’s assumed that you’ll have R version 2.13.0 installed in the default location, just like me. The file being searched for here is R.dll.

With that setup, to get connected to R in any of your methods, all you need is this:

REngine engine = REngine.GetInstanceFromID("RDotNet");

Simple! engine can now send and receive information, just like an R console would normally. It also persists through commands. For example, if you send the command x<- 1 and then ask for x , it will remember that it’s 1. Great!

Details of the Calculator

It’s a very basic calculator. All I did was bind each button press to add the text from that button (e.g., “1”, “2”, “3” and so on) to the text box at the top (called textBox_input). When the user hits the = sign, the text in textBox_input gets sent to R for evaluation. Here’s the function that adds button presses to the textBox_input box:

private void add_input(string input)
        {
            if (textBox_output.Text!="") {
                textBox_input.Text = "";
                textBox_output.Text = "";
            }

            textBox_input.Text += input;
        }

The if statement at the start of add_input checks to see if a calculation has already been run (i.e., the output textBox has text in it). If that is the case, then it wipes the proverbial slate clean, allowing us to put in something new to evaluate.

Interacting with R from R.NET

There are a number of ways to interact with R from R.NET. R.NET gives you access to various data types from R (e.g., numeric vectors, data frames, lists, and so on). For the purposes of the little calculator being demonstrated here, it’s a case of evaluating the input text of the calculator as follows:

string input = textBox_input.Text;
NumericVector x = engine.EagerEvaluate(input).AsNumeric();

EagerEvaluate sends input (the text in the input textbox) to our R instance for evaluation. Here, the information we get back from R is converted to a numeric vector.

Next, we turn to what we know from R. If we ask R to evaluate the following:

40 + 2

It returns us a nice and simple 42.

All that’s being done with the NumericVector x is that the output of that 42 is being added to one of R.NET’s NumericVector data types. If you want R.NET to grab a whole load of numbers in this fashion, it’s very easy.

Finally, it’s a case of setting up the output from the calculator to be equal to the first value in x. This is because we’re expecting x to be only one value anyway.

textBox_output.Text += x[0];

This then gives the output of 42 and adds it to the output textbox.

Next Steps

Here I’ve only shown a very simple example of how to use R.NET. I don’t think the world needs any new calculators, but in the future I’ll have a go at some more complex and interesting ways to get C# and R being friendly with one another.

Full C# Code

Here is all of my C# code. Note that I’ve included some bits and pieces to handle errors and exception, as well as different versions of R. Specifically, there’s a while loop in there to help the user hunt down the R.dll file if they don’t have the latest version of R, or it’s installed in something other than the default directory.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using RDotNet;

namespace RNet_Calculator
{
    public partial class Form1 : Form
    {

        // set up basics and create RDotNet instance
        // if anticipated install of R is not found, ask the user to find it.

        public Form1()
        {
            InitializeComponent();

            string dlldir = @"C:\Program Files\R\R-2.13.0\bin\i386";
            bool r_located = false;

            while (r_located == false)
            {
                try
                {
                    REngine.SetDllDirectory(dlldir);
                    REngine.CreateInstance("RDotNet");
                    r_located = true;
                }

                catch
                {
                    MessageBox.Show(@"Unable to find R installation's \bin\i386 folder.
                    Press OK to attempt to locate it.");

                    if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
                    {
                        dlldir = @folderBrowserDialog1.SelectedPath;
                    }

                }
            }

        }

        // This adds the input into the text box, and resets if necessary

        private void add_input(string input)
        {
            if (textBox_output.Text!="") {
                textBox_input.Text = "";
                textBox_output.Text = "";
            }

            textBox_input.Text += input;
        }

        // the equals button, which evaluates the text

        private void button_equals_Click(object sender, EventArgs e)
        {
            textBox_output.Text = "";

            REngine engine = REngine.GetInstanceFromID("RDotNet");

            String input = textBox_input.Text;

            try
            {
                NumericVector x = engine.EagerEvaluate(input).AsNumeric();
                textBox_output.Text += x[0];
            }

            catch
            {
                textBox_output.Text = "Equation Error";
            }

        }

        // Begin the button function calls - long list and not exciting

        private void button_1_Click(object sender, EventArgs e)
        {
            add_input(button_1.Text);
        }

        private void button_2_Click(object sender, EventArgs e)
        {
            add_input(button_2.Text);
        }

        private void button_3_Click(object sender, EventArgs e)
        {
            add_input(button_3.Text);
        }

        private void button_4_Click(object sender, EventArgs e)
        {
            add_input(button_4.Text);
        }

        private void button_5_Click(object sender, EventArgs e)
        {
            add_input(button_5.Text);
        }

        private void button_6_Click(object sender, EventArgs e)
        {
            add_input(button_6.Text);
        }

        private void button_7_Click(object sender, EventArgs e)
        {
            add_input(button_7.Text);
        }

        private void button_8_Click(object sender, EventArgs e)
        {
            add_input(button_8.Text);
        }

        private void button_9_Click(object sender, EventArgs e)
        {
            add_input(button_9.Text);
        }

        private void button_point_Click(object sender, EventArgs e)
        {
            add_input(button_point.Text);
        }

        private void button_0_Click(object sender, EventArgs e)
        {
            add_input(button_0.Text);
        }

        private void button_plus_Click(object sender, EventArgs e)
        {
            add_input(button_plus.Text);
        }

        private void button_minus_Click(object sender, EventArgs e)
        {
            add_input(button_minus.Text);
        }

        private void button_multiply_Click(object sender, EventArgs e)
        {
            add_input(button_multiply.Text);
        }

        private void button_divide_Click(object sender, EventArgs e)
        {
            add_input(button_divide.Text);
        }

        private void button_left_bracket_Click(object sender, EventArgs e)
        {
            add_input(button_left_bracket.Text);
        }

        private void button_right_bracket_Click(object sender, EventArgs e)
        {
            add_input(button_right_bracket.Text);
        }

        private void button_ce_Click(object sender, EventArgs e)
        {
            textBox_input.Text = "";
            textBox_output.Text = "";
        }

    }
}
About these ads