查找项目列表中最频繁出现的组合

时间:2018-10-30 16:14:52

标签: c# combinations

在我的asp.net c#应用程序中,我具有以下列出的项目组合。我想列出最常出现的组合。

  1. Item1
  2. Item1,Item2
  3. Item3
  4. Item1,Item3,Item2
  5. 第3项,第1项
  6. 第2项,第1项

根据上面的示例,我应该获得以下输出。

最经常出现的组合是

  1. 第1项和第2项-出现的次数为3(#2,#4和#6)
  2. 第1项和第3项-出现的次数为2(#4和#5)

我的结构如下。

public class MyList
{
    public List<MyItem> MyItems { get; set; }
}

public class MyItem
{
    public string ItemName { get; set; }
}

4 个答案:

答案 0 :(得分:1)

我想尽一切办法使用哈希将所有可能的组合映射起来,其中abba相同(或者您可以按字母顺序对项目进行排序,然后对它们进行哈希处理),然后只计算哈希的出现...

答案 1 :(得分:0)

您可以从列表中创建一个加权图,其中两个节点之间的权重代表发生频率。 This StackExchange post提供了一些信息,您还可以在this previous SO post here上了解邻接矩阵。

根据我的看法,明智的做法是使用 HashSet<Tuple<Item1, Item2>>代表一个连接,并将其值存储在字典中。

对于多个项目,问题类似于在图形的路径遍历算法中找出最经过的路径。

尽管对于非常大的数据集,我建议通过C#动态地通过SQL语句和分析查询使用SSAS和SSIS服务,以创建一个市场篮子分析,该分析应该为您生成所需的统计数据。

答案 2 :(得分:0)

这是一种快速而肮脏的方法,可以帮助您入门。您可能应该使用哈希表来提高性能,但我认为字典更易于可视化。

提琴:https://dotnetfiddle.net/yofkLf

public static void Main()
{
    List<MyItem[]> MyItems = new List<MyItem[]>()
    {
        new MyItem[] { new MyItem("Item1") },
        new MyItem[] { new MyItem("Item1"), new MyItem("Item2") },
        new MyItem[] { new MyItem("Item3") },
        new MyItem[] { new MyItem("Item1"), new MyItem("Item3"), new MyItem("Item2") },
        new MyItem[] { new MyItem("Item3"), new MyItem("Item1") },
        new MyItem[] { new MyItem("Item2"), new MyItem("Item1") }
    };
    Dictionary<Tuple<string, string>, int> results = new Dictionary<Tuple<string, string>, int>();      
    foreach (MyItem[] arr in MyItems)
    {
        // Iterate through the items in the array. Then, iterate through the items after that item in the array to get all combinations.
        for (int i = 0; i < arr.Length; i++)
        {
            string s1 = arr[i].ItemName;
            for (int j = i + 1; j < arr.Length; j++)
            {
                string s2 = arr[j].ItemName;
                // Order the Tuple so that (Item1, Item2) is the same as (Item2, Item1).                    
                Tuple<string, string> t = new Tuple<string, string>(s1, s2);
                if (string.Compare(s1, s2) > 0)
                {
                    t = new Tuple<string, string>(s2, s1);  
                }
                if (results.ContainsKey(t))
                {
                    results[t]++;
                }
                else
                {
                    results[t] = 1;
                }
            }
        }
    }
    // And here are your results.
    // You can always use Linq to sort the dictionary by values.
    foreach (var v in results)
    {
        Console.WriteLine(v.Key.ToString() + " = " + v.Value.ToString());
        // Outputs:
        // (Item1, Item2) = 3
        // (Item1, Item3) = 2
        // (Item2, Item3) = 1
    }
}

...

public class MyItem
{
    public string ItemName { get; set; }
    public MyItem(string ItemName)
    {
        this.ItemName = ItemName;   
    }
}

当然,如果MyItems中没有该字符串属性,则情况会有所不同。

答案 3 :(得分:0)

这是一个粗略的O(N​​ ^ 2)方法:

  • 遍历外部集合(List<List<Item>>
  • 提出一种定义当前行的方法,将其命名为rowId
  • 现在迭代已知的行ID(内部迭代)。
  • 计算其中一个是另一个的完整子集;当前行包含在前一组中,或者前一组包含在当前行中。 (这是您想要的解决方案。)这是通过增加先前看到的行(如果它们是当前行的子集)的计数,或跟踪当前行是先前看到的组合的子集的次数并将其设置为在每次内部迭代结束时。

一些假设:

  • 您不在乎所有可能的商品组合,仅关注已被看到的组合。
  • 物品具有唯一标识符

就像我上面说的那样,这是一种O(N ^ 2)方法,因此性能可能是一个问题。对于子集成员身份也有两项检查,这可能是性能问题。我也只是将ID作为字符串连接和拆分,您可能可以通过设置另一个跟踪ID的字典来获得更好的解决方案。 Dictionary.TryGetValue还有一些改进的空间。提取所需的项目集留给读者作为练习,但这应该是简单的OrderBy(..).Where(...)操作。但这应该可以帮助您入门。

public class MyItem
{
    public string ItemName { get; set; }
}

class Program
{
    public static void GetComboCount()
    {
        var itemsCollection = new List<List<MyItem>>() {
            new List<MyItem>() { new MyItem() { ItemName = "Item1" } },
            new List<MyItem>() { new MyItem() { ItemName = "Item1" }, new MyItem() { ItemName = "Item2" } },
            new List<MyItem>() { new MyItem() { ItemName = "Item3" } },
            new List<MyItem>() { new MyItem() { ItemName = "Item1" }, new MyItem() { ItemName = "Item3" }, new MyItem() { ItemName = "Item2" } },
            new List<MyItem>() { new MyItem() { ItemName = "Item3" }, new MyItem() { ItemName = "Item1" } },
            new List<MyItem>() { new MyItem() { ItemName = "Item2" }, new MyItem() { ItemName = "Item1" } }
        };

        var comboCount = new Dictionary<string, int>();

        foreach (var row in itemsCollection)
        {
            var ids = row.Select(x => x.ItemName).OrderBy(x => x);
            var rowId = String.Join(",", ids);
            var rowIdCount = ids.Count();

            var seen = false;

            var comboCountList = comboCount.ToList();
            int currentRowCount = 1;

            foreach (var kvp in comboCountList)
            {
                var key = kvp.Key;
                if (key == rowId)
                {
                    seen = true;
                    currentRowCount++;
                    continue;
                }

                var keySplit = key.Split(',');
                var keyIdCount = keySplit.Length;

                if (ids.Where(x => keySplit.Contains(x)).Count() == keyIdCount)
                {
                    comboCount[kvp.Key] = kvp.Value + 1;
                }
                else if (keySplit.Where(x => ids.Contains(x)).Count() == rowIdCount)
                {
                    currentRowCount++;
                }
            }

            if (!seen)
            {
                comboCount.Add(rowId, currentRowCount);
            }
            else
            {
                comboCount[rowId] = currentRowCount;
            }
        }

        foreach (var kvp in comboCount)
        {
            Console.WriteLine(String.Format("{0}: {1}", kvp.Key, kvp.Value));
        }
    }

    static void Main(string[] args)
    {
        GetComboCount();
    }
}

控制台输出:

Item1: 5
Item1,Item2: 3
Item3: 3
Item1,Item2,Item3: 1
Item1,Item3: 2