# Data Mining Lab 1 ## Author 313581037 許哲瑜 ## How to run my code in Step Ⅱ and Ⅲ. 1. run my code in Step Ⅱ ```sh python step2.py -f datasetA.data -s 0.3 -p Step2_task1 ``` * `-f` : dataset path。 預設值為 `datasetA.data`。 * `-s` : minimum support (%)。 預設值為 `0.1`。 * `-p` : output file dir path。 預設值為 ```./Step2_task1```。 2. 執行完上述指令後,會在 Step2_task1 資料夾中分別看到 `Step2_task1_datasetA_0.3_result1.txt` 、 `Step2_task1_datasetA_0.3_result2.txt` 、`Step2_task2_datasetA_0.3_result1.txt` 三個執行結果檔案,並在與 `step2.py` 同資料夾下的 `Step2_datasetA_0.3_log.txt` 檔案,是記錄**Task 1**與**Task 2**的執行時間與比例。 3. run my code in Step Ⅲ. ```sh python step3.py -f datasetA.data -s 0.3 -p Step3_task1 ``` * `-f` : dataset path。 預設值為 `datasetA.data`。 * `-s` : minimum support (%)。 預設值為 `0.1`。 * `-p` : output file dir path。 預設值為 ```./Step3_task1```。 4. 執行完上述指令後,會在 Step3_task1 資料夾中分別看到 `Step3_task1_datasetA_0.3_result1.txt` 執行結果檔案,並在與 `step2.py` 同資料夾下的 `Step3_datasetA_0.3_log.txt` 檔案,是記錄**FP-Growth Computation Time**的資訊。 5. 判斷正確性與分析執行時間 ```shell python correctness_analysis.py -r Step2_task1 -s Step3_task1 ``` * `-r` : Step2 dirname。 預設值為 `Step2_task1`。 * `-s` : Step3 dirname。 預設值為 `Step3_task1`。 * `-l` : log dirname。 預設值為 ```log```。 6. 上述指令會將兩個資料夾中的 xxx_task1_datasetA_0.9_result1.txt檔案進行 Frequent Itemset的比較看是否正確,正確結果如下方圖1。5 之後會將log資料夾中的檔案進行效能分析(可以將第2步與第4步的xxx_log.txt檔案移到log資料夾中),並在與`correctness_analysis.py`檔案同資料夾下產生`Analysis_log.txt`,裡面記錄 Step2 與 Step3 分別運行的時間與比率,如圖2。 執行結果圖如下: 圖1:<img src=https://hackmd.io/_uploads/BJr8bn0gye.png/> 圖2: <img src=https://hackmd.io/_uploads/ByPFZnAgJl.png/> ## Step II. - Mining the datasets generated in Step I using Apriori algorithm: ### Sept II. The modifications you made for Task 1 and Task 2 : #### 1. 修改讀取資料的函式 `dataFromFile` : > 由於 datasetA.data 的資料中,每一列的前三筆分別代表、第幾列、第幾列、ITEMS數量,因此在讀取資料時要記得過濾。 ```py def dataFromFile(fname): """Function which reads from the file and yields a generator""" with open(fname, "r") as file_iter: for line in file_iter.readlines(): line = line.strip().split(" ")[3:] # 以空格切分資料,並過濾前三筆 SID TID NITEMS 不需要的欄位資訊 record = frozenset(line) # frozenset可以將重複的資料刪除,且回傳一個無法再更動的集合 yield record # yield是節省記憶體空間的用法 ``` >[!Tip] > * file_iter.readlines(): 可以將檔案內容根據\n讀取成一行一行的串列,可以用for迴圈取出每一行內容。 > * line.strip().split(" ")[3:] : 將每一行的內容去除換行符號後以空格切分,並過濾出前三筆資料。 #### 2. 新增計算運行時間的程式碼: > 在Task1與Task2中都需要計算運行的時間:Count and show the whole computation time for this task (task independent),因此在 ```if __name__ == "__main__"``` 中做了以下修改 ```py ### Task 1 Mining all Frequent Itemset ### start_task1 = time.time() largeSet, candidate_counts, items = runApriori(inFile, minSupport) end_task1 = time.time() task1_time = end_task1 - start_task1 # Count the computation time for this task (task independent). print(f"Task 1 Computation Time: {task1_time:.4f} seconds") ### Task 2 Mining all Frequent Closed Itemset ### start_task2 = time.time() # The aim for this task is to mine all Frequent Closed Itemset with computation as fast as possible. closed_itemsets = find_closed_itemsets(items) end_task2 = time.time() task2_time = task1_time + end_task2 - start_task2 print(f"Task 2 Computation Time: {task2_time:.4f} seconds") # how the ratio of computation time compared to that of Task 1: Task2 / Task1 * 100% time_ratio = (task2_time / task1_time) * 100 print(f"Task 2 to Task 1 Time Ratio: {time_ratio:.2f}%") # 將結果儲存起來 fname = options.input.split("/")[-1].split('.')[0] with open('Step2_'+fname+'_'+str(options.minS)+'_log.txt', 'w') as file: # Print out the total number of frequent closed itemset. file.write(f"Task 1 Computation Time: {task1_time:.4f} seconds\n") file.write(f"Task 2 Computation Time: {task2_time:.4f} seconds\n") file.write(f"Task 2 to Task 1 Time Ratio: {time_ratio:.2f}%") ``` >[!Tip] > * time.time():可以取得當下的時間,再進行相減後便可以得到運行時間。 > * task2_time = task1_time + end_task2 - start_task2:Task2 是使用 Task1 的結果在進行運算取得**Frequent Closed Itemset** ,因此要加上Task1的運行時間 #### 3. 修改一些 Apriori algorithm 相關的函式以符合作業需求 : * `returnItemsWithMinSupport` : 為計算輸入的項目集分別的 support 支持度,並篩選出那些滿足 minSupport 最小支持度要求的項目集 -> Frequent Itemsets ```py # 計算項目集的 support 支持度,並篩選出那些滿足 minSupport 最小支持度要求的項目集 -> Frequent Itemsets def returnItemsWithMinSupport(itemSet, transactionList, minSupport, freqSet): """calculates the support for items in the itemSet and returns a subset of the itemSet each of whose elements satisfies the minimum support""" _itemSet = set() localSet = defaultdict(int) minSupport = round(float(minSupport) / 100,3) # 輸入為 %,因此要除 100 for item in itemSet: for transaction in transactionList: if item.issubset(transaction): # 如果此 item 出現在這筆交易 transaction 中 freqSet[item] += 1 localSet[item] += 1 for item, count in localSet.items(): support = float(count) / len(transactionList) # 計算每一筆 item 的 support 支持度(出現的頻率) if support >= minSupport: # 如果大於設定的 minSupport (最小支持度)則保留起來,即為 Frequent Itemsets (頻繁項目集) _itemSet.add(item) return _itemSet ``` >[!Caution] 注意!!! > * **minSupport = round(float(minSupport) / 100,3)** : 由於指令輸入的 **minSupport** 是百分比的格式(%),因此要將數值處以100才會正確。 > * 我一開始只有單純寫 minSupport = float(minSupport) / 100,在 datasetA_0.9的數據中會出現 minSupport 為 0.0090000002 的值! > 會導致 support 為 0.009 的 item 全部被刪除,進而出現錯誤的數量,因此要記得使用round進行浮點數的處理! * `joinSet` : 為得到下一次的itemset的組合( 擴展 ),沒有進行修改 ```py # 得到下一次的itemset的組合( 擴展 ) def joinSet(itemSet, length): """Join a set with itself and returns the n-element itemsets""" return set( [i.union(j) for i in itemSet for j in itemSet if len(i.union(j)) == length] ) ``` * `getItemSetTransactionList` : 從輸入資料中取得每個集合中只有一個值的 itemSet,與所有的交易筆數 transactionList,並回傳 1-itemSets及所有的transactions,沒有進行修改 ```py # 取得每個集合中只有一個值的 itemSet,與所有的交易筆數 transactionList def getItemSetTransactionList(data_iterator): transactionList = list() itemSet = set() # 集合的資料結構,不會出現重複的 item for record in data_iterator: transaction = frozenset(record) transactionList.append(transaction) for item in transaction: itemSet.add(frozenset([item])) # Generate 1-itemSets return itemSet, transactionList ``` * `runApriori` : 根據 Task 1: Mining all Frequent Itemset 中 (b) Result file 2 (statistics file)的需求,需要紀錄 apriori algorithm 中每一次運算的前後候選的itemset數量,因此我做了以下修改: > 1. ```candidate_counts = []``` : 用來記錄作業需求,方便後續寫檔案使用 > 2. ```returnItemsWithMinSupport```:是 apriori algorithm 的核心,即將輸入的候選 itemset 根據 minSupport 來判斷是否符合頻繁項目集 Frequent Itemset,因次要在此函式前後紀錄 itemset 的數量。 > 3. 另外作業有要求:The index of iteration starts from 1,因此我在 進入`while` 迴圈前有做一次紀錄: **`candidate_counts.append((1, num_candidates_before, num_candidates_after))`** ```py def runApriori(data_iter, minSupport): """ run the apriori algorithm. data_iter is a record iterator apriori algorithm 是根據以下兩個性質來運作: 1. 如果一個項目集是符合 minSupport 的 頻繁項目集,則他的所有子集合也是頻繁項目集。 2. 相反來說,如果一個項目集不符合(就不是頻繁項目集)的話,即可以刪除不用再看(透過剪枝可以大幅減少計算) Return toRetItems: - items (tuple, support) """ # 步驟一:生成候選項目集,從每個items只有一個開始。 itemSet, transactionList = getItemSetTransactionList(data_iter) freqSet = defaultdict(int) largeSet = dict() candidate_counts = [] num_candidates_before = len(itemSet) # which satisfy minSupport oneCSet= returnItemsWithMinSupport(itemSet, transactionList, minSupport, freqSet) num_candidates_after = len(oneCSet) candidate_counts.append((1, num_candidates_before, num_candidates_after)) currentLSet = oneCSet # 步驟二:逐步擴展候選項目集,直到計算完所有的可能,產生符合 minSupport 的 frequent itemsets k = 2 while currentLSet != set([]): largeSet[k - 1] = currentLSet # Generate next candidates by joining the current frequent itemsets currentLSet = joinSet(currentLSet, k) num_candidates_before = len(currentLSet) currentCSet= returnItemsWithMinSupport( currentLSet, transactionList, minSupport, freqSet ) currentLSet = currentCSet num_candidates_after = len(currentLSet) # 儲存每一次修剪前後的itemset數量 candidate_counts.append((k, num_candidates_before, num_candidates_after)) #print("candidate_counts:",candidate_counts) k = k + 1 def getSupport(item): """local function which Returns the support of an item""" return float(freqSet[item]) / len(transactionList) # 處理回傳結果,toRetItems 中包含每一個 frequent itemsets 與 該項目集的支持度 Support toRetItems = [] for key, value in largeSet.items(): toRetItems.extend([(tuple(item), getSupport(item)) for item in value]) return largeSet, candidate_counts, toRetItems ``` >[!Tip] 函式輸出: > * largeSet : 是所有 frequence Itemset,可以用len(largeSet),來取得該資料集符合 minSupport 的筆數。 > * candidate_counts : 即為 Result file 2 (statistics list)要求所需要的內容。 > * toRetItems : 是根據 Result file 1 (itemset list) 要求所需要的內容,裡面會包含所有的frequence Itemset以及對應的Support數值 * `find_closed_itemsets` : 是根據 Task 2: Mining all Frequent Closed Itemset 中的需求,所額外建立的函式。 > 1. 將 Task 1 所得到的`frequent Itemset`作為輸入。 > 2. 裡用雙層迴圈遍歷所有Itemset,並判斷當前`item` 是否為其他`other_item`的**超集的子集**,並且該`item`的`support`與`other_item`的`other_support`相同,則當前項集**不是閉合的**。 > 3. 最後判斷是否任然為閉合的,是則將其添加到閉合項集中。 > 4. 作業要求:Please sort those frequent closed itemsets according to support (from large to small).所以要將**closed_itemsets**根據support進行排序,設定reverse=True可以由大到小排序。 ```py # Task 2 - Frequent Closed Itemset # mine all Frequent Closed Itemset with computation def find_closed_itemsets(freq_items): closed_itemsets = [] freq_dict = {frozenset(item): support for item, support in freq_items} for item, support in freq_dict.items(): is_closed = True for other_item, other_support in freq_dict.items(): # 該 item 是其他 freq_items的子集合,或有相同的支持度,則此 item 就不是 Frequent Closed Item if item != other_item and item.issubset(other_item) and support == other_support: is_closed = False break if is_closed: closed_itemsets.append((item, support)) # Please sort those frequent closed itemsets according to support (from large to small). closed_itemsets.sort(key=lambda x: x[1], reverse=True) return closed_itemsets ``` >[!Tip] 函式輸出: > * closed_itemsets : 包含了所有的 Frequent Closed Itemset,並且根據support值由大到小排序。 #### 4. 將上述的結果寫成檔案: > 為了確保檔案名稱的一致性,我將數入參數進行以下處理,以取得資料集名稱與最小支持度 ```py fname = options.input.split('.')[0] # 取得輸入的資料及名稱 minSupport = options.minS ``` * `weitefile_itemset` : 將```runApriori```函式的結果 toRetItems 與 fname、minSupport輸入,注意要求的suooort要以百分比表示。 ```py # Task 1 / Result 1 / # Print out all frequent itemsets with support (take percentage % and round to the first decimal place) def weitefile_itemset(items,fname,minSupport): with open(options.path + '/Step2_task1_'+fname+'_'+str(minSupport)+'_result1.txt','w') as outfile: # sort those frequent itemsets according to support (from large to small). for item, support in sorted(items, key=lambda x: x[1], reverse=True): temp = ','.join(list(item)) # take percentage % and round to the first decimal place support = round(support*100,1) # File format (each frequent itemset): [support]\t[item_set]\n outfile.write("{:.1f}".format(support)+'\t{'+temp+'}\n') ``` >[!Note] 使用方式: > ``` weitefile_itemset(items,fname,minSupport) ``` * `save_statistics` : 將```runApriori```函式的結果 largeSet、candidate_counts 與 fname、minSupport輸入。 ```py # Task 1 / Result 2 / # the number of candidates generated before and after pruning def save_statistics(largeSet, candidate_counts,fname,minSupport): total_freq_itemsets = sum(len(v) for v in largeSet.values()) with open(options.path + '/Step2_task1_'+fname+'_'+str(minSupport)+'_result2.txt', 'w') as file: # Print out the total number of frequent itemsets. file.write(f"{total_freq_itemsets}\n") # Print out the number of candidates generated before and after pruning, respectively, in each iteration of Apriori. for iteration, num_candidates_before, num_candidates_after in candidate_counts: # [index_of_iteration]\t[the number of candidates (before pruning)]\t[the number of candidates (after pruning)]\n file.write(f"{iteration}\t{num_candidates_before}\t{num_candidates_after}\n") ``` >[!Note] 使用方式: > ``` save_statistics(largeSet, candidate_counts,fname,minSupport) ``` * `save_closed_itemsets` : 將```find_closed_itemsets```函式的結果 closed_itemsets 與 fname、minSupport輸入,,注意要求的suooort要以百分比表示。 ```py # Task 2 / Mining all Frequent Closed Itemset def save_closed_itemsets(closed_itemsets,fname,minSupport): fname = options.input.split('.')[0] with open(options.path + '/Step2_task2_'+fname+'_'+str(minSupport)+'_result1.txt', 'w') as file: # [total number of frequent closed itemsets]\n file.write(f"{len(closed_itemsets)}\n") # [support]\t[item_set]\n for item, support in closed_itemsets: support_percentage = round(support * 100,1) #take percentage % and round to the first decimal place item_list = '{'+','.join(item)+'}' file.write(f"{support_percentage}\t{item_list}\n") ``` >[!Note] 使用方式: > ``` save_closed_itemsets(closed_itemsets,fname,minSupport)``` ### Sept II. Computation time and Anaylsis : 以下針對不同的資料集與不同的最小支持度來做一些分析,其中冒號後方的數字為Frequent Itemset的總數量。 - In datasetA with minimum support 0.3% : 7776 <img src=https://hackmd.io/_uploads/HkWreA0xke.png/> - In datasetA with minimum support 0.6% : 909 <img src=https://hackmd.io/_uploads/BJXwlARlkg.png/> - In datasetA with minimum support 0.9% : 414 <img src=https://hackmd.io/_uploads/rybYeARgkg.png/> - In datasetB with minimum support 0.2% : 5820 <img src=https://hackmd.io/_uploads/Sk3se0Aeke.png/> - In datasetB with minimum support 0.4% : 1445 <img src=https://hackmd.io/_uploads/B1bTxA0xJg.png/> - In datasetB with minimum support 0.6% : 667 <img src=https://hackmd.io/_uploads/H1cClARg1g.png/> - In datasetC with minimum support 0.5% : 991 <img src=https://hackmd.io/_uploads/Sk31b00xJx.png/> - In datasetC with minimum support 1.0% : 356 <img src=https://hackmd.io/_uploads/B16gWARgke.png/> - In datasetC with minimum support 1.5% : 247 <img src=https://hackmd.io/_uploads/ByC-ZRRgJl.png/> 以上的時間計算皆不包含寫入檔案所花費得時間。 ### Sept II. observations/discoveries : 1. 根據上述的實驗結果顯示,Task 2 利用 Task 1的結果再進行額外的運算,花費的時間會根據Frequent Itemset的總數量來增加,不過所額外花費的時間非常少,兩者運行時間的比例都接近100%左右。 2. 在同一份資料集中,可以發現 minimum support 的數值設定越小,符合的Itemset越多,在 apriori演算法下每次迭代得量也越多,因次花飛的時間也大幅增加。 3. 隨著資料集的筆數增加(datasetA->datasetB->datasetC),所需的時間也會大幅增加,但也是要根據資料集的特性、分佈而定,並不是線性成長的關係。 4. 另外如上方紅色注意區塊顯示,minimum support數值計算中浮點數位元的問題,造成結果根本依據不是我們所設定的minimum support。這是我在做完sept3後回去對照時才發現!前前後後花費了不少時間成本重新計算,下次要更加小心才行。 ## Step III. - Mining the datasets generated in Step I using Apriori algorithm: 我採用了**FP-Growth** 演算法來mining 頻繁項目集 Frequent Itemset。 FP-Growth 是一種非候選集生成(Non-Candidate-based)的演算法,通過構建 **頻繁模式樹(FP-Tree)** 來壓縮交易數據並進行模式挖掘。 我根據下方的網頁來學習並建立 FP-Growth algorithm: > https://github.com/lxtGH/MachineLearning-1/blob/python-2.7/docs/12.%E4%BD%BF%E7%94%A8FP-growth%E7%AE%97%E6%B3%95%E6%9D%A5%E9%AB%98%E6%95%88%E5%8F%91%E7%8E%B0%E9%A2%91%E7%B9%81%E9%A1%B9%E9%9B%86.md > > https://medium.com/@tinahuang_4101/associating-rule-%E9%97%9C%E8%81%AF%E5%BC%8F%E6%B3%95%E5%89%87%E6%87%89%E7%94%A8-apriori-fp-growth-3ab46deeeb77 ### FP-Growth algorithm 的 基本流程: > FP-Growth 主要分成兩大步驟,首先是建立 FP-Tree,再來就是從建立好的FP-Tree計算得到Frequent Itemset,於是我便照著流程寫出對應的函式。 1. 首先計算每個項目的支持度,並保留那些支持度超過最小支持度閾值的項目。 ```py # 預處理交易數據 def preprocess_transactions(data_iterator, min_support): transactionList = list() item_counts = defaultdict(int) for record in data_iterator: transaction = frozenset(record) transactionList.append(transaction) for item in transaction: item_counts[item] += 1 # 計算該 item 出現在交易數據中的次數 # 計算 最小支持度的閾值 ( min_support(%) / 100 * 交易數據比數) min_support = round(min_support / 100 * len(transactionList),3) # 過濾掉低於最小支持度的項目 items = set(item for item, count in item_counts.items() if count >= min_support) # 移除非頻繁項目,並對剩下的項目進行排序 # 將所有交易中的項目按照支持度從高到低進行排序(高頻項目先排序),這樣具有相同前綴的交易可以共享樹中的節點。 processed_transactions = [] for transaction in transactionList: processed_transaction = [item for item in transaction if item in items] processed_transaction.sort(key=lambda item: (-item_counts[item], item), reverse=True) if processed_transaction: processed_transactions.append(processed_transaction) return processed_transactions, len(transactionList) ``` >[!Important] 設定穩定排序 > * processed_transaction.sort(key=lambda item: (-item_counts[item], item), reverse=True) > > 在相同的support數值下,根據項目名稱的數字順序進行次要排序,這樣可以穩定FP-Tree 的建立。 > 如果沒有這樣處理,會導致每次執行的結果都不相同! 2. 構建 FP-Tree:對每筆交易進行排序,將頻繁項目按支持度排序後插入 FP-Tree。 > 這裡根據作業要求對開源的 FP-Growth 基礎程式碼 `fpgrowth.py` 進行了以下修改: * **表頭鏈結改進**:在 `FPTree` 類別中,添加了 `headers` 字典來記錄每個項目的節點鏈接,便於快速建立條件模式基。 * 穩定排序:在多處排序中增加了穩定排序的條件,確保多次執行結果的一致性 : `items = sorted(tree.headers.items(), key=lambda x: (sum(node.count for node in x[1]),x[0]))` * **輸出結果格式**:作業要求,並增加了支持度百分比的計算,並且存取和調整需要的輸出資料 : `frequent_patterns[tuple(new_pattern)] = support`。 ```py from collections import defaultdict, namedtuple from itertools import chain, combinations # 建立 FP Tree 的節點: # 可以表示每個節點的計數 class FPTreeNode: def __init__(self, item, count, parent): self.item = item self.count = count self.parent = parent self.children = {} self.link = None # 用於節點間的鏈接 def increment(self, count): self.count += count # 建立 FP Tree: class FPTree: def __init__(self): self.root = FPTreeNode(None, 1, None) self.headers = defaultdict(list) # 每個項目的表頭 # insert 將交易資料輸入到樹的資料結構中 def add_transaction(self, transaction, count): current_node = self.root for item in transaction: if item not in current_node.children: # 創建新節點 new_node = FPTreeNode(item, count, current_node) current_node.children[item] = new_node # 更新表頭鏈接 self.headers[item].append(new_node) else: current_node.children[item].increment(count) current_node = current_node.children[item] def is_single_path(self, node=None): if node is None: node = self.root while len(node.children) == 1: node = next(iter(node.children.values())) return len(node.children) == 0 # FP-Growth 遞歸挖掘 def get_fpgrowth(tree, min_support, suffix, frequent_patterns): items = sorted(tree.headers.items(), key=lambda x: (sum(node.count for node in x[1]),x[0])) for item, nodes in items: # 計算支持度 support = sum(node.count for node in nodes) if support >= min_support: # print("support",support) # 發現頻繁項目集 new_pattern = suffix + [item] frequent_patterns[tuple(new_pattern)] = support # 構建條件模式基 conditional_tree = FPTree() for node in nodes: path = [] parent = node.parent while parent and parent.item is not None: path.append(parent.item) parent = parent.parent path.reverse() if path: conditional_tree.add_transaction(path, node.count) # 遞歸查找子樹的頻繁模式 if conditional_tree.root.children: get_fpgrowth(conditional_tree, min_support, new_pattern, frequent_patterns) ``` 使用以下程式碼,將欲處理好的交易資料加入到 FP-Tree 中 對於每筆交易,逐項插入 FP-Tree,並使用 `headers` 字典鏈接每個相同項目的節點。 ```py # 構建 FP-Tree tree = FPTree() for transaction in processed_transactions: tree.add_transaction(transaction, 1) ``` 3. 使用條件模式基(Conditional Pattern Base)來生成條件 FP-Tree,並遞歸地從子樹中挖掘頻繁模式。 - 針對每個項目生成條件模式基,並構建子 FP-Tree。 - 遞歸地對條件 FP-Tree 進行頻繁項目集挖掘,直到所有頻繁項目集均被找到。 - 透過上方定義好的 `get_fpgrowth` 函式進行。 ```py # 挖掘頻繁模式 min_support = int(min_support / 100 * lengh) frequent_patterns = {} get_fpgrowth(tree, min_support, [], frequent_patterns) ``` 4. **結果輸出**: - 將所有頻繁項目集及其支持度以指定格式輸出。 ```py # Task 3 / Result 1 / all frequent itemsets def weitefile_itemset(items,lengh,fname,minSupport): # fname = "T10I6001M" with open(options.path + '/Step3_task1_'+fname+'_'+str(minSupport)+'_result1.txt','w') as outfile: for item, support in sorted(items.items(), key=lambda item: item[1], reverse=True): temp = ','.join(list(item)) support = round(support/lengh*100,1) # take percentage % and round to the first decimal place outfile.write("{:.1f}".format(support)+'\t{'+temp+'}\n') ``` ### FP-Growth 演算法的改進與優勢(Differences/Improvements compared to Apriori) FP-Growth 比 Apriori 更高效的原因 - **無需生成候選項目集**:FP-Growth 無需生成候選項目集,而是使用 FP-Tree 直接在壓縮後的樹結構中進行模式挖掘,極大地減少了內存和計算資源的消耗。 - **條件模式基的遞歸挖掘**:FP-Growth 的條件模式基(Conditional Pattern Base)和遞歸結構允許它針對頻繁模式進行逐層分解和處理,從而避免了重複計算。 - **空間利用率更高**:FP-Tree 通過壓縮交易資料來節省內存,特別是當多筆交易具有相同或部分重疊的項目時,FP-Tree 可以顯著降低空間複雜度。 - **效率提升**:FP-Growth 避免了在每一階段產生大量候選項目集的計算開銷,特別是在數據量大的情況下效率更高。 >FP-Growth 在處理大數據集方面的擴展性良好,但依然會受制於記憶體資源。以下是演算法在數據集規模擴展時的效能變化: - **計算時間隨數據量的變化**:FP-Growth 的運行時間隨數據量增加而增加,尤其在頻繁項目較多的情況下。但相較於 Apriori,FP-Growth 的時間增長更為平緩,對大數據的擴展性更優。 - **支持的最大數據集大小**:在典型的 16 GB 記憶體環境下,FP-Growth 可處理數百萬條交易記錄的大型數據集。當數據集規模繼續擴大時,可以考慮分散式 FP-Growth 以處理更大的數據集。 >FP-Growth 透過 FP-Tree 壓縮數據並進行高效的遞歸模式挖掘,適合於大規模數據分析。 ### Sept III. Computation time and Anaylsis : 以下針對不同的資料集與不同的最小支持度來做一些分析,其中冒號後方的數字為Frequent Itemset的總數量。 - In datasetA with minimum support 0.3% : 7776 <img src=https://hackmd.io/_uploads/HklRVkkWyl.png/> - In datasetA with minimum support 0.6% : 909 <img src=https://hackmd.io/_uploads/S1-xHk1Wyg.png/> - In datasetA with minimum support 0.9% : 414 <img src=https://hackmd.io/_uploads/SyIbBykZ1e.png/> - In datasetB with minimum support 0.2% : 5820 <img src=https://hackmd.io/_uploads/HJ3GHk1Wyx.png/> - In datasetB with minimum support 0.4% : 1445 <img src=https://hackmd.io/_uploads/B10QB11W1g.png/> - In datasetB with minimum support 0.6% : 667 <img src=https://hackmd.io/_uploads/B1_BHkk-Jx.png/> - In datasetC with minimum support 0.5% : 991 <img src=https://hackmd.io/_uploads/ry1wH1y-yx.png/> - In datasetC with minimum support 1.0% : 356 <img src=https://hackmd.io/_uploads/rye_rk1Wyl.png/> - In datasetC with minimum support 1.5% : 247 <img src=https://hackmd.io/_uploads/r1ZFSkyWJe.png/> 以上的時間計算皆不包含寫入檔案所花費得時間。 從這些結果中可以看出,**FP-Growth 演算法(Step 3)在處理大型數據集時相比 Apriori(Step 2)所花費的時間大幅減少,但跟Apriori算法類似,如果minimum Support越小,所需要的計算時間也越多。** #### 分析結果 - **Dataset A**:FP-Growth 在 `datasetA` 中加速達 96.3% 到 98.7% 不等,尤其在較低支持度(0.3%)的情況下達到最大加速效果。 - **Dataset B**:FP-Growth 在 `datasetB` 中的加速比也顯著,在 0.2% 的支持度下加速達 99%,隨著支持度增加,加速比稍微下降,但仍在 97% 以上。 - **Dataset C**:對於更大的數據集 `datasetC`,FP-Growth 的加速效果依然穩定,保持在 96.4% 到 97.7% 之間。 - **隨著數據集規模擴大**,FP-Growth 依然能夠保持高效的計算速度。從 `datasetA` 到更大的 `datasetB` 和 `datasetC`,FP-Growth 的加速效果仍穩定在 96%-99% 之間。 - **支持的最大數據集大小**:FP-Growth 在處理百萬級以上的交易數據時表現良好,但在數據集更大時可能需要更大的內存空間,特別是當頻繁項目集增多時。若數據集進一步增大,可以考慮分散式 FP-Growth 演算法以支持更大規模的數據集。