【C#】縦持ちのデータを集計して横持ちにする【LINQ】


SQLと同じことができるのなら、LINQでデータ分析だってできるはず!

いわゆるピボットテーブルですが、集計後の形が思ったようにならない!
もっとLinqで完結してコントロールできるのでは?
以下、LinqPadで検証します。

データ構造

public class Employee
{
    public int Group { set; get; }
    public string Name { set; get; }
}

仮データ

var employees = new List<Employee>()
{
    new Employee {Group = 1, Name = "A" },
    new Employee {Group = 2, Name = "B" },
    new Employee {Group = 1, Name = "C" },
    new Employee {Group = 3, Name = "D" },
};

とりあえず Linq Pivot で検索して出てくる形

var q = employees
        .GroupBy(c => c.Group)
        .Select(g => new {
            Group = g.Key,
            i1 = g.Where(c => c.Group == 1).Count(),
            i2 = g.Where(c => c.Group == 2).Count(),
            i3 = g.Where(c => c.Group == 3).Count()
    });

q.Dump();

結果

横持ちに。あとはMAXで一通りにすればいい。。。が、ぐぐっても出てこないので、試行錯誤しました。

GroupByするキーがない

ないなら追加すれば

var q = employees
        .GroupBy(c => c.Group)
        .Select(g => new {
            //Group = g.Key,
            i0 = 1,
            i1 = g.Where(c => c.Group == 1).Count(),
            i2 = g.Where(c => c.Group == 2).Count(),
            i3 = g.Where(c => c.Group == 3).Count()
        })
        .GroupBy(t => t.i0)
        .Select(s => new {
            m1 = s.Max(t => t.i1),
            m2 = s.Max(t => t.i2),
            m3 = s.Max(t => t.i3)
        });

q.Dump();

結果

ダミーのキー(i0 = 1)をつくり、このキーで集計(GroupBy(t => t.i0))することで一通りに。

ちなみにデータが0件だったらどうなるのかを調べると

var employees = new List<Employee>()
{
    new Employee {Group = 1, Name = "A" },
    new Employee {Group = 1, Name = "B" },
    new Employee {Group = 1, Name = "C" },
    new Employee {Group = 3, Name = "D" },
};

(Group = 2 が0件)

結果

0件として列が入ります。
Where句を差しておけば、出力の形式が崩れないことがわかりました。

以下全文

void Main()
{
    var employees = new List<Employee>()
    {
        new Employee {Group = 1, Name = "A" },
        new Employee {Group = 2, Name = "B" },
        new Employee {Group = 1, Name = "C" },
        new Employee {Group = 3, Name = "D" },
    };

    var q = employees
            .GroupBy(c => c.Group)
            .Select(g => new {
                //Group = g.Key,
                i0 = 1,
                i1 = g.Where(c => c.Group == 1).Count(),
                i2 = g.Where(c => c.Group == 2).Count(),
                i3 = g.Where(c => c.Group == 3).Count()
            })
            .GroupBy(t => t.i0)
            .Select(s => new {
                m1 = s.Max(t => t.i1),
                m2 = s.Max(t => t.i2),
                m3 = s.Max(t => t.i3)
            });

    q.Dump();
}

// Define other methods and classes here
public class Employee
{
    public int Group { set; get; }
    public string Name { set; get; }
}