Reading a file and mapping values - c#

I found an implementation of a parallel coordinates application in c#. What I am trying to achieve is that I want to be able to read a CSV file and map the values and Labels onto the coordinates. The method mapping the values is assigning the values manually. Instead, I want those values to be read from the CSV file.
Here is the current method:
public void DataBind()
{
IList<DemoInfo> infos = new List<DemoInfo>();
for (int i = 0; i < ObjectsCount; i++)
{
var x = new DemoInfo();
x.X = m_Random.NextDouble() * 400 - 100;
x.Y = m_Random.NextDouble() * 500 - 100;
x.Z = m_Random.NextDouble() * 600 - 300;
x.V = m_Random.NextDouble() * 800 - 100;
x.K = 1.0;
//x.M = i % 2 == 0 ? 1.0 : -20.0;
x.M = i;
x.Tag = i + 1;
infos.Add(x);
}
var dataSource = new MultiDimensionalDataSource<DemoInfo>(infos, 6);
dataSource.MapDimension(0, info => info.X);
dataSource.MapDimension(1, info => info.Y);
dataSource.MapDimension(2, info => info.Z);
dataSource.MapDimension(3, info => info.V);
dataSource.MapDimension(4, info => info.K);
dataSource.MapDimension(5, info => info.M);
//dataSource.MapDimensionToOpacity(0, 0.5);
dataSource.MapTag(info => info.Tag);
dataSource.Labels[0] = "X";
dataSource.Labels[1] = "Y";
dataSource.Labels[2] = "Z";
dataSource.Labels[3] = "V";
dataSource.Labels[4] = "K";
dataSource.Labels[5] = "M";
dataSource.HelperAxisLabel = "Helper axis";
DataSource = dataSource;
}
Here is some of the data in the CSV File:
SWW Institutions Undergradutes Postgraduates
University College 2085 250
Metropolitan University 4715 1135
Would really appreciate your help !!
Thanks.

I am not sure how your CSV file is mapping to the DemoInfo class. Also, the example below is based on a CSV file, but your sample data is showing a TSV file. If it is a TSV file, just replace ',' with '/t'. Also, something watch out for is if any strings contain your delimiter, such as a SWW Institutions string like "Univeristy, Madison".
You can open the file to read the lines of text and split the line based on your delimiter.
using (var sr = File.OpenText(path))
{
var line = string.Empty;
while ((line = sr.ReadLine()) != null)
{
var dataPoints = line.Split(',');
// Create Your Data Mappings Here
// dataPoints[0]...
}
}

Related

fill chart from csv file

I have csv file with data:
1,1671790171,75
0,1671790182,11
1,1671790193,11
0,1671790212,19
1,1671790239,27
0,1671790248,97
1,1671790342,94
0,1671790361,19
1,1671790368,79
0,1671790408,40
1,1671790417,90
0,1671790432,15
I need create application to show chart where first data is "work" or "not work", second data is linuxtimestamp data and time, third data is the number of seconds the process took.
I wrote something like this:
// Use OpenFileDialog to allow the user to select a CSV file
var openFileDialog = new OpenFileDialog
{
Filter = "CSV files (*.csv)|*.csv"
};
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
// Clear the chart
chart1.Series.Clear();
// Read the CSV file
using (var reader = new StreamReader(openFileDialog.FileName))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
// Parse the values
var working = values[0] == "1";
var timestamp = long.Parse(values[1]);
var duration = int.Parse(values[2]);
// Convert the Unix timestamp to a DateTime
var date = DateTimeOffset.FromUnixTimeSeconds(timestamp).UtcDateTime;
// Create a new series in the chart
var series = new System.Windows.Forms.DataVisualization.Charting.Series
{
Name = date.ToString("dd-MM-yyyy HH:mm:ss"),
Color = working ? Color.Red : Color.Green,
BorderColor = Color.Black,
ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Bar,
IsXValueIndexed = false,
XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.DateTime
};
series.Points.AddXY(date, duration);
chart1.Series.Add(series);
}
}
// Set the bar width for all series
foreach (var series in chart1.Series)
{
foreach (var point in series.Points)
{
// point.Width = 20;
point.BorderWidth = 0;
}
}
However, I need help with replacing so that on line X there are all occurrences (i.e. each line) with preferably a vertical entry of the date of occurrence, and on line Y there is time of operation (i.e. the last data from each line)

how to find average with strings c#

I need to set a variable as the average of 3 other variables, which are numbers but they are set as strings. How do I do this? I'm using c#, visual studio, windows forms.
The variable i'm trying to set is called skiTime, the variables i'm using to get the average are called skiTime1, skiTime2 and skiTime3.
basically i need the c# version of: skiTime = (skiTime1 + skiTime2 + skiTime3) / 3
The code where I start (declare? I don't know the word to use) the variables
List<string> skiTime1 = new List<string>();
List<string> skiTime2 = new List<string>();
List<string> skiTime3 = new List<string>();
string skiTime
The code where i set the value for the variables:
using (StreamReader sr = new StreamReader("pupilSkiTimes.txt"))
{
string line = "";
while ((line = sr.ReadLine()) != null)
{
string[] components = line.Split("~".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
skiTime1.Add(components[2]);
skiTime2.Add(components[3]);
skiTime3.Add(components[4]);
}
sr.Close();
}
I need to display skiTime1, skiTime2 and skiTime3 in a data grid view, so i think they need to be strings, if i'm not mistaken. skiTime will only be used in another calculation so maybe it can be turned into an int. I don't really know what i'm doing and only got this far because of tutorials, help.
I can post the whole code if this question is too confusing or doesn't have enough information.
public string CalculateAverage(List<string> skiTime1, List<string> skiTime2, List<string> skiTime3)
{
List<string> allValues = new List<string>();
allValues.AddRange(skiTime1);
allValues.AddRange(skiTime2);
allValues.AddRange(skiTime3);
float totalcount = 0;
float average = 0;
foreach (var value in allValues)
{
totalcount = totalcount + float.Parse(value);
}
average = totalcount / allValues.Count();
return average.ToString();
}
Function for returning the average value
Now call the function where u need like:
string skiTime = CalculateAverage(skiTime1, skiTime2, skiTime3);
You need to parse the strings to decimals then calculate the average:
List<decimal> avgTime = new List<decimal>();
for (var i = 0; i < skiTime1.Length; i++) {
var avg = (decimal.Parse(skiTime1[i]) + decimal.Parse(skiTime2[i]) + decimal.Parse(skiTime3[i])) / 3;
avgTime.Add(avg);
}

generate a trendline from files in a chart

I want to be able to fetch .csv files from a folder and plot them up in a chart.
Currently, I'm just saving the files and displaying an individual curve like this:
RunTest function:
public List<Tuple<double,double>> runTest()
{
_dpg = new Root(this, "english", false);
_dpg.Init();
var filename = "Dpg10Export_" + DateTime.Now.ToString("yyyyMMdd_HHmm") + ".csv";
List<Tuple<double, double>> results = new List<Tuple<double, double>>();
var measurement = new Measurement();
var resultCode = RunMeasurement(60, 1000, 2200, ref measurement, null /* TODO: ref ProgressBar? */);
using (var fileStream = new StreamWriter(filename))
{
var count = measurement.I_inc_Values.Count;
for (var i = 0; i < count; i++)
{
var item = measurement.I_inc_Values[i + 1];
var current = (float)Convert.ToDouble(item);
item = measurement.LI_inc_Values[i + 1];
var inductance = (float)Convert.ToDouble(item);
var line = current.ToString() + ";" + inductance.ToString() + ";";
fileStream.WriteLine(line);
currentList.Add(current);
inductanceList.Add(inductance);
results.Add(new Tuple<double, double>(current,inductance));
if (i == 0 || (i + 1) % 32 == 0)
{
Console.WriteLine((i + 1) + ": " + line);
}
}
}
return results;
}
This code produces a csv-file that looks something like this:
0,22 | 0,44
0,32 | 0,54
0,44 | 0,65
And those values produce a curve that looks like this:
When you click on the "get Waveform" button, the curve above is generated. However, I want to display all the curves that has been generated, and a trendline as well. How would I achieve this?
void BindData(List<Tuple<double,double>> results)
{
chart.Series.Clear();
var series1 = new System.Windows.Forms.DataVisualization.Charting.Series
{
Name = "curr/induc",
Color = System.Drawing.Color.Green,
IsVisibleInLegend = true,
IsXValueIndexed = true,
ChartType = SeriesChartType.Line
};
foreach (var i in results)
{
series1.Points.AddXY(i.Item2,i.Item1);
}
chart.Invalidate();
chart.Series.Add(series1);
}
private void getWaveformBtn_Click(object sender, EventArgs e)
{
Dpg10Client.Dpg10Settings settings = new Dpg10Client.Dpg10Settings();
Dpg10Instrument hej = new Dpg10Instrument(settings);
List<Tuple<double,double>> results = hej.runTest();
double current, inductance;
foreach(var i in results)
{
current = i.Item1;
inductance = i.Item2;
textBoxWaveformInput.Text += current.ToString() + inductance.ToString();
}
BindData(results);
}
TL;DR
Parse the information from the CSV files to generate curves, and create a trendline based on those files.
Marked as duplicate: That answer is regarding a straight line, these values can fluctuate in a curve.
There are many ways to solve most of the various problems in your question.
Let me tackle only the one that actually has to do with calculating an average from multiple series, i.e. will not deal with creating a Series with data from a CSV file.
There is a built-in class that can do all sorts of advanced math, both financial and statistical, but I didn't find one that will help in creating an average line/curve over more than one series.
So let's do it ourselves..
The first issue is that to calculate averages we need not just data but the data, that is their x-values, must be grouped into 'bins' from which we want to get the averages.
Unless the data already are grouped like that, e.g. because they have one value per series per day, we need to create such groups.
Let's first collect all the points from all series we want to handle; you may need to adapt the loop to include just your set of series..:
var allPoints = new List <DataPoint>();
for (int s = 0; s < 3; s++) // I know I have created these three series
{
Series ser = chartGraphic.Series["S" + (s+1)];
allPoints.AddRange(ser.Points);
}
Next we need to decide on a bin range/size, that is a value that determines which x-values shall fall into the same bin/group. I chose to have 10 bins.
So we best get the total range of the x-values and divide by the number of bins we want..:
double xmax = allPoints.Max(x => x.XValue);
double xmin = allPoints.Min(x => x.XValue);
int bins = 10;
double groupSize = (xmax - xmin) / (bins - 1);
Next we do the actual math; for this we order our points, group them and select the average for each grouped set..:
var grouped = allPoints
.OrderBy(x => x.XValue)
.GroupBy(x => groupSize * (int)(x.XValue /groupSize ))
.Select(x => new { xval = x.Key, yavg = x.Average(y => y.YValues[0]) })
.ToList();
Now we can add them to a new series to display the averages in the bins:
foreach (var kv in grouped) avgSeries.Points.AddXY(kv.xval + groupSize/2f, kv.yavg);
I center the average point in the bins.
Here is an example:
A few notes on the example:
The average line doesn't show a real 'trend' because my data are pretty much random.
I have added Markers to all series to make the DataPoints stand out from the lines. Here it is how I did it for the averages series:
avgSeries.MarkerStyle = MarkerStyle.Cross;
avgSeries.MarkerSize = 7;
avgSeries.BorderWidth = 2;
I have added a StripLine to show the bins. Here is how:
StripLine sl = new StripLine();
sl.BackColor = Color.FromArgb(44,155,155,222);
sl.StripWidth = groupSize;
sl.Interval = groupSize * 2;
sl.IntervalOffset = chartGraphic.ChartAreas[0].AxisX.IntervalOffset;
chartGraphic.ChartAreas[0].AxisX.StripLines.Add(sl);
I have also set one point to have a really low value of -300 to demonstrate how this will pull down the average in its bin. To keep the chart still nicely centered on the normal range of data I have added a ScaleBreakStyle to the y-axis:
chartGraphic.ChartAreas[0].AxisY.ScaleBreakStyle.BreakLineStyle = BreakLineStyle.Wave;
chartGraphic.ChartAreas[0].AxisY.ScaleBreakStyle.Enabled = true;

Skipping lines of text file

So I had some assistance with this yesterday but now I needed to make a change and my change doesn't seem to work. Wanted to see if someone would help me with what I'm doing wrong.
So here is an example of the file read in
Now originally this was reading the FSD number of 0.264 then reading the first number in the first column of 3.4572 and the last number of that column. This worked fine. But now I know longer need that FSD number and I no longer need the first number 3.4572. Instead I need the first number that doesn't have 0.00 in the last column, and then the last number still. So this is what I have but it isn't grabbing anything at all, if (dataWithAvgVolts.Count() > 1) is skipped.
public partial class FrmTravelTime : Form
{
const string FSD__Line_Identifier = "Drilling Data";
const string Data_Start_Point_Identifier = "AVG_VOLTS";
const string Spark_Point_Identifier = "0.00";
static readonly char[] splitter = { ' ', '\t' };
public FrmTravelTime()
{
InitializeComponent();
}
private void btnCalculate_Click(object sender, EventArgs e)
{
foreach (DataGridViewRow row in DGV_Hidden.Rows)
{
FileInfo info = new FileInfo();
{
var lines = File.ReadAllLines(row.Cells["colfilelocation"].Value.ToString());
var fsdLine = lines.FirstOrDefault(line => line.Contains(FSD__Line_Identifier));
var dataWithAvgVolts = lines.SkipWhile(line => line.Contains(Data_Start_Point_Identifier + Spark_Point_Identifier + FSD__Line_Identifier)).ToList();
if (dataWithAvgVolts.Count() > 1)
{
var data = dataWithAvgVolts[1].Split(splitter);
info.startvalue = Convert.ToDouble(data[0]);
data = dataWithAvgVolts[dataWithAvgVolts.Count - 1].Split(splitter);
info.endvalue = Convert.ToDouble(data[0]);
}
info.finalnum = info.startvalue - info.endvalue;
}
}
}
public class FileInfo
{
public double startvalue;
public double endvalue;
public double firstnum;
public double finalnum;
}
So you see here the first line that finally does not have 0.00 is
3.0164 7793 1 0 0.159 0.02
So my info.startvalue will be 3.0164
This goes down a good 100ish lines until the last line of
2.7182 8089 0 0 0.015 22.19
So my info.endvalue should be 2.7182
#Amit
var lines = File.ReadAllLines(row.Cells["colfilelocation"].Value.ToString());
var fsdLine = lines.FirstOrDefault(line => line.Contains(FSD__Line_Identifier));
info.FSD = fsdLine.Substring(fsdLine.IndexOf(FSD_Identifier) + FSD_Identifier.Length, 7);
var dataWithAvgVolts = lines.SkipWhile(line => !line.Contains(Data_Start_Point_Identifier)).ToList();
var data = lines.Where(line => (!line.Contains(Data_Start_Point_Identifier) && !line.Contains(FSD__Line_Identifier) && !line.EndsWith("0.00"))).ToList();
if (data.Count > 1)
{
var line = data[0];
var tempdata = data[0].Split(splitter);
info.startvalue = Convert.ToDouble(tempdata[0]);
var thisdata = data[data.Count - 1].Split(splitter);
info.endvalue = Convert.ToDouble(thisdata[0]);
}
Your SkipWhile condition is incorrect.
line => !line.Contains(Data_Start_Point_Identifier + Spark_Point_Identifier + FSD__Line_Identifier)
Will mean AVG_VOLTS0.00Drilling Data . You are saying, skip the text file till the line does not contain AVG_VOLTS0.00Drilling Data. You do not have this is one line anywhere. So, basically, you seem to be skipping the entire file. Put up a else condition, that will work.
This line returns all rows which do not have "Drilling Data" word, "AVG_VOLTS" word, and do not end with "0.00".
var data = lines.Where(line=>(!line.Contains(Data_Start_Point_Identifier) && !line.Contains(FSD__Line_Identifier) && !line.EndsWith("0.00"))).ToList();
Then, take the rows, and process.
If condition can be
if (data.Count > 1)
{
var line = data[0];
var tempdata = data[0].Split(splitter);
info.startvalue = Convert.ToDouble(tempdata[0]);
var thisdata = data[data.Count - 1].Split(splitter);
info.endvalue = Convert.ToDouble(thisdata[0]);
}

Get first value in column from CSV file in C#

I am using this code in my Web Api to get data from a csv file, and plug that data into a Item List.
private List<Item> ietms = new List<Item>();
public ItemRepository()
{
string filename = HttpRuntime.AppDomainAppPath + "App_Data\\items.csv";
var lines = File.ReadAllLines(filename).Skip(1).ToList();
for (int i = 0; i < lines.Count; i++)
{
var line = lines[i];
var columns = line.Split('$');
//get rid of newline characters in the middle of data lines
while (columns.Length < 9)
{
i += 1;
line = line.Replace("\n", " ") + lines[i];
columns = line.Split('$');
}
//Remove Starting and Trailing open quotes from fields
columns = columns.Select(c => { if (string.IsNullOrEmpty(c) == false) { return c.Substring(1, c.Length - 2); } return string.Empty; }).ToArray();
items.Add(new Item()
{
Id = int.Parse(columns[0]),
Name = columns[1],
Description = columns[2],
Price = string.IsNullOrEmpty(columns[3].Trim()) ? null : (double?)double.Parse(columns[3]),
Weight = columns[8],
PhotoUrl = columns[7],
Category=columns[9]
});
}
}
In the csv file one of the columns/value is structured like this:
Groups>Subgroup>item
or in some cases
MajorGroup|Groups>Subgroup>item
How do I pull out only the first value before the > or |, so that I would get the value as Groups in the first case and MajorGroup in the second, and store it in the Category property in the Item List, which is now just set to the entire value in column 9 which would return the whole string "Groups>Subgroup>item".
Add the following line before calling "Add item"
var temp = columns[9].Split('|', '>');
Then assign the category as follows.
Category = temp[0];
Based on: MSDN String Method Documentation
Did you mean something like this?
string data = "MajorGroup|Groups>Subgroup>item";
string groupOrCategory;
if (data.Contains('|'))
{
groupOrCategory = data.Substring(0, data.IndexOf('|'));
}
else
{
groupOrCategory = data.Substring(0, data.IndexOf('>'));
}
Console.WriteLine(groupOrCategory);

Categories