Search code examples
c#winformscsvmschart

C# Read string from CSV file and plot line graph


Currently, I am able to read data from multiple CSV file and plot line graph using windows form application. However, now I need to plot a line graph based on a CSV file's section name (3rd column of csv file).

Modified/New CSV file: (Added the Section Name column)

Values,Sector,Name
5.55,1024,red
5.37,1536,red
5.73,2048,blue
5.62,2560,.blue
5.12,3072,.yellow
...
  1. Based on the Section Name column, my line graph need to be plotted accordingly in a Single line and different sections must be plotted with different colors, including the legends shown at the side of the graph must be shown based on the different section names.
  2. 1 csv file = 1 Series. But there are same section names in a csv file (csv file example shown above, e.g. red for the 1st 20lines). Same section names = same color. If I open 2 or more csv files = 2 Series. Each Series will have different colors due to different section names in the csv file.

I am quite new with programming, and would really appreciate someone could help me by editing from my code.

Thank you.


Solution

  • Updated code:

    GraphDemo (Form):

        List<Read> rrList = new List<Read>();
    
        void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenFileDialog ff = new OpenFileDialog();
            Read rr;
    
            ff.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); //"C:\\";
            ff.Filter = "csv files (*.csv)|*.csv|All files (*.*)|*.*";
            ff.Multiselect = true;
            ff.FilterIndex = 1;
            ff.RestoreDirectory = true;
    
            if (ff.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    rrList.Clear();
                    foreach (String file in ff.FileNames) //if ((myStream = ff.OpenFile()) != null)
                    {
                        rr = new Read(file);
                        rrList.Add(rr); 
                    }
    
                    //Populate the ComboBoxes
                    if (rrList.Count > 0)
                    {
                        string[] header = rrList[0].header; //header of first file
                        xBox.DataSource = header; 
                        yBox.DataSource = header.Clone(); //without Clone the 2 comboboxes link together!
                    }
                    if (yBox.Items.Count > 1) yBox.SelectedIndex = 1; //select second item
                }
                catch (Exception err)
                {
                    //Inform the user if we can't read the file
                    MessageBox.Show(err.Message);
                }
            }
        }
    
        private void button1_Click(object sender, EventArgs e)
        {
            Plot.Draw(rrList, xBox, yBox, chart);
        }
    

    class Read:

    public class Read
    {
        public int nLines { get; private set; }
        public int nColumns { get; private set; }
        public string[] header { get; private set; }
        public float[,] data { get; private set; }
        public string fileName { get; set; }
        public string[] section { get; private set; }
    
        public Read(string file)
        {
            string[] pieces;
    
            fileName = Path.GetFileName(file);  
            string[] lines = File.ReadAllLines(file); // read all lines
            if (lines == null || lines.Length < 2) return; //no data in file
            header = lines[0].Split(','); //first line is header
            nLines = lines.Length - 1; //first line is header
            nColumns = header.Length;
    
            //read the numerical data and section name from the file
            data = new float[nLines, nColumns - 1]; // *** 1 less than nColumns as last col is sectionName
            section = new string[nLines]; // *** 
            for (int i = 0; i < nLines; i++) 
            {
                pieces = lines[i + 1].Split(','); // first line is header
                if (pieces.Length != nColumns) { MessageBox.Show("Invalid data at line " + (i + 2) + " of file " + fileName); return; }
                for (int j = 0; j < nColumns - 1; j++)
                {
                    float.TryParse(pieces[j], out data[i, j]); //data[i, j] = float.Parse(pieces[j]);
                }
                section[i] = pieces[nColumns - 1]; //last item is section
            }
        }
    
    }
    

    class Plot:

    public class Plot
    {
        //public Plot() { } //no constructor required as we use a static class to be called
    
        public static void Draw(List<Read> rrList, ComboBox xBox, ComboBox yBox, Chart chart) //***
        {
            int indX = xBox.SelectedIndex;
            int indY = yBox.SelectedIndex;
    
            chart.Series.Clear(); //ensure that the chart is empty
            chart.Legends.Clear();
            Legend myLegend = chart.Legends.Add("myLegend");
            myLegend.Title = "myTitle";
    
            //define a set of colors to be used for sections
            Color[] colors = new Color[] { Color.Black, Color.Blue, Color.Red, Color.Green, Color.Magenta, Color.DarkCyan, Color.Chocolate, Color.DarkMagenta }; 
    
            //use a Dictionary to keep iColor of each section
            // key=sectionName, value=iColor (color index in our colors array)
            var sectionColors = new Dictionary<string, int>();
    
            int i = 0;
            int iColor = -1, maxColor = -1;
            foreach (Read rr in rrList)
            {
                float[,] data = rr.data;
                int nLines = rr.nLines;
                int nColumns = rr.nColumns;
                string[] header = rr.header;
    
                chart.Series.Add("Series" + i);
                chart.Series[i].ChartType = SeriesChartType.Line;
    
                //chart.Series[i].LegendText = rr.fileName;
                chart.Series[i].IsVisibleInLegend = false; //hide default item from legend
    
                chart.ChartAreas[0].AxisX.LabelStyle.Format = "{F2}";
                chart.ChartAreas[0].AxisX.Title = header[indX];
                chart.ChartAreas[0].AxisY.Title = header[indY];
    
                for (int j = 0; j < nLines; j++)
                {
                    int k = chart.Series[i].Points.AddXY(data[j, indX], data[j, indY]);
                    string curSection = rr.section[j];
                    if (sectionColors.ContainsKey(curSection))
                    {
                        iColor = sectionColors[curSection];
                    }
                    else
                    {
                        maxColor++;
                        iColor = maxColor; sectionColors[curSection] = iColor;
                    }
                    chart.Series[i].Points[k].Color = colors[iColor];
                }
    
                i++; //series#
    
            } //end foreach rr
    
            //fill custom legends based on sections/colors
            foreach (var x in sectionColors)
            {
                string section = x.Key;
                iColor = x.Value;
                myLegend.CustomItems.Add(colors[iColor], section); //new LegendItem()
            }
        }
    
    }