【问题标题】:Doing pivot with LINQ使用 LINQ 进行数据透视
【发布时间】:2023-04-04 04:55:01
【问题描述】:

我遇到了这个问题..我有一个格式如下的 CSV 文件(客户,购买的商品对):

customer1 item1
customer1 item2
customer1 item3
customer2 item4
customer2 item2
customer3 item5
customer3 item1
customer3 item2
customer4 item1
customer4 item2
customer5 item5
customer5 item1

现在,我希望在查询结果中显示:

item x; item y; how many customers have bought itemx and item together

例如:

item1 item2 3 (because cust1 and cust2 and cust3 bought item1 and item2 together)
item1 item5 1 (because cust5 and cust3 bought item1 and item5 together)

查询返回客户成对购买的所有可能的商品组合。另请注意,Pair(x, y) 与 Pair(y, x) 相同。

SQL 查询如下所示:

SELECT a1.item_id, a2.item_id, COUNT(a1.cust_id) AS how_many_custs_bought_both
  FROM data AS a1
INNER JOIN data AS a2
  ON a2.cust_id=a1.cust_id AND a2.item_id<>a1.item_id AND a1.item_id<a2.item_id
GROUP BY a1.item_id, a2.item_id

您将如何在 C# 中做到这一点 1) 使用常规 for/foreach 循环 2) 使用 LINQ ?

我首先尝试在 LINQ 中执行此操作,但当我注意到 LINQ 不支持 join 子句中的多个 equals 关键字时卡住了。然后我尝试使用普通循环,但是它变得如此低效,以至于它每秒只能处理 30 行(CSV 文件行)。

请指教!

【问题讨论】:

  • 您尝试过的 linq 语句在哪里?还有那个 .csv 文件的分隔符是什么。还有为什么不将该查询放入存储过程或为什么不使用system.data.sqlclient 类并使用参数化查询..?

标签: c# linq


【解决方案1】:

使用 LINQ(并遵循 Tim 回答的前 5 行)将链式方法语法与连接部分的查询语法相结合:

var custItems =  new [] {
    new { customer = 1, item = 1 },
    new { customer = 1, item = 2 },
    new { customer = 1, item = 3 },
    new { customer = 2, item = 4 },
    new { customer = 2, item = 2 },
    new { customer = 3, item = 5 },
    new { customer = 3, item = 1 },
    new { customer = 3, item = 2 },
    new { customer = 4, item = 1 },
    new { customer = 4, item = 2 },
    new { customer = 5, item = 5 },
    new { customer = 5, item = 1 }
};
};

var pairs = custItems.GroupBy(x => x.customer)
         .Where(g => g.Count() > 1)
         .Select(x =>  (from a in x.Select( y => y.item )
                        from b in x.Select( y => y.item )
                        where a < b //If you want to avoid duplicate (a,b)+(b,a)
                        // or just: where a != b, if you want to keep the dupes.
                        select new { a, b}))
         .SelectMany(x => x)
         .GroupBy(x => x)
         .Select(g => new { Pair = g.Key, Count = g.Count() })
         .ToList();

pairs.ForEach(x => Console.WriteLine(x));

编辑:忘记了 OP 想要对出现计数,添加了另一个 .GroupBy() 魔法。

编辑:完成示例以显示它将输出的内容:

{ Pair = { a = 1, b = 2 }, Count = 3 }
{ Pair = { a = 1, b = 3 }, Count = 1 }
{ Pair = { a = 2, b = 3 }, Count = 1 }
{ Pair = { a = 2, b = 4 }, Count = 1 }
{ Pair = { a = 1, b = 5 }, Count = 2 }
{ Pair = { a = 2, b = 5 }, Count = 1 }

编辑:回滚并将字符串更改为整数,因为 OP 显示了一个以整数作为 ID 的数据集,因此不再需要 .GetHashCode()

【讨论】:

  • 抱歉,刚刚发现,这根本行不通。它不会输出正确的信息。
  • 嘿@user315648,我已经用生成的输出完成了这个例子。为什么它不起作用?这不是你想要的吗?
  • 抱歉含糊其辞,输出重复信息。例如: (Item1, Item2) 对与 (Item2, Item1) 相同。上面的代码将 (a, b) 与 (b, a) 不同,这是错误的..
  • 哦,你的意思是它实际上应该输出重复。我将编辑答案(将 where a.GetHashCode() &lt; b.GetHashCode() 更改为 a != b)我会删除我的答案,但方法不同,所以如果它留下来也许会很好。
  • 哦,该死的对不起,我认为它把 (a, b) THE SAME 当作 (b, a),这是错误的.. "where a != b" 与它无关,它只删除 (item1, item1) 重复项,但不删除 (item1, item2) (item2, item1) 重复项。
【解决方案2】:

也许:

var lines = File.ReadLines(csvFilePath);
var custItems = lines
    .Select(l => new { split = l.Split() })
    .Select(x => new { customer = x.split[0].Trim(), item = x.split[1].Trim() })
    .ToList();

var groups = from ci1 in custItems
             join ci2 in custItems
             on ci1.customer equals ci2.customer
             where ci1.item != ci2.item 
             group new { Item1 = ci1.item, Item2 = ci2.item } by new { Item1 = ci1.item, Item2 = ci2.item } into ItemGroup
             select ItemGroup;

var result = groups.Select(g => new
{
    g.Key.Item1,
    g.Key.Item2,
    how_many_custs_bought_both = g.Count()
});

请注意,由于自连接,当文件很大时,ToList 的具体化很重要。

{ Item1 = item1, Item2 = item2, how_many_custs_bought_both = 3 }
{ Item1 = item1, Item2 = item3, how_many_custs_bought_both = 1 }
{ Item1 = item2, Item2 = item1, how_many_custs_bought_both = 3 }
{ Item1 = item2, Item2 = item3, how_many_custs_bought_both = 1 }
{ Item1 = item3, Item2 = item1, how_many_custs_bought_both = 1 }
{ Item1 = item3, Item2 = item2, how_many_custs_bought_both = 1 }
{ Item1 = item4, Item2 = item2, how_many_custs_bought_both = 1 }
{ Item1 = item2, Item2 = item4, how_many_custs_bought_both = 1 }
{ Item1 = item5, Item2 = item1, how_many_custs_bought_both = 2 }
{ Item1 = item5, Item2 = item2, how_many_custs_bought_both = 1 }
{ Item1 = item1, Item2 = item5, how_many_custs_bought_both = 2 }
{ Item1 = item2, Item2 = item5, how_many_custs_bought_both = 1 }

【讨论】:

  • 该代码还将 (item1, item2) 与 (item2, item1) 不同,这是错误的。它输出重复..
  • 抱歉,它对待它们的方式相同。
【解决方案3】:

你可以这样写:

IDictionary<int, int> pivotResult = customerItems.ToLookup(c => c.Customer)
                                 .ToDictionary(x=>x.Key, y=>y.Count());

【讨论】:

    【解决方案4】:

    工作 LINQ 示例,不太漂亮!

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    class Data
    {
        public Data(int cust, int item)
        {
            item_id = item;
            cust_id = cust;
        }
        public int item_id { get; set; }
        public int cust_id { get; set; }
    
        static void Main(string[] args)
        {
            var data = new List<Data>
                           {new Data(1,1),new Data(1,2),new Data(1,3),
                            new Data(2,4),new Data(2,2),new Data(3,5),
                            new Data(3,1),new Data(3,2),new Data(4,1),
                            new Data(4,2),new Data(5,5),new Data(5,1)};
    
               (from a1 in data
                from a2 in data
                where a2.cust_id == a1.cust_id && a2.item_id != a1.item_id && a1.item_id < a2.item_id
                group new {a1, a2} by new {item1 = a1.item_id, item2 = a2.item_id}
                into g
                select new {g.Key.item1, g.Key.item2, count = g.Count()})
                .ToList()
                .ForEach(x=>Console.WriteLine("{0} {1} {2}",x.item1,x.item2,x.count))
                ;
               Console.Read();
        }
    }
    

    输出:

    1 2 3
    1 3 1
    2 3 1
    2 4 1
    1 5 2
    2 5 1
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-11
      • 2010-12-07
      • 2013-05-15
      • 1970-01-01
      • 1970-01-01
      • 2022-01-03
      相关资源
      最近更新 更多