频繁项集挖掘算法Apriori + FpGrowth(C++代码)

频繁项集挖掘算法Apriori + FpGrowth(C++代码)

Apriori算法代码

#include <iostream>
#include <vector>
#include <fstream>
#include <string>
#include <map>
#include <algorithm>
#include <ctime>
#include <cstring>
using namespace std;


double minsup;                      // 最小支持度
int minSupport;                     // 最小支持度下数量
vector<vector<int> > events;        // 存储事务集合
const int maxn = 88888;
bool book[maxn], tbook[maxn];
map<int, int> M;
int freqItemsCount;
ofstream fout("AprioriFreqItemSet.txt");

// 读入事务集
void readEvent() {
    ifstream fin("retail.dat");
    int eventCount = 0;             // 事务数量
    string line;
    while(getline(fin, line)) {
        vector<int> event;          // 存储单个事务
        int num = 0;
        line += ' ';                // 方便处理最后一个数
        for(int i = 0; line[i] != '\0'; i++) {
            if(isdigit(line[i])) { num *= 10; num += line[i] - '0'; }
            else {
                if(isdigit(line[i - 1])) {
                    event.push_back(num);
                    M[num]++;
                }
                num = 0;
            }
        }
        sort(event.begin(), event.end());   // 排序,提高检索效率
        events.push_back(event);            // 将事务添加进事务集
        eventCount++;
    }
    minSupport = ceil(eventCount * minsup);
    fin.close();
}
// 生成频繁一项集
void generateOneItemSet(vector<pair<vector<int>, int> >& Ck) {
    int eventsSize = events.size();
    for(int i = 0; i < eventsSize; i++)
        for(auto item : events[i]) {
            if(M[item] >= minSupport) {
                book[i] = true;
                break;
            }
        }
    for(auto it : M)
        if(it.second >= minSupport)             // 满足条件则加入候选项
            Ck.push_back(make_pair(vector<int> (1, it.first), it.second));
}
// 检查两个项集能否连接
bool check(vector<int>& v1, vector<int>& v2) {
    int len = v1.size();
    for(int i = 0; i < len - 1; i++)        // 前 k - 1 项必须相同
        if(v1[i] != v2[i]) return false;
    return true;
}
// 计算该项集出现次数
int itemCount(vector<int>& item) {
    int cnt = 0;
    int len1 = item.size();
    int eventsSize = events.size();
    for(int i = 0; i < eventsSize; i++) {
        if(!book[i]) continue;
        int len2 = events[i].size();
        if(len2 < len1) continue;    // 该事务项数小于新集合长度,不可能包含
        int p1 = 0, p2 = 0;
        while(p1 < len1 && p2 < len2) {
            if(item[p1] == events[i][p2]) p1++;
            p2++;
        }
        if(p1 == len1) {
            cnt++;       // 遍历至最后,说明包含该集合
            tbook[i] = true;
        }
    }
    return cnt;
}
// 连接生成 k + 1 候选项集
void Link(vector<pair<vector<int>, int> >& Ck) {
    memset(tbook, false, sizeof tbook);
    vector<pair<vector<int>, int> > itemSet;        // 存储候选项集
    int len = Ck.size();                            // 项集个数
    for(int i = 0; i < len; i++) {
        for(int j = i + 1; j < len; j++) {
            vector<int> tmp;            // 存储新项集
            if(check(Ck[i].first, Ck[j].first)) {
                tmp = Ck[i].first;
                tmp.push_back(Ck[j].first.back());
                if(tmp[tmp.size() - 1] < tmp[tmp.size() - 2])
                    swap(tmp[tmp.size() - 1], tmp[tmp.size() - 2]);
                if(itemCount(tmp) >= minSupport) {
                    itemSet.push_back(make_pair(tmp, itemCount(tmp)));
                }
            }
        }
    }
    memcpy(book, tbook, sizeof book);
    Ck = itemSet;
}
// 展示频繁项集
void showItemSet(vector<pair<vector<int> ,int> >& Ck) {
    if(Ck.size()) fout << "----" << Ck[0].first.size() << "项集----\n";
    for(auto it : Ck) {
        fout << "{ ";
        for(auto item : it.first) fout << item << " ";
        fout << "}: " << it.second << endl;
    }
    freqItemsCount += Ck.size();
}

int main(void)
{
    cout << "请输入最小支持度:";
    cin >> minsup;
    double startTime = clock();         // 记录开始时间
    vector<pair<vector<int>, int> > Ck; // 存储候选项
    readEvent();                        // 读入事务集
    generateOneItemSet(Ck);             // 生成频繁一项集
    showItemSet(Ck);                    // 展示频繁一项集
    while(Ck.size() > 1) {
        Link(Ck);                       // Ck --> Ck+1
        showItemSet(Ck);                // 展示频繁 k 项集
    }
    cout << "频繁项集个数:" << freqItemsCount << endl;
    cout << "频繁项集信息已保存到文件AprioriFreqItmeSet.txt" << endl;
    cout << "程序总用时:" << double(clock() - startTime) / CLOCKS_PER_SEC << endl;
    return 0;
}

FpGrowth算法代码

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <map>
#include <fstream>
#include <ctime>
#include <cstring>
#include <set>
using namespace std;

typedef vector<int> V;
typedef vector<pair<vector<int>, int> > VPV;
typedef vector<int>::iterator Vit;
int minSup; double mins;
const int maxn = 16500;
int M[maxn];
int pos[maxn];
// map<int, int> M;
// map<int, int> pos;                  // 标记一项集中各项存放位置
set<V> freqItemList;                // 存放频繁项集
// FpTree 的节点
struct fpNode {
    int id;
    vector<fpNode*> children;        // 孩子节点
    fpNode* parent;                  // 父节点
    fpNode* next;                    // 下一个相同 id 节点
    int count;                       // 该点出现次数
    fpNode() {
        id = -1;
        parent = next = nullptr;
        count = 0;
    }
};
typedef vector<fpNode*> headTab;    // 存放表头
// 排序规则
bool cmp(int a, int b) {
    return M[a] > M[b];
}
// 读入事务集
void readEvent(VPV& events, V& itemSet, headTab& head) {
    ifstream fin("retail.dat");
    int eventCount = 0;         // 事务数量
    string line;
    while(getline(fin, line)) {
        V event;
        int num = 0;
        line += ' ';
        for(int i = 0; line[i] != '\0'; i++) {
            if(isdigit(line[i])) { num *= 10; num += line[i] - '0'; }
            else {
                if(isdigit(line[i - 1])) {
                    event.push_back(num);
                    M[num]++;
                    itemSet.push_back(num);
                }
                num = 0;
            }
        }
        sort(event.begin(), event.end());
        events.push_back(make_pair(event, 1));            // 将事务添加进事务集
        eventCount++;
    }
    minSup = ceil(eventCount * mins);
    fin.close();
    sort(itemSet.begin(), itemSet.end());
    itemSet.erase(unique(itemSet.begin(), itemSet.end()), itemSet.end());
    sort(itemSet.begin(), itemSet.end(), cmp);      // 按出现次数排序
    for(Vit it = itemSet.begin(); it != itemSet.end(); it++) {  // 过滤掉低于最小支持度的项
        if(M[*it] < minSup) {
            itemSet.erase(it, itemSet.end());
            break;
        }
    }
    int itemCount = itemSet.size();
    for(int i = 0; i < itemCount; i++) pos[itemSet[i]] = i;
    head.resize(itemCount);
}
// 更新 FpTree
void updateFpTree(fpNode* inTree, headTab& head, Vit it, Vit end, int count) {
    if(it == end) return;
    bool flag = false;
    int len = inTree->children.size();
    for(int i = 0; i < len; i++) {
        fpNode* child = inTree->children[i];
        if(child->id == *it) {
            child->count += count;
            updateFpTree(child, head, it + 1, end, count);
            flag = true;
            break;
        }
    }
    if(!flag) {  // 将剩下的点直接接到该节点上
        while(it != end) {
            fpNode* tmp = new fpNode();
            tmp->id = *it;
            tmp->parent = inTree;
            tmp->count = count;
            tmp->next = head[pos[*it]];
            head[pos[*it]] = tmp;
            inTree->children.push_back(tmp);
            inTree = tmp;
            it++;
        }
    }
}
// 建立 FpTree
void createFpTree(fpNode* fpTree, VPV& events, headTab& head) {
    for(auto event : events) {
        stable_sort(event.first.begin(), event.first.end(), cmp);   // 排序
        for(Vit it = event.first.begin(); it != event.first.end(); it++) { // 过滤
            if(M[*it] < minSup) {
                event.first.erase(it, event.first.end());
                break;
            }
        }
        updateFpTree(fpTree, head, event.first.begin(), event.first.end(), event.second);
    }
}
// 展示 FpTree
void showFpTree(fpNode* inTree) {
    cout << inTree->id << endl;
    for(auto child : inTree->children) showFpTree(child);
}
// 获取项集
vector<int> getItemSet(fpNode* fp, V S) {
    if(fp->id == -1) return S;
    S.push_back(fp->id);
    return getItemSet(fp->parent, S);
}
// 利用 FpTree 挖掘频繁项集
void fpGrowth(headTab head, V path) {
    for(int i = head.size() - 1; i >= 0; i--) {
        fpNode* fp = head[i];
        VPV condEvents;          // 条件模式基
        V condItemSet;
        headTab condHead;
        // M.clear();
        memset(M, 0, sizeof M);
        // pos.clear();
        memset(pos, -1, sizeof pos);
        while(fp != nullptr) {
            V tmp;
            V condEvent = getItemSet(fp->parent, tmp);
            sort(condEvent.begin(), condEvent.end());
            condEvents.push_back(make_pair(condEvent, fp->count));
            for(auto item : condEvent) {
                M[item] += fp->count;
                condItemSet.push_back(item);
            }
            fp = fp->next;
        }
        // for(auto item : condItemSet) cout << item << " "; cout << endl;
        sort(condItemSet.begin(), condItemSet.end());
        condItemSet.erase(unique(condItemSet.begin(), condItemSet.end()), condItemSet.end());
        sort(condItemSet.begin(), condItemSet.end(), cmp);
        for(Vit it = condItemSet.begin(); it != condItemSet.end(); it++) {
            if(M[*it] < minSup) {
                condItemSet.erase(it, condItemSet.end());
                break;
            }
        }
        int itemCount = condItemSet.size();
        for(int i = 0; i < itemCount; i++) pos[condItemSet[i]] = i;
        condHead.resize(itemCount);
        // 建立条件 FpTree
        fpNode* condFpTree = new fpNode();
        createFpTree(condFpTree, condEvents, condHead);
        
        V newPath = path;
        newPath.push_back(head[i]->id);
        sort(newPath.begin(), newPath.end());
        freqItemList.insert(newPath);
        for(auto item : condItemSet) {
            V tmp = newPath;
            tmp.push_back(item);
            sort(tmp.begin(), tmp.end());
            freqItemList.insert(tmp);
        }
        if(condHead.size()) {
            fpGrowth(condHead, newPath);        // 继续挖掘
        }
    }
}

void showFreqItemSet() {
    cout << "频繁项个数:" << freqItemList.size() << endl;
    ofstream fout("FpGrowthFreqItemSet.txt");
    for(auto freqItems : freqItemList) {
        fout << "{ ";
        for(auto item : freqItems) fout << item << " ";
        fout << "}" << endl;
    }
    cout << "频繁项集信息已保存到文件FpGrowthFreqItemSet.txt" << endl;
    fout.close();
}

int main(void)
{
    cout << "请输入最小支持度:";
    cin >> mins;
    double startTime = clock();
    VPV events;         // 事务集
    V itemSet;          // 频繁一项集
    headTab head;       // 表头
    fpNode* fpTree = new fpNode();          // FpTree
    readEvent(events, itemSet, head);       // 读入事务集
    createFpTree(fpTree, events, head);     // 建立 FpTree
    cout << "建树用时:" << (clock() - startTime) / CLOCKS_PER_SEC << endl;
    // showFpTree(fpTree);
    V path;
    fpGrowth(head, path);
    cout << "程序总用时:" << (clock() - startTime) / CLOCKS_PER_SEC << endl;
    showFreqItemSet();
    return 0;
}
  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值