根据权重和概率随机选择表格中的行

时间:2016-01-14 19:48:58

标签: mysql

我正在使用MySQL。我有一张看起来像这样的表:

id: primary key name: varchar weight: int (this can be either 1,2 or 3)

我想要做的是随机选择一行,直到我从一个类似下面有500行的表中获得400个选定行的列表,但考虑到了权重。 For example, if I have 3 rows: id, name, weight 1, "some content", 2 2, "other content", 1 3, "something", 3

创建列表时,权重为2的行在列表中出现30%的次数,权重为1的行在列表中出现20%的次数,权重为3的行出现50%的次数在列表中。

允许重复,但不允许重复。

有办法吗?

如果您不了解某些内容,请随时提出。

提前致谢。

3 个答案:

答案 0 :(得分:1)

我仍然没有解决重复部分。但这会给你一个开始

<强> SQL Fiddle Demo

  • 大多数内部选择​​指定一个随机数
  • 中间选择使用变量按行权分配每个行分区的row_number
  • 最后选择过滤器以匹配比率。在这种情况下,生成一个大小为50的列表。
  • 每个类别的原始数据均匀分布~30。因此,大小60将成为实现50% Weight = 3
  • 的限制

SELECT `ID`,`Name`,`Weight`, RowNumber
FROM ( 
        SELECT *,
                @row_num := IF(@prev_value = `Weight`,
                               @row_num + 1,
                               IF(@prev_value:=`Weight`,
                                  1,
                                  1)
                               ) AS RowNumber
        FROM (                                                      
              SELECT `ID`,`Name`,`Weight`,  rand() as rng
              FROM      `myTable`              
              ORDER BY `Weight`, rng
             ) X  
        CROSS JOIN  (SELECT @row_num := 1, @prev_value := 0) y
     ) T     
WHERE ( Weight = 3 and RowNumber <= 50 * 0.5 )
   OR ( Weight = 2 and RowNumber <= 50 * 0.3 )
   OR ( Weight = 1 and RowNumber <= 50 * 0.2 )
ORDER BY Weight, RowNumber

答案 1 :(得分:0)

我建议您制作一个临时表,其中1个记录的所有记录重复2次,2个记录的记录重复3次,所有3个记录重复5次。然后在所有记录中的临时表中进行随机选择。如果总数足够大(例如400),这在统计上应该最终得到非常接近目标的分布。

答案 2 :(得分:0)

在我的另一个答案中,我解决了如何为每个重量分配ID。在这里,我将向您展示如何创建一个列表来处理重复项。

我使用表格来显示整个过程,您也可以选择演示来验证每个结果。但是有些工作可以在一个查询中合并,但不容易阅读。

<强> SQL FIDDLE DEMO

首先,我们需要创建一个表来存储哪一行将参与您的列表

CREATE TABLE `incr` (
   `weight` mediumint,
   `row` mediumint
);

使用存储过程我们填写表格。

CREATE PROCEDURE dowhile(IN Size INT)
BEGIN
  DECLARE v1 INT DEFAULT Size * 0.5;

  WHILE v1 >= 0 DO
    IF v1 <= (Size - 1) * 0.5 THEN       
        INSERT incr VALUES (3, v1);
    END IF;
    IF v1 <= (Size - 1) * 0.3 THEN
        INSERT incr VALUES (2, v1);
    END IF;
    IF v1 <= (Size - 1) * 0.2 THEN
        INSERT incr VALUES (1, v1);
    END IF;

    SET v1 = v1 - 1;
  END WHILE;
END//

CALL dowhile(300); -- Indicate List Size

现在创建一个新表,以了解我们样本中每个权重类别的大小。

CREATE TABLE maxWeight 
      SELECT `Weight`, COUNT(*) as mw 
      FROM `myTable`
      GROUP BY `Weight`;

使用%运算符,我们可以重复这些行来填充所需的大小

CREATE TABLE rowList
      SELECT i.weight,
             CASE WHEN i.row >= w.mw then i.row % w.mw
                  ELSE i.row
             END newrow
      FROM incr i
      JOIN maxWeight w
        ON i.weight = w.weight;

正如您在此处所见,即使我的列表仅为100,最终结果为300

SELECT weight, count(*)
FROM rowList
GROUP BY weight;

| weight | count(*) |
|--------|----------|
|      1 |       60 |
|      2 |       90 |
|      3 |      150 |

现在将两个表连接在一起

CREATE TABLE finalResult
      SELECT `ID`,`Name`, T.`Weight`, RowNumber
      FROM ( 
              SELECT *,
                      @row_num := IF(@prev_value = `Weight`,
                                     @row_num + 1,
                                     IF(@prev_value:=`Weight`,
                                        0,
                                        0)
                                     ) AS RowNumber
              FROM (                                                      
                    SELECT `ID`,`Name`,`Weight`,  rand() as rng
                    FROM      `myTable`              
                    ORDER BY `Weight`, rng
                   ) X  
              CROSS JOIN  (SELECT @row_num := 0, @prev_value := 0) y
           ) T   
      JOIN  rowList
        ON T.`RowNumber` = rowList.`newrow`
       AND T.`Weight` = rowList.`weight`; 

最终结果是使用重复名称

的欲望比率
SELECT `Weight`, COUNT(*) total, COUNT(DISTINCT `Name`) d_name
FROM finalResult
GROUP BY `Weight`;    


| Weight | total | d_name |
|--------|-------|--------|
|      1 |    60 |     36 |
|      2 |    90 |     32 |
|      3 |   150 |     30 |

即使原始表格有37 weight = 1,我用来生成随机值的工具也会复制一个名称,因此d_name = 36