2023 深セン杯の数学モデリングのアイデア - ケース: FPTree - 頻度パターン ツリー アルゴリズム

## コンテストの質問のアイデア

(コンテストの質問が出たらすぐに CSDN で共有します)

https://blog.csdn.net/dc_sinor?type=blog

アルゴリズムの概要

FP-Tree アルゴリズムの正式名は、FrequentPattern Tree アルゴリズムです。これは、頻繁に使用されるパターン ツリー アルゴリズムです。Apriori アルゴリズムと同様に、頻繁に使用されるアイテムセットのマイニングにも使用されますが、違いは、FP-Tree アルゴリズムが最適化であることです。 Apriori アルゴリズムのプロセスで、Apriori アルゴリズムを解決します。このアルゴリズムはその過程で多数の候補セットを生成しますが、FP-Tree アルゴリズムは候補セットを生成せずに頻繁に発生するパターンを見つけます。ただし、頻繁に使用されるパターンがマイニングされた後、相関ルールを生成する手順は Apriori と同じです。

頻繁に使用されるアイテムセットをマイニングするための一般的なアルゴリズムは 2 つあり、1 つは Apriori アルゴリズムで、もう 1 つは FP-growth です。Apriori は、候補セットの構築と候補セットのスクリーニングを継続的に行うことによって、頻繁に使用されるアイテムセットをマイニングします。元のデータを複数回スキャンする必要があります。元のデータが大きい場合、ディスク I/O 回数が多くなり、効率が比較的低くなります。FPGrowth は Apriori の「プローブ」戦略とは異なり、アルゴリズムは元のデータを 2 回スキャンし、より効率的な FP ツリー データ構造を通じて元のデータを圧縮するだけで済みます。

FP は Frequent Pattern の略で、アルゴリズムは主に FP ツリーの構築と頻出項目セットのマイニングの 2 つのステップに分かれます。

FPツリー表現

FP ツリーは、トランザクションを 1 つずつ読み込み、トランザクションを FP ツリー内のパスにマッピングすることによって構築されます。異なるトランザクションには複数の同一のアイテムがある可能性があるため、それらのパスは部分的に重複する可能性があります。パスが互いに重なり合うほど、FP ツリー構造を使用することによって得られる圧縮効果は高くなります。FP ツリーがメモリに格納できるほど小さい場合は、繰り返しスキャンすることなく、頻繁に使用されるアイテムセットをメモリ内の構造から直接抽出できます。ハードドライブ上のデータに保存されます。

FP ツリーは次の図に示されています:
  ここに画像の説明を挿入
通常、データのトランザクションはいくつかの共通項目を共有することが多いため、FP ツリーのサイズは非圧縮データのサイズよりも小さくなります。最良の場合、すべてのトランザクションには同じ項目セットがあります。 、FP ツリーにはノード パスが 1 つだけ含まれます。各トランザクションに固有のアイテムセットがある場合、トランザクションには共通のアイテムが含まれていないため、最悪のケースが発生し、FP ツリーのサイズは実質的に元のサイズと同じになります。データ。

FP ツリーのルート ノードは φ で表され、残りのノードにはデータ項目とこのパス上のデータ項目のサポートが含まれます。各パスはトレーニング データ内の最小サポートを満たすデータ項目セットです。FPツリーにはすべての項目も含まれます。同じ項目がリンク リストに接続されます。これは、上の図の青い線で表されます。

ツリー内の同じ項目にすばやくアクセスするには、同じ項目を持つノードを接続するポインタ リスト (headTable) を維持することも必要です。各リスト要素には、データ項目、項目のグローバル最小サポート、およびヘッダーの FP ツリー ポインター内の項目を指すリンク リスト。
  ここに画像の説明を挿入

FPツリーの構築

これで、次のデータが得られました。

ここに画像の説明を挿入

FP 成長アルゴリズムは、FP ツリーを構築するために元のトレーニング セットを 2 回スキャンする必要があります。

最初のスキャンでは、最小サポートを満たさないすべての項目がフィルターで除外されます。最小サポートを満たす項目については、グローバル最小サポートに従って並べ替えられます。これに基づいて、処理の便宜上、次のようにすることもできます。アイテムのキーワードに従って再ソートされます。
ここに画像の説明を挿入

2 番目のスキャンでは FP ツリーが構築されます。

フィルタリングされたデータがスキャンに参加します。データ項目が初めて見つかった場合は、ノードを作成し、headTable 内のノードへのポインタを追加します。それ以外の場合は、パスに従って項目に対応するノードを見つけて、ノードを変更します。情報。具体的なプロセスは次のとおりです。

ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入
 上記を見ると、headTable は FPTree と一緒に作成されるのではなく、最初のスキャン時に作成されることがわかります。FPTree を作成するときは、対応するノードにポインタを指すだけで済みます。トランザクション 004 から開始して、異なるパス上の同じ項目がリンク リストに接続されるように、ノード間の接続を作成する必要があります。

実装コード

def loadSimpDat():
    simpDat = [['r', 'z', 'h', 'j', 'p'],
               ['z', 'y', 'x', 'w', 'v', 'u', 't', 's'],
               ['z'],
               ['r', 'x', 'n', 'o', 's'],
               ['y', 'r', 'x', 'z', 'q', 't', 'p'],
               ['y', 'z', 'x', 'e', 'q', 's', 't', 'm']]
    return simpDat

def createInitSet(dataSet):
    retDict = {
    
    }
    for trans in dataSet:
        fset = frozenset(trans)
        retDict.setdefault(fset, 0)
        retDict[fset] += 1
    return retDict

class treeNode:
    def __init__(self, nameValue, numOccur, parentNode):
        self.name = nameValue
        self.count = numOccur
        self.nodeLink = None
        self.parent = parentNode
        self.children = {
    
    }

    def inc(self, numOccur):
        self.count += numOccur

    def disp(self, ind=1):
        print('   ' * ind, self.name, ' ', self.count)
        for child in self.children.values():
            child.disp(ind + 1)


def createTree(dataSet, minSup=1):
    headerTable = {
    
    }
    #此一次遍历数据集, 记录每个数据项的支持度
    for trans in dataSet:
        for item in trans:
            headerTable[item] = headerTable.get(item, 0) + 1

    #根据最小支持度过滤
    lessThanMinsup = list(filter(lambda k:headerTable[k] < minSup, headerTable.keys()))
    for k in lessThanMinsup: del(headerTable[k])

    freqItemSet = set(headerTable.keys())
    #如果所有数据都不满足最小支持度,返回None, None
    if len(freqItemSet) == 0:
        return None, None

    for k in headerTable:
        headerTable[k] = [headerTable[k], None]

    retTree = treeNode('φ', 1, None)
    #第二次遍历数据集,构建fp-tree
    for tranSet, count in dataSet.items():
        #根据最小支持度处理一条训练样本,key:样本中的一个样例,value:该样例的的全局支持度
        localD = {
    
    }
        for item in tranSet:
            if item in freqItemSet:
                localD[item] = headerTable[item][0]

        if len(localD) > 0:
            #根据全局频繁项对每个事务中的数据进行排序,等价于 order by p[1] desc, p[0] desc
            orderedItems = [v[0] for v in sorted(localD.items(), key=lambda p: (p[1],p[0]), reverse=True)]
            updateTree(orderedItems, retTree, headerTable, count)
    return retTree, headerTable


def updateTree(items, inTree, headerTable, count):
    if items[0] in inTree.children:  # check if orderedItems[0] in retTree.children
        inTree.children[items[0]].inc(count)  # incrament count
    else:  # add items[0] to inTree.children
        inTree.children[items[0]] = treeNode(items[0], count, inTree)
        if headerTable[items[0]][1] == None:  # update header table
            headerTable[items[0]][1] = inTree.children[items[0]]
        else:
            updateHeader(headerTable[items[0]][1], inTree.children[items[0]])

    if len(items) > 1:  # call updateTree() with remaining ordered items
        updateTree(items[1:], inTree.children[items[0]], headerTable, count)


def updateHeader(nodeToTest, targetNode):  # this version does not use recursion
    while (nodeToTest.nodeLink != None):  # Do not use recursion to traverse a linked list!
        nodeToTest = nodeToTest.nodeLink
    nodeToTest.nodeLink = targetNode

simpDat = loadSimpDat()
dictDat = createInitSet(simpDat)
myFPTree,myheader = createTree(dictDat, 3)
myFPTree.disp()

上記のコードは、最初のスキャン後に各トレーニング データのフィルタリングされた項目を並べ替えず、2 番目のスキャンに並べ替えを行うため、コードの複雑さを簡素化できます。

コンソール情報:

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/dc_sinor/article/details/131868438