Accessing and extracting Data Frame Results - R.NET - c#

I run a R script from C# and get the results in a dataframe as :
var OUTPUT = engine.GetSymbol("detail.dt").AsDataFrame();
OUTPUT dataframe has let's say 3 columns as:
NAME Month Rate
Rob 1 100
Rob 2 150
Rob 3 500
Ned 1 200
Ned 2 500
Sansa 1 500
Sansa 2 1000
I can extract individual column values as :
var Name = OUTPUT[0].AsEnumerable().ToList();
var Month = OUTPUT[1].AsNumeric().ToList();
var Rate = OUTPUT[2].AsNumeric().ToList();
My question is instead of extracting column by column values, I basically want to extract Month and Rate if user asks for "Rob" or "Sansa".
How do I extract Month and Rate values for a givenName?
Is there a better and faster way?

I would loop through the data.frame. Name appears to be duplicated in your data, but here's how you can get one of them:
string userInput = "Rob";
int myMonth = 0;
int myRate = 0;
for (int i = 0; i < OUTPUT.RowCount; ++i)
{
if (OUTPUT[i, 0].ToString() == userInput) {
myMonth = Convert.ToInt32(OUTPUT[i, 1].ToString());
myRate = Convert.ToInt32(OUTPUT[i, 2].ToString());
break;
}
}
If you need all of them, get rid of the break statement, and pile them into a list or whatever data structure that suits you.

Related

OPC dll - How to retrieve tags value in bulk

I'm using OPCSiemensDAAutomation dll with C# .NET to retrieve tag's value from OPC Server. I managed to retrieve the values using QueryAvailableProperties() and GetItemProperties(), but the objective is to retrieve 500k tags value per request.
I have tested with 100 tags and the code finished it in 45 seconds, with multi threading resulted in a small improvement of 30 seconds for 100 tags. It'll requires more than 4 hours to achieve the targeted tags volume with current speed. Is there any way that I can retrieve the tags value in bulk with better performance? Thanks.
var opcServer = new OPCSiemensDAAutomation.OPCServer();
opcServer.Connect("PCS7.OPCDAServer.1");
ConcurrentBag<DataRow> myBag = new ConcurrentBag<DataRow>(dt.AsEnumerable().ToList());
Parallel.ForEach(myBag, data =>
{
if (count <= num)
{
int cnt;
Array propertyIds, descriptions, dataTypes, errors, vals;
try
{
opcServer.QueryAvailableProperties(data[0].ToString(), out cnt, out propertyIds, out descriptions, out dataTypes);
opcServer.GetItemProperties(data[0].ToString(), cnt, propertyIds, out vals, out errors);
Tags tag = new Tags();
tag.Id = data[0].ToString();
tag.Value = vals.GetValue(2).ToString();
tags.Add(tag);
Interlocked.Increment(ref count);
}
catch
{ }
}
});
You can create OPC Groups:
OPCGroup myGroup = myServer.addGroup(groupName, isActive, isSubscribed, updateRate);
And then you can add tags to your group:
myGroup.OPCItems.AddItem("FullAddress", ClientHandle) //a unique number inside the group
The FullAddress is composed of the OPCChannel name, the connection name and the complete address, ie: S7:[MyPLCName]DB1.dbx4.
When you have the group fully populated, you can read all the variables at once.
int itemCount = myGroup.OPCItems.Count;
object qualities = null;
object timeStamps = null;
object errors = null;
int serverHandles[itemCount];
Array values = Array.CreateInstance(TypeOf(object), {itemCount },{1})
for (int i = 0; i < itemCount; i++){
serverHandles[i] = myGroup.OPCItems.Item(i + 1).ServerHandle;
values.SetValue("", i);
}
myGroup.SyncRead(OPCSiemensDAAutomation.OPCDataSource.OPCDevice, itemCount + 1, ServerHandles, values, errors, qualities, timeStamps);
Then you will have four new arrays related to the first one serverHandles.
It is wise to check the qualities array before using the data from the values one.

MVC 5 How to bind linq result(Number) to array[i]?

Let's say i have a inventory slot range from 1 to 100 how to find the first empty slot from the range?
First, i'm thinking of using Array.FindIndex but i'm stuck at here.
var InventorySlot = (from i in db.InventoryModel where i.userId == 1 select i.slot).ToArray();
int index = Array.FindIndex(InventorySlot, item => item == null );
But this will create
InventorySlot[0] = 1
InventorySlot[1] = 3
InventorySlot[2] = 4
So how to create like this?
InventorySlot[1] = x
InventorySlot[3] = x
InventorySlot[4] = x
Perhaps i can use Array.FindIndex to locate the empty array? The index variable i need is 2(First empty number in the array)
What you need to do is compare the result from the database with the range without exceptions:
var firstEmptySlot = Enumerable.Range(1, InventorySlot.Max()).Except(InventorySlot).First();
If you want to get the largest slot plus one if no slot is empty, you can do:
var firstEmptySlot = Enumerable.Range(1, InventorySlot.Max()+1).Except(InventorySlot).First();
You would need to test firstEmptySlot in this case to make sure it is <= 100.
You could combine testing and the query by limiting the range to 1 - 100:
var firstEmptySlot = Enumerable.Range(1, Math.Min(InventorySlot.Max()+1, 100)).Except(InventorySlot).FirstOrDefault();
You will get a 0 back if no empty slots from 1 - 100 are available.

For loop with 20 items skip each time

I would like to write a piece of for loop which would go through an existing list and take 20 items out of that list each time it iterates.
So something like this:
If filteredList list contains let's say 68 items...
First 3 loops would take 20 times each and then in last 4th iteration it would take the rest of the 8 items that are residing in the list...
I have written something like this:
var allResponses= new List<string>();
for (int i = 0; i < filteredList.Count(); i++)
{
allResponses.Add(GetResponse(filteredList.Take(20).ToList()));
}
Where assuming filteredList is a list that contains 68 items. I figured that this is not a way to go because I don't want to loop to the collections size, but instead of 68 times, it should be 4 times and that I take 20 items out of the list each time... How could I do this?
You are pretty close - just add a call to Skip, and divide Count by 20 with rounding up:
var allResponses= new List<string>();
for (int i = 0; i < (filteredList.Count+19) / 20; i++) {
allResponses.Add(GetResponse(filteredList.Skip(i*20).Take(20).ToList()));
}
The "add 19, divide by 20" trick provides an idiomatic way of taking the "ceiling" of integer division, instead of the "floor".
Edit: Even better (Thanks to Thomas Ayoub)
var allResponses= new List<string>();
for (int i = 0 ; i < filteredList.Count ; i = i + 20) {
allResponses.Add(GetResponse(filteredList.Skip(i).Take(20).ToList()));
}
You simply have to calculate the number of pages:
const in PAGE_SIZE = 20;
int pages = filteredList.Count() / PAGE_SIZE
+ (filteredList.Count() % PAGE_SIZE > 0 ? 1 : 0)
;
The last part makes sure that with 21, there will be added 1 page above the previously calculated page size (since 21/20 = 1).
Or, when using MoreLINQ, you could simply use a Batch call.
I suggest a simple loop with page adding on 0, 20, 40... iterations without Linq and complex modular operations:
int pageSize = 20;
List<String> page = null;
for (int i = 0; i < filteredList.Count; ++i) {
// if page reach pageSize, add a new one
if (i % pageSize == 0) {
page = new List<String>(pageSize);
allResponses.Add(page);
}
page.Add(filteredList[i]);
}
i needed to do same but i needed to skip 10 after each element so i wrote this simple code
List<Location2D> points = new List<Location2D>();
points.ForEach(p =>
{
points.AddRange(path.Skip((int)points.Count * 10).Take(1));
});
if (!points.Contains(path.LastOrDefault()))
points.Add(path.LastOrDefault());

Merge two reports to create a four column single report

This is related to a GameBetting related Project.
We have Two simple Lists of following Class.
class Gameresults
{
int userid,
double amount
}
The following two reports are printed on paper.
A) Game Winners
uid amount
10 -
14 -
15 -
B) Game Losers
uid amount
11 -
12 -
13 -
16 -
Since the columns in such report leaves space across
the width of a Paper, we have to merge both the reports and
prepare the following report
Game Winners Game Losers
uid Amount uid Amount | uid Amount uid Amount
10 - 15 - 11 - 13 -
14 - 12 - 16 -
The above report has two columns for each report.
The Row length of WinnerReport is totalrecords/2 , therefore two records in first
column and 1 record in next column
So first the right column is filled, rest goes to the left column
Same for LosersReport
the Rowlength of Winner vs Losers reports is not important.
They however must be equal (+/- 1) with respect to record count in their
respective Lists
I made a Class to put all the records in one row, as we use printer for output of the report
class MergeRow
{
int uidWinCol1; // userid,amount of winner on column 1
double amtWinCol1;
int uidWinCol2; // userid.amount of winner on column 2
double amtWinCol2;
int uidLosCol1;
double amtLosCol1;
int uidLosCol2;
double amtLosCol2;
}
I need advice for the part on how to merge both lists, i presume such
a method is possible only in Linq, but any pointer or link will be helpful.
thank you
You can split the winnersList and losersList, each into 2 halves. Thus you would have 4 sublists.
Now you can use FirstOrDefault on each sublist to get an instance ofMergeRow. In case one of the sublists become empty before the others, use DefaultIfEmpty, with a placeholder item.
The code would look like:
var winnersList = new List<Gameresults>();
var losersList = new List<Gameresults>();
//Populate the winnersList and losersList
var winnersList1 = winnersList.Take(winnersList.Count/2).ToList();
var winnersList2 = winnersList;
var losersList1 = losersList.Take(losersList.Count/2).ToList();
var losersList2 = losersList;
var allLists = new List<List<Gameresults>> {winnersList1, winnersList2, losersList1, losersList2};
var mergeRows = new List<MergeRow>();
while (allLists.Any(l => l.Count > 0))
{
var resultsInOneRow = allLists.Select(l => l.DefaultIfEmpty(new Gameresults()).FirstOrDefault()).ToList();
mergeRows.Add(GetMergeRow(resultsInOneRow));
}
Your GetMergeRow() method would look like:
private MergeRow GetMergeRow(List<Gameresults> recordsToMerge)
{
var mergeRow = new MergeRow();
mergeRow.uidWinCol1 = recordsToMerge[0].userid;
mergeRow.amtWinCol1 = recordsToMerge[0].amount;
//... and so on
return mergeRow;
}

2d array,adding it values in a weird pattern

i started learning C# and programming a few months ago and have some problems. The idea here is we create a 2 dimensional array (the number of rows / columns are added by the user), the numbers need to be between 1 and 10.
Then when the array is created the number sequence ( 3-5-7-9-11 etc) is started in the first and finishes in the last column. The rest of the numbers in the columns are added via keyboard by the user starting with the first row (ignoring column 1 and the last column cause we have that added).
The questions are :
What will be the best way to check if the numbers of rows/columns are between 1 and 10? (I was thinking of IF-else but isn't there a better way ?)
How will i make it so that the number sequence 3-5-7 etc is started in the first and finishes in the last column?
Yeah i feel lost.
Where i am at the moment :
Console.WriteLine("Add row value of 1-10");
string s1
s1 = Console.ReadLine();
int k = int.Parse(s1);
Console.WriteLine("Add column value of 1-10");
string s2;
s2 = Console.ReadLine();
int p = int.Parse(s2);
int[,] M = new int[k, p];
Example : we added k(row) & p(coulmn) value of 4.So the array should look like :
3 x x 11
5 x x 13
7 x x 15
9 x x 17
Then the X's should be added again manually without overwriting the existing numbers .The value of the numbers doesnt matter.
So... If I get it right you want to ask user the "length and width" of dynamical 2d array?
To check if entered number is between 1 and 10 there's only 1 method:
int [,] M;
if (k >= 1 && k <= 10 && p >= 1 && p <= 10)
{
M = new int[k,p];
}
And better is to do int.TryParse() for case if user enters characters there instead of numbers, or else you can easily get an Exception.
Filling with numbers:
int num = 3;
for (int i = 0; i < k; ++i)
{
M[i,0] = num;
num+=2;
}
for (int i = 0; i < k; ++i)
{
M[i,p] = num;
num+=2;
}
This adds numbers in 1st and last column in each row. After that to fill other cells manually you check every cell thet it is not in firs or last column.
I hope I understood you correctly. Provided code may be simplified, but provided in such way for better understanding.
if(k>0 && k<11 && p>0 && p<11)
{
int i;
int M[,] = new int[k,p];
for (i=0;i<k;i++)
{
M[i,0]=i*2+3;
M[i,p-1]=(i+k)*2+3;
}
}

Categories

Resources