C# and R.Net not displaying any graph with ggplot2 - c#

I have the following code to plot my input series using R charts from my C# application:
public void plotGraphR_2D(List<double> x, double[,] y)
{
string Rpath = #"C:\Program Files\R\R-3.1.0\bin\x64";
REngine.SetEnvironmentVariables(Rpath);
REngine engine = REngine.GetInstance();
var v1 = engine.CreateNumericVector(x);
var v2 = engine.CreateNumericMatrix(y);
if (engine.IsRunning == false)
{
engine.Initialize();
}
engine.SetSymbol("v1", v1);
engine.SetSymbol("v2", v2);
engine.Evaluate("require('ggplot2')");
engine.Evaluate("library('ggplot2')");
engine.Evaluate("my_data <- data.frame(v2)");
engine.Evaluate("colnames(my_data) <- c('Price', 'Quantity')");
engine.Evaluate("myChart <- ggplot() + geom_line(data = my_data, my_data$Price)"); // THIS DOESN'T WORK
engine.Evaluate("myChart");
//engine.Evaluate("plot(my_data$Price)"); // THIS WORKS
}
My input x is a list while y is a 2 dimensional array. I first convert x to numeric vector and y to data frame, then I change column names to the data frame.
I want to plot one of the column of my data frame (my_data$Price) but when using ggplot2 it doesn't work. I don't get any error but I don't see any chart popping up.
If I try using the last line engine.Evaluate("plot(my_data$Price)") (so normal plot) it works fine.
Is there any problem with the way I call ggplot2? I have installed the library with RStudio. Anything else I should do to fix the issue?
Thanks.

The R code provided does not work as it is reported. The code below does create a ggplot successfully, however the print statement creates a graphic device window but shows an incorrect display (blank form). So yes there is an issue, but I do not know exactly why. The only workaround I can suggest to try is to output images to disk.
engine.Evaluate("myChart <- ggplot(my_data, aes(x=Price, y=Quantity)) + geom_line()");
engine.Evaluate("print(myChart)");

Related

chart control - string on x axis

How do I show string values on the x axis of a chart control ? the datapoints (x & y) are both double. I have an IEnumerable containing a list of objects with 2 properties. The name of a report and an integer value indicating how many times it was run. so the integer is on the y axis and the report name is on the x axis. But I only seem to have double options for the value types. So when I create my datapoint, Im geting an error
DataPoint p1 = new DataPoint();
p1.XValue = log.ReportName; ---> this is invalid
p1.YValues = new Double[] { log.ReportTotal };
how can this be done ?
Ive tried this
reportTotal.XValueType = ChartValueType.String;
and when I plot my datapoints, Ive done this
int i = 1;
foreach (var log in this._reportLogs)
{
DataPoint p1 = new DataPoint();
p1.XValue = (double)i;
p1.YValues = new Double[] { log.ReportFrequency };
i++;
reportTotal.Points.Add(p1);
}
but now all im getting along the x axis are the number of the i varaible, how on earth do I just get the content of the text property ?
this is how I made it work
Dictionary<string, int> ReportLogs = new Dictionary<string, int>();
foreach (var log in this._reportLogs)
ReportLogs.Add(log.ReportName, log.ReportFrequency);
foreach (KeyValuePair<string, int> log in ReportLogs)
reportTotal.Points.AddXY(log.Key, log.Value);
The labels along the axis are created from the x-values and their number is determined either automatically as they fit or by setting the Interval property of the x-axis.
So their placement will not necessarily coincide with the actual DataPoints.
And you can format them but only within the normal formatting rules, ie you can't get creative with expressions to convert a number to a custom string, which seems to be just what you want: show a report name according to its number.
Other examples could be show a city from its zip code or a person name acording to emp.ID..
To gain full control you may need to use CustomLabels. They are a little bit tricky but quite useful. See several of these posts for numerous examples!
Also note: If you change the x-value type to string (which is possible : yourseries.XValueType = ChartValueType.String) these strings would show directly but you would loose the actual x-values! Usually not recommended (but possibly fine in your case)!
So if you want to can do it; however you can't prepare the DataPoint like you did. Instead you need to use the Points.AddXY method.
This will convert the strings to double (resulting in zeroes) and copy the string into the labels..:
reportTotal.Points.AddXY(log.ReportName, new Double[] { log.ReportTotal });
Update
Another way would be to set the AxisLabel of each DataPoint. For all of these to show up you may need to set the yourxaxis.Interval = 1.
So in your loop simply add
p1.AxisLabel = log.ReportName;

Microsoft Computer Vision OCR : Disable grouping text by regions

I've been using Microsoft Computer Vision to read receipts, trying to find an alternative to Abby's OCR as there is a substantial price difference.
The results I get are always grouped by regions. This obviously makes it much harder to identify the corresponding fields with their amounts.
Is there a way through Microsoft Vision or anyway at all that I can achieve the same aligned output as Abby's?
Here's an image with both results and the receipt
Ocr Results
I realize this is not a complete solution but I think it's enough to get you started.
The Computer Vision API returns a JSON result with a lines property that is just an array of objects with a boundingBox property.
These boundingBoxes are the X,Y coordinates of the top-left and bottom-right coordinates of the "square" of each phrase.
You basically need to process this array and "sort" the items based on this property.
In this JSFiddle you'll see that I'm sorting the lines by Y coordinate and then grouping them.
What's left to do is be "smarter" about the grouping - if the Y coordinates are 201 and 202 you can assume that they are on the same line and just add them to one same line, sorted by ascending X coordinate.
Code:
if (jsonResponse.status == 'Succeeded') {
var result = '';
// Sort lines by Y coordinate
jsonResponse.recognitionResult.lines.sort(function(a, b) {
var topLeftYCoordA = a.boundingBox[1];
var topLeftYCoordB = b.boundingBox[1];
if (topLeftYCoordA > topLeftYCoordB) {
return 1;
}
if (topLeftYCoordA < topLeftYCoordB) {
return -1;
}
return 0;
})
// group lines by Y coordinate
var grouped = {};
jsonResponse.recognitionResult.lines.map(function(line) {
var topLeftYcoordinate = line.boundingBox[1];
if (!grouped[topLeftYcoordinate]) {
grouped[topLeftYcoordinate] = line;
} else {
grouped[topLeftYcoordinate] += line;
}
});
Object.keys(grouped).forEach(function(yCoordinate) {
result += yCoordinate + ' - ' + grouped[yCoordinate].text + '</br>';
})
$(".right").html(result);
}
Result:

Cannot display labels for specific data points in MSChart

I want to display labels for specific data points in MSChart pie chart winform application with the following code
if (Accountchart.Series[0].Points.Count > 0)
{
for (int i = 0; i < Accountchart.Series[0].Points.Count; i++)
{
double calc=(yValues[i] * 100 / (double)totalTimeSpent);
if ( calc< 10.00)
Accountchart.Series[i].Points[i]["PieLabelStyle"] = "Disabled";
}
}
But getting the following error while executing the code segment in if block second time
[Screenshot]![1
You probably ONLY have Series[0], I guess.
Series[i] looks like a mistake to me - in the whole context of this code.
So, when i==1 (the second time through), the Series[i] does NOT exist! Thus the exception, cause index is out of range.
But it is only a guess!
I think You in fact wanted to write: Series[0]....... in the IF-command, just like in the lines above the for loop.

Programmatically check textboxes for completeness

I am looking for a way to figure out if textboxes are filled in in a specific way.
I have the following textboxes:
X1 Y1 Z1
X2 Y2 Z2
X3 Y3 Z3
Users are expected to input into them continuously. For example, if only X1,Y1 have texts, it's valid. While if X1,Z1 have texts while Y1 hasn't, it's invalid.
The reason for this is because:
string G1
if(!string.IsNullOrWhiteSpace(X1.Text) && string.IsNullOrWhiteSpace(Y1.Text))
{
G1 = "<" + X1.Text + ">";
}
else if(!string.IsNullOrWhiteSpace(X1.Text) && !string.IsNullOrWhiteSpace(Y1.Text))
{
G1 = "<" + X1.Text + ">, ";
}
If they skip around then what is printed in the end will not be what is expected.
If you can think of an easier way to do all of this, please share. I am sure that my way of attacking this problem is quite simplistic and inefficient.
The answer I found is to use an array of the text boxes, then to iterate over that to determine the text of the filled out boxes. Then add the text from those boxes to a list.
Finally I used joined all of the items of that list, in a way that made the final output correct.
Code I used in the end:
string[] cells = new string[] { X1.Text, Y1.Text, Z1.Text, X2.Text, Y2.Text, Z2.Text, X3.Text, Y3.Text, Z3.Text };
List<string> final_cells = new List<string>();
//Process array to determine which cells are full, adds full cells to list
for (int i = 0; i < cells.Length; i++)
{
if(cells[i].Length == 0)
{
//If cell is empty, continue to the next one
continue;
}
else
{
// If cell is full add its contents to the final_cells list
final_cells.Add(cells[i]);
}
}
//Join the list to one string
string content = string.Join(">, <", final_cells);
Also I am sorry that I did not better explain the original situation.
The purpose of the final application is to output text in a specific format for a minecraft plugin to read. The 3x3 of text boxes is meant to mimic the crafting grid from the game.
The type of recipe this part of the application is supposed to add does not need to use 9 items/blocks, and is not shaped (so it doesn't technically matter where the user inputs each item name). Because of this, the empty boxes have to be completely ignored, and the program needs to know if there is something coming next, so I could not simply say that the recipe ended at the first empty box.
An output of <item1> <item2> is not valid, the correct format is <item1>, <item2>. However the input can also be a single item... so this <only_item> is a valid format.
I hope it is clear both what the function of this is, but also why this was important for my application.

Extract x,y values from deldir object using RDotNet

Background
I am using RDotNet to run an R script that performs a voronoi tessellation using the deldir package. After
R:tiles = tile.list(voro) I wish to extract R:tiles[[i]][c("x","y")] for the each tile i into a C#:List<Tuple<double,double>>.
Issue 1
I can extract the R:tiles object into C#-world using var tiles = engine.Evaluate("tiles").AsVector().ToList(); but I am struggling to understand how to use RDotNet to extract the x, y values for each tile from this point:
I don't know how to iterate over this object to extract the x, y values that I desire.
Issue 2
Alternatively, I attempted to create a new simpler object in R, i.e. values and attempt to extract a string and parse values from that. So far I have only created this object for one of the points:
R: e.g.
values <- tiles[[1]][c("x","y")]
C#: e.g.
var xvalues = engine.Evaluate("values[\"x\"]").AsCharacter();
var yvalues = engine.Evaluate("values[\"y\"]").AsCharacter();
// Some boring code that parses the strings, casts to double and populates the Tuple
However I can only extract one string at a time and have to split the string to obtain the values I'm after. This does not seem like the way I should be doing things.
Question
How can extract the x,y coordinates for every tile from R:tiles[[i]][c("x","y")] into a C#:List<Tuple<double,double>>?
I think you are after something like the following if I got what you seek correctly. The full code I tested is committed to a git repo I've just set up for SO queries. I've tested against the NuGet package for 1.5.5; note to later readers that subsequent versions of R.NET may let you use other idioms.
var res = new List<List<Tuple<double, double>>>();
// w is the result of tile.list as per the sample in ?tile.list
var n = engine.Evaluate("length(w)").AsInteger()[0];
for (int i = 1; i <= n; i++)
{
var x = engine.Evaluate("w[[" + i + "]]$x").AsNumeric().ToArray();
var y = engine.Evaluate("w[[" + i + "]]$y").AsNumeric().ToArray();
var t = x.Zip(y, (first, second) => Tuple.Create(first, second)).ToList();
res.Add(t);
}

Categories