【问题标题】:How create groups with evenly sizes如何创建大小均匀的组
【发布时间】:2016-08-12 09:18:38
【问题描述】:

我有一张按日期列出汽车信息的表格。

car_id date       <aditional info>
1      20160101
2      20160102
1      20160103
2      20160104
1      20160105
1      20160106
1      20160107
1      20160108
1      20160109
1      20160110

如果汽车有&lt;= 5 记录,那么将只有一组。 CASE car_id = 2

如果汽车有&lt;= 10 记录,那么将有两个平均组。 CASE car_id = 1 按日期排序

  • 20160101 - 20160106 GROUP 1
  • 20160107 - 20160110 GROUP 2

如果汽车有 '> 10' 则将有三个均匀分布的组。

渴望输出。

car_id date         group_id
1      20160101        1
1      20160103        1
1      20160105        1
1      20160106        1
------------------------
1      20160107        2
1      20160108        2
1      20160109        2
1      20160110        2
------------------------
2      20160102        1
2      20160104        1

我尝试使用ntile(),但无法使组号动态化。

SQL Fiddle Demo

SELECT car_id, 
       "date",
       ntile(3) over (partition by car_id order by "date") as group_id
FROM Table1      

如果可以直接在C# LINQ 上完成加分,否则我会在 postgres 上创建一个函数。

附加信息 我会以不同颜色(组)显示历史车辆信息,因此数据量少的车辆将以单一颜色显示。最大颜色数为 3。

【问题讨论】:

    标签: c# linq postgresql grouping


    【解决方案1】:

    你可以使用ntile:

    SELECT car_id, "date",
            ntile(CASE WHEN c <= 5 THEN 1
                       WHEN c <= 10 THEN 2
                       ELSE 3
                  END)  OVER (PARTITION BY car_id ORDER BY "date") AS group_id
    FROM (SELECT car_id, "date",COUNT(*) OVER(PARTITION BY car_id) AS c
          FROM Table1) AS s
    

    SqlFiddleDemo

    输出:

    ╔════════╦══════════╦══════════╗
    ║ car_id ║   date   ║ group_id ║
    ╠════════╬══════════╬══════════╣
    ║      1 ║ 20160101 ║        1 ║
    ║      1 ║ 20160103 ║        1 ║
    ║      1 ║ 20160105 ║        1 ║
    ║      1 ║ 20160106 ║        1 ║
    ║      1 ║ 20160107 ║        2 ║
    ║      1 ║ 20160108 ║        2 ║
    ║      1 ║ 20160109 ║        2 ║
    ║      1 ║ 20160110 ║        2 ║
    ║      2 ║ 20160102 ║        1 ║
    ║      2 ║ 20160104 ║        1 ║
    ╚════════╩══════════╩══════════╝
    

    【讨论】:

    • 谢谢小伙子,我在 Gordon 回答后提出了相同的解决方案,但由于我的互联网中断而无法发送。但有趣的方法是将案例放在ntile() 中,我为此创建了一个子查询。
    【解决方案2】:

    我会使用row_number()count() 手动计算组:

    select t1.*,
           (case when cnt <= 5 then 1
                 when car_id * 2 <= cnt then 1
                 else 2
            end) as grp
    from (select t1.*,
                 row_number() over (partition by car_id order by date) as seqnum,
                 count(*) over (partition by car_id) as cnt
         from table1
        ) t
    where cnt <= 10
    order by car_id, grp, date;
    

    【讨论】:

    • 感谢 Gordon,这个答案帮助我找到了最终解决方案。
    【解决方案3】:

    SQL Fiddle Demo

    正如戈登建议的那样。

    • 首先计算每个car_id的日期数
    • 然后根据cnt分配我想要的组数
    • 然后将groups 用于ntile(grp) 解析函数

    .

    WITH car_dates_count as (
      select t1.*,             
             count(*) over (partition by car_id) as cnt
      from table1 t1
    ),
    car_groups as (
      select cdc.*,
           (case when cnt <= 5  then 1
                 when cnt <= 10 then 2
                                else 3
            end) as grp
      from car_dates_count cdc
    ) 
    SELECT *,
           ntile(grp) over (partition by car_id order by "date") as group_id
    FROM car_groups;  
    

    输出

    | car_id |     date | cnt | grp | group_id |
    |--------|----------|-----|-----|----------|
    |      1 | 20160101 |   9 |   2 |        1 | \
    |      1 | 20160102 |   9 |   2 |        1 |  |
    |      1 | 20160103 |   9 |   2 |        1 |  |
    |      1 | 20160104 |   9 |   2 |        1 |  |
    |      1 | 20160105 |   9 |   2 |        1 |  |=> (cnt 9 <= 10) Mean two groups
    |      1 | 20160106 |   9 |   2 |        2 |  |
    |      1 | 20160107 |   9 |   2 |        2 |  |
    |      1 | 20160108 |   9 |   2 |        2 |  |
    |      1 | 20160109 |   9 |   2 |        2 | /
    --------------------------------------------
    |      2 | 20160101 |   5 |   1 |        1 | \
    |      2 | 20160102 |   5 |   1 |        1 |  |
    |      2 | 20160103 |   5 |   1 |        1 |  |=> (cnt 5 <= 5) Mean one group
    |      2 | 20160104 |   5 |   1 |        1 |  |
    |      2 | 20160105 |   5 |   1 |        1 | /
    --------------------------------------------
    |      3 | 20160101 |  16 |   3 |        1 | \
    |      3 | 20160102 |  16 |   3 |        1 |  |
    |      3 | 20160103 |  16 |   3 |        1 |  |  
    |      3 | 20160104 |  16 |   3 |        1 |  |
    |      3 | 20160105 |  16 |   3 |        1 |  |
    |      3 | 20160106 |  16 |   3 |        1 |  |
    |      3 | 20160107 |  16 |   3 |        2 |  |
    |      3 | 20160108 |  16 |   3 |        2 |  |=> (cnt 16 > 10) Mean three groups
    |      3 | 20160109 |  16 |   3 |        2 |  |
    |      3 | 20160110 |  16 |   3 |        2 |  |
    |      3 | 20160111 |  16 |   3 |        2 |  |
    |      3 | 20160112 |  16 |   3 |        3 |  |
    |      3 | 20160113 |  16 |   3 |        3 |  |
    |      3 | 20160114 |  16 |   3 |        3 |  |
    |      3 | 20160115 |  16 |   3 |        3 |  |
    |      3 | 20160116 |  16 |   3 |        3 | /
    

    【讨论】:

      【解决方案4】:

      假设您有如下定义的Car 类,您可以使用Linq 执行此操作。

      public class Car
      {
          public int car_id;
          public DateTime date;       
          // additional info
      }
      

      我们需要应用两次分组,一次在car_id 上,然后在均匀拆分到多个组时再次应用。我更喜欢为此使用Linq

          var grouped = cars.GroupBy(c=>c.car_id)
              .Select(c => 
                      new 
                      {
                          car_grp_id = c.Key,
                          splits = c.Select((s,i)=> 
                          new 
                          {
                              grp_id = i/(c.Count() <= 5 ? 5 :(c.Count() %2 ==0)? c.Count() /2 : (c.Count() /3 +1)),
                              item = s
                          }), 
      
                      })          
              .Select(s=> 
                      new 
                      {                       
                          grouponcars = s.splits.GroupBy(g=>g.grp_id)
                              .Select(x=>
                              new 
                              {
                                  group_id = x.Key,
                                  cars = x.Select(y=>y.item)
                              })
                      })
              .ToList();
      

      输出

      groupid : 0 -       Car_Id : 1 -         Date -1/1/2016 12:00:00 AM
      groupid : 0 -       Car_Id : 1 -         Date -1/3/2016 12:00:00 AM
      groupid : 0 -       Car_Id : 1 -         Date -1/5/2016 12:00:00 AM
      groupid : 0 -       Car_Id : 1 -         Date -1/6/2016 12:00:00 AM
      ----------------------------------------
      groupid : 1 -       Car_Id : 1 -         Date -1/7/2016 12:00:00 AM
      groupid : 1 -       Car_Id : 1 -         Date -1/8/2016 12:00:00 AM
      groupid : 1 -       Car_Id : 1 -         Date -1/9/2016 12:00:00 AM
      groupid : 1 -       Car_Id : 1 -         Date -1/10/2016 12:00:00 AM
      ----------------------------------------
      groupid : 0 -       Car_Id : 2 -         Date -1/2/2016 12:00:00 AM
      groupid : 0 -       Car_Id : 2 -         Date -1/4/2016 12:00:00 AM
      

      查看Demo

      【讨论】:

      • 我喜欢你的部分回答并给我一些想法。但正如我在这个问题上解释的那样,组的数量取决于 car.count()。最大组数为 3。对于您的样本,每个组的大小最大为 4。随着 car.count() 变大,组会增加dotnetfiddle.net/9VugsV
      • 这个逻辑可以推导出来,让我更新以适应需求。
      • 玩过一些数学 :-),检查更新的代码和演示链接。
      • 我不想听起来不完美,因为你的代码再次给了我一些想法。但是如果count &gt; 10,这段代码只会创建两个组,我想我可以让它变得简单,并制作一个CASE cars.count和三个选择
      • 没错,只需使用一些简单的开关盒并决定组大小。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-14
      • 2017-10-12
      • 2021-07-30
      • 2012-09-06
      • 2021-03-17
      • 1970-01-01
      相关资源
      最近更新 更多