C# 从 CSV 文件中读取字符串并绘制折线图
C# Read string from CSV file and plot line graph
目前,我可以使用 windows 表单应用程序从多个 CSV 文件中读取数据并绘制折线图。但是,现在我需要根据 CSV 文件的部分名称(csv 文件的第 3 列)绘制折线图。
Modified/New CSV 文件:(添加了部分名称列)
Values,Sector,Name
5.55,1024,red
5.37,1536,red
5.73,2048,blue
5.62,2560,.blue
5.12,3072,.yellow
...
- 根据 Section Name 列,我的折线图需要相应地绘制成 Single line 并且必须绘制不同的部分不同的颜色,包括图表旁边显示的图例必须根据不同的部分名称显示。
- 1 个 csv 文件 = 1 个系列。但是 csv 文件中有相同的部分名称(上面显示的 csv 文件示例,例如第一个 20 行为红色)。 相同的部分名称 = 相同的颜色。如果我打开 2 个或更多 csv 文件 = 2 个系列。由于 csv 文件中的不同部分名称,每个系列将有不同的颜色。
我对编程很陌生,非常感谢有人可以通过编辑我的代码来帮助我。
谢谢。
您可以按部分列分隔数据,并将部分名称用作 Series
集合的索引,而不是使用 i
.
最好使用节名作为Series.Name
。我建议使用包含两个数字和字符串的数据 class,并将它们收集在 List<Dataclass>
中。然后为不同的部分创建 Series
。然后遍历它们..
以下是一些代码示例:
为您的数据定义一个 class
:
public class Data3
{
public int N1 { get; set;}
public double N2 { get; set;}
public string S1 { get; set;}
public Data3(double n2, int n1, string s1)
{
N1 = n1; N2 = n2; S1 = s1;
}
}
选择你自己的名字!可选但始终推荐:添加一个不错的 ToString()
重载!
声明一个class级变量:
List<Data3> data = new List<Data3>();
在读取期间收集数据:
data.Add(new Data3(Convert.ToDouble(pieces[1]), Convert.ToInt32(pieces[0]), pieces[2]));
要绘制图表,首先创建 Series
:
var sections= data.Select(x => x.S1).Distinct<string>();
foreach (string s in sections)
chart.Series.Add(new Series(s) { ChartType = SeriesChartType.Line });
然后绘制数据;该系列可以通过 Names
:
索引
foreach (var d in data) chart.Series[d.S1].Points.AddXY(d.N1, d.N2);
我遗漏了将代码集成到您的应用程序中的细节;如果您 运行 遇到问题,请通过编辑您的问题来显示新代码!
一些注意事项:
如有疑问,请始终创建一个 class 来保存您的数据
如有疑问,请始终选择 classes 而不是结构
如有疑问,请始终选择 List<T>
而不是数组
始终尝试将您的代码分解为具有有用名称的小块。
示例:要读取 csv 文件中的所有数据,请创建一个函数来执行此操作:
public void AppendCsvToDataList(string file, List<Data3> list)
{
if (File.Exists(file))
{
var lines = File.ReadAllLines(file);
for (int l = 1; l < lines.Length; l++)
{
var pieces = lines[l].Split(',');
list.Add(new Data3(Convert.ToInt32(pieces[1]),
Convert.ToDouble(pieces[0]), pieces[2]));
}
}
}
更新代码:
图形演示(表格):
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 阅读:
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 剧情:
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()
}
}
}
目前,我可以使用 windows 表单应用程序从多个 CSV 文件中读取数据并绘制折线图。但是,现在我需要根据 CSV 文件的部分名称(csv 文件的第 3 列)绘制折线图。
Modified/New CSV 文件:(添加了部分名称列)
Values,Sector,Name
5.55,1024,red
5.37,1536,red
5.73,2048,blue
5.62,2560,.blue
5.12,3072,.yellow
...
- 根据 Section Name 列,我的折线图需要相应地绘制成 Single line 并且必须绘制不同的部分不同的颜色,包括图表旁边显示的图例必须根据不同的部分名称显示。
- 1 个 csv 文件 = 1 个系列。但是 csv 文件中有相同的部分名称(上面显示的 csv 文件示例,例如第一个 20 行为红色)。 相同的部分名称 = 相同的颜色。如果我打开 2 个或更多 csv 文件 = 2 个系列。由于 csv 文件中的不同部分名称,每个系列将有不同的颜色。
我对编程很陌生,非常感谢有人可以通过编辑我的代码来帮助我。
谢谢。
您可以按部分列分隔数据,并将部分名称用作 Series
集合的索引,而不是使用 i
.
最好使用节名作为Series.Name
。我建议使用包含两个数字和字符串的数据 class,并将它们收集在 List<Dataclass>
中。然后为不同的部分创建 Series
。然后遍历它们..
以下是一些代码示例:
为您的数据定义一个 class
:
public class Data3
{
public int N1 { get; set;}
public double N2 { get; set;}
public string S1 { get; set;}
public Data3(double n2, int n1, string s1)
{
N1 = n1; N2 = n2; S1 = s1;
}
}
选择你自己的名字!可选但始终推荐:添加一个不错的 ToString()
重载!
声明一个class级变量:
List<Data3> data = new List<Data3>();
在读取期间收集数据:
data.Add(new Data3(Convert.ToDouble(pieces[1]), Convert.ToInt32(pieces[0]), pieces[2]));
要绘制图表,首先创建 Series
:
var sections= data.Select(x => x.S1).Distinct<string>();
foreach (string s in sections)
chart.Series.Add(new Series(s) { ChartType = SeriesChartType.Line });
然后绘制数据;该系列可以通过 Names
:
foreach (var d in data) chart.Series[d.S1].Points.AddXY(d.N1, d.N2);
我遗漏了将代码集成到您的应用程序中的细节;如果您 运行 遇到问题,请通过编辑您的问题来显示新代码!
一些注意事项:
如有疑问,请始终创建一个 class 来保存您的数据
如有疑问,请始终选择 classes 而不是结构
如有疑问,请始终选择
List<T>
而不是数组始终尝试将您的代码分解为具有有用名称的小块。
示例:要读取 csv 文件中的所有数据,请创建一个函数来执行此操作:
public void AppendCsvToDataList(string file, List<Data3> list)
{
if (File.Exists(file))
{
var lines = File.ReadAllLines(file);
for (int l = 1; l < lines.Length; l++)
{
var pieces = lines[l].Split(',');
list.Add(new Data3(Convert.ToInt32(pieces[1]),
Convert.ToDouble(pieces[0]), pieces[2]));
}
}
}
更新代码:
图形演示(表格):
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 阅读:
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 剧情:
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()
}
}
}