use std::{cmp::Ordering, collections::HashMap, usize};
use crate::tree::Tree;
use crate::ItemType;
pub struct FPGrowth<T> {
transactions: Vec<Vec<T>>,
minimum_support: usize,
}
impl<T: ItemType> FPGrowth<T> {
pub fn new(transactions: Vec<Vec<T>>, minimum_support: usize) -> FPGrowth<T> {
FPGrowth {
transactions,
minimum_support,
}
}
pub fn find_frequent_patterns(&self) -> Vec<(Vec<T>, usize)> {
let mut items = HashMap::new();
for transaction in self.transactions.iter() {
for &item in transaction.iter() {
let count = items.entry(item).or_insert(0);
*count += 1;
}
}
let cleaned_items: HashMap<&T, &usize> = items
.iter()
.filter(|(_, &count)| count >= self.minimum_support)
.collect();
let mut tree = Tree::<T>::new();
for transaction in self.transactions.clone().into_iter() {
let mut cleaned_transaction: Vec<T> = transaction
.into_iter()
.filter(|item| cleaned_items.contains_key(item))
.collect();
cleaned_transaction.sort_by(|a, b| {
let &a_counter = cleaned_items.get(a).unwrap();
let &b_counter = cleaned_items.get(b).unwrap();
match b_counter.cmp(a_counter) {
Ordering::Equal => {
match b.cmp(a) {
Ordering::Greater => Ordering::Less,
Ordering::Less => Ordering::Greater,
Ordering::Equal => Ordering::Equal,
}
}
Ordering::Less => Ordering::Less,
Ordering::Greater => Ordering::Greater,
}
});
tree.add_transaction(cleaned_transaction);
}
self.find_with_suffix(&tree, &[])
}
fn find_with_suffix(&self, tree: &Tree<T>, suffix: &[T]) -> Vec<(Vec<T>, usize)> {
let mut results = vec![];
for (item, nodes) in tree.get_all_items_nodes().iter() {
let mut support = 0;
for node in nodes.iter() {
support += node.count();
}
if support >= self.minimum_support && !suffix.contains(item) {
let mut frequent_pattern = vec![*item];
frequent_pattern.append(&mut Vec::from(suffix));
results.push((frequent_pattern.clone(), support));
let partial_tree = Tree::generate_partial_tree(&tree.generate_prefix_path(*item));
results.append(&mut self.find_with_suffix(&partial_tree, &frequent_pattern));
}
}
results
}
}