我有一个CSV文件,看起来像这样。
,Location_Code,Location_Desc,Type_Code,Fault_type,Prod_Number,Model,Causer,Auditor,Prio,Capture_Date,Steer,Engine,Country,Current shift number,VIN,Comment,Shift,Year,Fault location C_Code,Fault location C_Desc,Fault type C_Code,Fault type C_Desc,Comment R,Baumuster Sales desc.,Baumuster Technical desc.,T24
0,09122,Engine,42,Poor fit,7117215,W205,Final 3,"Plant 1, WSA",0,2019-04-05,1,83,705,T1220190404T0092,55SWF8DB7KU316971,,A,2019,,,,,,C 300,205 E20 G,
1,09122,Engine,42,Poor fit,7117235,W205,Final 3,"Plant 1, WSA",0,2019-04-05,1,83,705,T1220190404T0122,55SWF8DB2KU316991,,A,2019,,,,,,C 300,205 E20 G,
2,09122,Transmission,42,Poor fit,7117237,W205,Final 3,"Plant 1, WSA",0,2019-04-05,1,83,705,T1220190404T0126,55SWF8DB6KU316993,,A,2019,,,,,,C 300,205 E20 G,
我想写一段代码,在对所选列的单词进行标记后,得到所选列标题的单词数(以字典风格的键值对)。 我还想让单词数按值降序排列,例如:
Location_Desc
Engine: 2
Transmission: 1
这是我目前的代码。
int colNumber;
for(colNumber=0; colNumber<columns.Length; colNumber++)
{
if ( columns[colNumber].Equals(columnHeader))
{
break;
}
}
Debug.WriteLine("Column Number: " + colNumber);
for(int i=0; i<inputCsv.Length; i++)
{
string[] row = inputCsv[i].Split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)");
string column = row[colNumber];
Debug.WriteLine(row.ToString());
}
我可以通过for循环获得列头名称,但我不仅无法忽略引号内的逗号,也无法从列头(在Python的Pandas中也被称为Series)获得值。
非常感谢帮助
我可能会把你的计数存储在 Dictionary<string, Dictionary<string , long>>
而不是一个二维数组。这样你就可以更容易地访问每个列的计数。
使用 CsvHelper
NuGet包,我们可以创建一个类来模拟你的CSV文件。我们在这里唯一要注意的是为你的列选择正确的数据类型。我选择的数据类型可能不是最适合你的情况。你也可以在API文档中找到 此处.
public class CsvModel
{
[Name("")]
public string RowNumber { get; set; }
[Name("Location_Code")]
public string LocationCode { get; set; }
[Name("Location_Desc")]
public string LocationDesc { get; set; }
[Name("Type_Code")]
public long TypeCode { get; set; }
[Name("Fault_type")]
public string FaultType { get; set; }
[Name("Prod_Number")]
public long ProdNumber { get; set; }
public string Model { get; set; }
public string Causer { get; set; }
public string Auditor { get; set; }
public long Prio { get; set; }
[Name("Capture_Date")]
public DateTime CaptureDate { get; set; }
public long Steer { get; set; }
public long Engine { get; set; }
public long Country { get; set; }
[Name("Current shift number")]
public string CurrentShiftNumber { get; set; }
public string VIN { get; set; }
public string Comment { get; set; }
public string Shift { get; set; }
public long Year { get; set; }
[Name("Fault location C_Code")]
public string FaultLocationCCode { get; set; }
[Name("Fault location C_Desc")]
public string FaultLocationCDesk { get; set; }
[Name("Fault type C_Code")]
public string FaultTypeCCode { get; set; }
[Name("Fault type C_Desc")]
public string FaultTypeCDesc { get; set; }
[Name("Comment R")]
public string CommentR { get; set; }
[Name("Baumuster Sales desc.")]
public string BaumusterSalesDesc { get; set; }
[Name("Baumuster Technical desc.")]
public string BaumusterTechnicalDesc { get; set; }
public string T24 { get; set; }
}
然后,我们可以将这些记录读入 IEnumerable<CsvMode>
与 GetRecords<T>
:
var path = "C:\\data.csv";
using var reader = new StreamReader(path);
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);
var records = csv.GetRecords<CsvModel>();
然后用反射法将列数转化为一个列数。Dictionary<string, Dictionary<string , long>>
:
var recordCounts = new Dictionary<string, Dictionary<string, long>>();
foreach (var record in records)
{
var properties = record.GetType().GetProperties();
foreach (var property in properties)
{
var propertyName = property.Name;
if (!recordCounts.ContainsKey(propertyName))
{
recordCounts.Add(propertyName, new Dictionary<string, long>());
}
var propertyValue = property.GetValue(record, null);
var propertyKey = propertyValue.ToString();
if (propertyValue != null && !string.IsNullOrEmpty(propertyKey))
{
var count = recordCounts[propertyName].GetValueOrDefault(propertyKey, 0) + 1;
recordCounts[propertyName][propertyKey] = count;
}
}
}
然后,我们可以通过用LINQ创建一个新的字典,将列数按降序排序。
var sortedRecordCounts = recordCounts
.ToDictionary(
kvp => kvp.Key,
kvp => new SortedDictionary<string, long>(
kvp.Value.OrderByDescending(kvp => kvp.Value)
.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value)));
它使用 Enumerable.ToDictionary
来创建字典(内层+外层),并用 Enumerable.OrderByDescending
.
我们还使用 OrderedDictionary
来保证字典的排序顺序,因为 Dictionary
订购不保证。
这一点在 MSDN:
返回项的顺序是未定义的。
然后,我们可以迭代这个字典来显示记录数,这也表明是否没有找到有效的(空或空)值。
foreach (var kvp in sortedRecordCounts)
{
Console.WriteLine($"Column: {kvp.Key}");
if (kvp.Value.Count == 0)
{
Console.WriteLine("No values found");
}
foreach (var value in kvp.Value)
{
Console.WriteLine($"Value: {value.Key}, Count: {value.Value}");
}
Console.WriteLine();
}
输出:
Column: RowNumber
Value: 0, Count: 1
Value: 1, Count: 1
Value: 2, Count: 1
Column: LocationCode
Value: 09122, Count: 3
Column: LocationDesc
Value: Engine, Count: 2
Value: Transmission, Count: 1
Column: TypeCode
Value: 42, Count: 3
Column: FaultType
Value: Poor fit, Count: 3
Column: ProdNumber
Value: 7117215, Count: 1
Value: 7117235, Count: 1
Value: 7117237, Count: 1
Column: Model
Value: W205, Count: 3
Column: Causer
Value: Final 3, Count: 3
Column: Auditor
Value: Plant 1, WSA, Count: 3
Column: Prio
Value: 0, Count: 3
Column: CaptureDate
Value: 5/04/2019 12:00:00 AM, Count: 3
Column: Steer
Value: 1, Count: 3
Column: Engine
Value: 83, Count: 3
Column: Country
Value: 705, Count: 3
Column: CurrentShiftNumber
Value: T1220190404T0092, Count: 1
Value: T1220190404T0122, Count: 1
Value: T1220190404T0126, Count: 1
Column: VIN
Value: 55SWF8DB7KU316971, Count: 1
Value: 55SWF8DB2KU316991, Count: 1
Value: 55SWF8DB6KU316993, Count: 1
Column: Comment
No values found
Column: Shift
Value: A, Count: 3
Column: Year
Value: 2019, Count: 3
Column: FaultLocationCCode
No values found
Column: FaultLocationCDesk
No values found
Column: FaultTypeCCode
No values found
Column: FaultTypeCDesc
No values found
Column: CommentR
No values found
Column: BaumusterSalesDesc
Value: C 300, Count: 3
Column: BaumusterTechnicalDesc
Value: 205 E20 G, Count: 3
Column: T24
No values found
如果你想支持多个CSV文件,并且不为列存储一个类(也避免了反射),你可以使用像这样的通用解决方案。
var path = "C:\\data.csv";
var recordCounts = new Dictionary<string, Dictionary<string, long>>();
using (var reader = new StreamReader(path))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
csv.Read();
csv.ReadHeader();
var headerRow = csv.Context.HeaderRecord;
if (string.IsNullOrEmpty(headerRow[0]))
{
headerRow[0] = "RowNumber";
}
foreach (var header in headerRow)
{
recordCounts.Add(header, new Dictionary<string, long>());
}
while (csv.Read())
{
foreach (var header in headerRow)
{
var headerKey = header == "RowNumber" ? string.Empty : header;
var columnValue = csv.GetField(headerKey);
if (!string.IsNullOrEmpty(columnValue))
{
var count = recordCounts[header].GetValueOrDefault(columnValue, 0) + 1;
recordCounts[header][columnValue] = count;
}
}
}
}
它使用的是来自 使用CsvHelper获取列名列表的最佳方法是什么?列表,并使用 手读 的方法提供的。CsvHelper
文件。这些资源和建议是由 @Iliar Turdushev 的注释中。
然后,你可以将上述解决方案与上面的LINQ字典排序查询和打印代码结合起来,产生类似的结果。