【问题标题】:Create 2D Array from CSV and obtain Word Count of Specified Column从 CSV 创建二维数组并获取指定列的字数
【发布时间】:2020-08-20 22:16:11
【问题描述】:

我有一个如下所示的 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 )。

非常感谢您的帮助!

【问题讨论】:

  • 有很多库可以读取 CSV 文件。例如,CsvHelper。考虑使用它来读取 CSV 文件。这将为您节省大量时间。
  • I want to write code that gets the word count of words of a selected column header after tokenizing the words of the selected column (in dictionary style key-value pairs). 我有一个关于你这部分问题的问题。您的样本中的列Fault_type 的预期结果是什么:1. 一对("Poor fit": 3)2. 两对("Poor": 3)("fit": 3)
  • @IliarTurdushev 是一对

标签: c# string multidimensional-array tokenize word-count


【解决方案1】:

我可能会将您的计数存储在Dictionary&lt;string, Dictionary&lt;string , long&gt;&gt;,而不是二维数组中。然后,您可以更轻松地访问每列计数。

使用CsvHelper NuGet 包,我们可以创建一个类来为您的 CSV 文件建模。我们在这里唯一需要注意的是为您的列选择正确的数据类型。我选择的数据类型可能不是最适合您的情况。您还可以找到 API 文档here

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; }
}

然后我们可以用GetRecords&lt;T&gt;将记录读入IEnumerable&lt;CsvMode&gt;

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&lt;string, Dictionary&lt;string , long&gt;&gt;

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;
            }

        }
    }
}

它使用What is the best way to get the list of column names using CsvHelper? 中的标头读取方法,并使用CsvHelper 文档中提供的Reading by Hand 方法。这些资源和建议是由 cmets 中的 @Iliar Turdushev 提出的。

然后,您可以将上述解决方案与上述 LINQ 字典排序查询和打印代码相结合,以产生类似的结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-10
    • 1970-01-01
    • 1970-01-01
    • 2010-11-06
    • 2016-12-27
    相关资源
    最近更新 更多