1

次のデータセットがあります。

Year   Category  Score
2011   A         83
2012   A         86
2013   A         62
2011   B         89
2012   B         86
2013   B         67
2011   C         85
2012   C         73
2013   C         79
2011   D         95
2012   D         78
2013   D         67

次の構造に変換したい。

categories: [2011, 2012, 2013], 
series: [
   { data: [83, 86, 62], name: 'A' }, 
   { data: [85, 73, 79], name: 'B' }, 
   { data: [83, 86, 62], name: 'C' }, 
   { data: [95, 78, 67], name: 'D' }]

ソース データ セット内の「欠落」データをコードで許容できるようにしたいと考えています。各年とカテゴリの少なくとも 1 つがソース データで表されていることは、安全な仮定です。

「スケッチ」データの例

Year   Category  Score
2011   A         83
// 2012 A is missing
2013   A         62
// 2011 B is missing    
2012   B         86
2013   B         67
2011   C         85
// 2012 C is missing    
2013   C         79
2011   D         95
2012   D         78
2013   D         67

これが得られるはずです:

categories: [2011, 2012, 2013], 
series: [
   { data: [83,  0, 62], name: 'A' }, 
   { data: [ 0, 73, 79], name: 'B' }, 
   { data: [83,  0, 62], name: 'C' }, 
   { data: [95, 78, 67], name: 'D' }]
4

1 に答える 1

0

Pastebin コードから次の LINQPad コードを作成しました - 実装に続くメモを参照してください。

void Main()
{
    var scores = new [] {
        new CScore { Year = 2011, Category = 'A', Score = 83 },
        // 2012 A is missing
        new CScore { Year = 2013, Category = 'A', Score = 62 },
        // 2011 B is missing   
        new CScore { Year = 2012, Category = 'B', Score = 86 },
        new CScore { Year = 2013, Category = 'B', Score = 67 },
        new CScore { Year = 2011, Category = 'C', Score = 85 },
        // 2012 C is missing 
        new CScore { Year = 2013, Category = 'C', Score = 79 },
        new CScore { Year = 2011, Category = 'D', Score = 95 },
        new CScore { Year = 2012, Category = 'D', Score = 78 },
        new CScore { Year = 2013, Category = 'D', Score = 67 },
    };

    int[] years = scores.Select(i => i.Year).Distinct()
        .OrderBy(i=>i).ToArray();
    char[] categories = scores.Select(i => i.Category).Distinct()
        .OrderBy(i=>i).ToArray();

    var series =
        from year in years
        from cat in categories
        join score in scores
        on new { Year = year, Category = cat }
        equals new { score.Year, score.Category } into scoreGroup
        select scoreGroup.SingleOrDefault() ??
            new CScore { Year = year, Category = cat } into scoreWithDefault
        group scoreWithDefault.Score by scoreWithDefault.Category into g
        select new Series { Name = g.Key.ToString(), Data = g.ToArray() };

    years.Dump(); // categories
    series.Dump(); // series
}

class CScore
{
    public char Category {get;set;}
    public int Year {get;set;}
    public int Score {get;set;}
}

class Series
{
    public string Name {get;set;}
    public int[] Data {get;set;}
}

コメント

  1. CScore- 発生した命名エラーを避けるために名前を変更しました
  2. 入力データに応じて潜在的な順序の問題を回避するために、個別のアイテムを並べ替えました。
  3. シリーズ クエリ:
    1. from 句は、すべてのカテゴリ/年の組み合わせの外積を形成します。
    2. は、欠落している年join..intoのデフォルトの生成を許可しますCScore
    3. SingleOrDefault入力データに結合で一致するCScoreアイテムが複数ある場合、冗長性に対処するためにさらに何かを行う必要があることを示すクエリがスローされるように選択しました。これは、この悪いデータ/奇妙なデータのケースで失敗しないInvalidOperationExceptionよりも好ましいと思います。FirstOrDefault
    4. Score = 00 がデフォルトであるため、CScore初期化ブロックでは省略されます。
    5. select..intoクエリの継続group..byにより、カテゴリ/名前でスコアをグループ化する にクエリをフィードできました。ここでnull合体演算子に本当に感謝しています。
    6. group..by..into g-- このタイプは、group-by で停止した場合に使用Seriesするタイプに類似しています。IGrouping<char,int>代わりに、最後の select は、IGrouping の型を目的のSeries型に投影します。

LINQPad の出力で答えを検証し、「これを生成する必要がある」サンプル出力データにいくつかの欠陥を発見しました。また、このコードは私のマシンでは約 1 ミリ秒で実行されるため、処理するデータがこれよりも多くない限り、改善しようとは思わないでしょう。

まだまだお話しできることはありますが、そのままにしておきます。うまくいけば、私は誰も失うことはありませんでした。

于 2013-02-15T04:36:39.443 に答える