[cpp,algorithm] FpTree_2_首次扫描数据库抽取项集_并根据支持度剪枝

16 篇文章 0 订阅
3 篇文章 0 订阅

实验目的:

接上一次的实验,1_从文件中读取数据集并将其解析

在实验1中,将数据从数据文件中导入到程序的事务数据库(transaction_database) 数据结构对象中,

本次实验目的是对数据库进行扫描,从中选出数据库中的所有项集(item)组成项集集合(itemset ).


实验思路:

接下来细化一下将要实现的功能是,fp-tree 算法中首次扫描数据库,从中抽取出数据库(transaction_database)

中所有的项集(item),将其存放值项集集合 (itemset) 数据结构对象中,并将项集总数 itemset.size  统计出来,

进而可以求出系统中的频繁项集的最小出现频度次数:min_frequency  =  min_sup * itemset.size  ,

(其中min_sup 是数据集中频繁项集的最小支持度百分比)

将求出的最小出现次数(最小频度)作为用来筛选一个项集是否会成为是频繁项集的阈值条件,

把itemset 中出现频度 < 最小频度阈值的项集通过剪枝(prune) 的手段将其移除。

然后,对剩余的项集集合(itemset) 中的项集(item )根据其出现的频度计数从大到小进行排序。 


实验方法:

根据上述信息进行数据结构的设计

1) . 项集集合 itemset 

itemset 具有对其中重复出现的 item 项进行计数,

并且会根据计数的大小来对其中的数据进行排序和删除。

所以将 itemset 的数据结构定为  unordered_map<item , size_t >,

并在即内部定义数据结构体 

struct item_with_counter

{

  item _item ;

  size_t counter ;

} ;

并在结构体的基础上,定义一个用于排列顺序的stl 数据类型:

typedef set<item_with_counter>  ordered_set ;

同样为了使得 set 可以实现根据项集 item 出现次数的大小从大到小进行排序的功能 ,

需要对item_with_counter 中的 < ,即小于号进行运算符重写

bool operator< (const item_wiith_counter & a, const item_with_counter &b ) 

{

   if (a.counter != b.counter )

return a.counter > b.counter ; //if not equal , descending by counter 

   else

return a.item < b.item ; //if counters equals to each other ,ascending by item itself 

//item is in type of char * , so need another ’operator<  ‘ overload by strcmp(a.item  , b.item ) 

}


2). 事务对象 transaction

由于一个事务transaction中可能包含着多个 item,即一个transaction对应的是多个items

所以使用 unordered_set< item > 来作为transaction的变量类型

typedef unordered_set <item> transaction ;


3).事务数据库 transaction_database

我们知道的是,在数据库中数据都是以记录(元组) 的方式来一行行的存放的,

而在这里我们将事务抽象成关系数据库中的记录来看待。

( 不过在这里应该清楚地了解到,二者区别很多,一个事务中的项集(item)的个数和种类都是不固定的,

但是在关系数据库中每条记录(record) 或是元组 (tuple) 中的 属性字段( 域,field )的个数是固定的,并且属性类型固定 )

所以,在事务数据库的数据结构对象的设计中仿照关系数据库中的每张表(table) 对元组(tuple) 进行组织的方式,

我们将其设定为是:

typedef  vector<transaction>  transaction_database ;


通过上述的一系列定义我们可以的到下面这样一个结构:


|_transaction_database_| :

|_transaction_|________items__________|

transaction[0] : { apple, banana }

transaction[1] : {water , paper , book , e-book}

....

transaction [n] : {book , beer,  food1 ,coffee, food2 ,food3}


和一个用来统计在该 transaction_database 中所出现的所有项的出现次数的数据结构

|_itemset_| :

|_item_|_counter_|

<apple  : 1>

<food2  : 3>

...

<foodn  : x> 


实验代码:

在昨天(确切的说应该是今天凌晨)实现的 fileParser.h   fileParser.cpp 的基础之上进行修改的。

由于数据结构发生了变化,所以对于存放文件解析出来的数据结构和变量也有相应的变动。



//item.h

#ifndef ITEM_H
#define ITEM_H

#include <string>

namespace fptSpace
{
	    typedef  std::string  item_type ;
		 
}
 
#endif //ITEM_H



//itemset.h

#ifndef ITEMSET_H
#define ITEMSET_H

#include <unordered_map>
#include <set>
#include <cstddef>
#include <cassert>

#include "item.h"

namespace fptSpace
{
	struct item_with_counter
	{
		item_type _item ;
		std::size_t counter ;
		 
		item_with_counter() 
		{}
		item_with_counter( item_type i , std::size_t c  ):
			_item(std::string (i ) ) , counter( c )  
		{}
	} ;


	//Operators overload ing for set's get order
	bool operator< ( const item_with_counter & , const item_with_counter&) ;

	typedef std::set<item_with_counter>  ordered_itemset ;

	//class of itemset is the object used for analyzing the frequency of
	//each stored in transaction_database 

	class itemset : private std::unordered_map< fptSpace::item_type , std::size_t >
	{
	 //here we define an type with the name of container_type
		//with it ,we could use the father's method 
	typedef  std::unordered_map<item_type, std::size_t > container_type ;

	public :
		void prune (std::size_t min_sup ) ;
	 void insert ( fptSpace::item_type i )
		{
			++(*this)[i] ;
		}
	 
		ordered_itemset get_ordered()  const ;
		using container_type::iterator ;
		using container_type::const_iterator ;
		using container_type::begin ;
		using container_type::end ;
		using container_type::size ; 
	} ;
	
}

//we implements the operator overloading method here 

inline bool fptSpace::operator< ( const item_with_counter &a , const item_with_counter &b )
{
	if ( a.counter != b.counter)
		return a.counter > b.counter ;
	else
		return a._item< b._item ;
}


#endif //ITEMSET_H

//itemset.cpp

#include "itemset.h"

using fptSpace::itemset ;


void itemset::prune ( std::size_t min_sup )
{
	for ( auto i = begin () ; i != end () ; )
		if( i->second < min_sup)
			i = erase (i) ;
		else
			i++ ;
}

fptSpace::ordered_itemset itemset::get_ordered() const
{
	ordered_itemset order_set ;
	item_with_counter* it ;

	for ( const auto &i : (*this ) )
	{
		it = new item_with_counter(i.first , i.second) ;
		order_set.insert( *it ) ;
	}
	return order_set ;
}

//transaction.h

#ifndef	TRANSACTION_H
#define TRANSACTION_H

#include <unordered_set>
#include <vector>
#include "item.h"
#include "itemset.h"

namespace fptSpace
{
	typedef std::unordered_set<item_type> transaction ;

	//here is the transaction_database which is parsed from the data file 
	class transaction_database : private std::vector<transaction>
	{
	public :
		void insert ( fptSpace::transaction trans ) ;
		fptSpace::itemset  extract_itemset () const ;

		//here are the unoverloaded functions from type vector<transaction>
		using std::vector<transaction>::iterator ;
		using std::vector<transaction>::const_iterator;
		using std::vector<transaction>::begin ;
		using std::vector<transaction>::end ;
		using std::vector<transaction>::size ;
	};

}

#endif //TRANSACTION_H

//transaction.cpp

#include <utility>
#include "transaction.h"

using fptSpace::transaction_database ;

void transaction_database::insert ( fptSpace::transaction trans )
{
	push_back( std::move(trans)) ;	
}

fptSpace::itemset transaction_database::extract_itemset() const
{
	fptSpace::itemset items ;

	for ( const transaction &i : (*this ) )
		for ( const item_type &j : i )
			items.insert( j ) ;

	return items ;
}
 

//parser.h

#ifndef PARSER_H
#define PARSER_H

#include <exception>
#include <string>
#include <vector>
#include "transaction.h"

 
namespace fptSpace
{
	fptSpace::transaction_database parseFromFile ( const std::string &filename ) ;

	class file_open_exception : public std::exception 
	{
	private :
		const std::string message ;
	public :
		file_open_exception ( const std::string &filename ) :
			message( std::string("fail to open fiel : ")+filename  )
		{}
		virtual ~file_open_exception () throw() {}
		virtual const char *what () const throw () 
		{
			return message.c_str () ;
		}
	};
} 

#endif //PARSER_H

//parser.cpp

#include <fstream>
#include <sstream>
#include <iostream>

#include "parser.h"

using namespace std ;

fptSpace::transaction_database fptSpace::parseFromFile ( const string &filename )
{
	fptSpace::transaction trans ;
	fptSpace::transaction_database transaction_db ;
	string line ;

	ifstream file ( filename ) ;

	if ( !file.is_open() )
		throw file_open_exception( filename ) ;

	while ( getline (file , line ) )
	{
		cout <<line <<endl ;

		int from = 0 , end ;
		string substr ;

		while ( line[from++] != ':') ;
		from += 1 ;

		while ( 1 )
		{
			end = from ;
			while ( line [end] != ',' && line[end] != '"' )
				end++ ;
			substr = line.substr(from , end - from ) ;

			cout <<substr <<endl;

			trans.insert(substr) ;

			if ( line[end] != '"')
				from = end+1 ;
			else
				break ;
		}//while 1 
		transaction_db.insert(move(trans)) ;
	}

	return transaction_db ;
}


//Main.cpp

#include <cstdio>
#include <cstdlib>
#include <iostream>

//#include "fileParser.h"
#include "parser.h"

using namespace std ;

int main ( void )
{
	string filename ("d:\\test\\a.dat") ;

	/*parser::transaction_database database ;
	database = parser::parseFile(filename) ;

 <span style="white-space:pre">	</span>cout<<endl<<"here are all the items in transaction database "<<endl;

	for ( const auto &i : database ) <span style="white-space:pre">	</span> //traverse vector < vector <string> >  ;  i   is  type of  vector<string>
		for (const auto &j : i )	//traverse vector <string >	;  j is the type of string  , out  put directly is ok 
			cout<<j<<endl ;
<span style="white-space:pre">	</span>*/
	fptSpace::transaction_database trans_db = fptSpace::parseFromFile(filename) ;

	//trans_db is a vector of transaction  , i is an transaction element in trans_db 
	//transaction is an unordered_set<string> with the element with the type of  string 
	//so , j is the iterator of  
	for ( const auto &i : trans_db ) 
		for ( const auto &j : i )
		 cout<<"name "<<j<<endl ;

	fptSpace::itemset itemSet = trans_db.extract_itemset() ;

	//itemset is the type of the unordered_map<item_type , size_t >
	//item_type is in type of the string 

	cout<<"itemset unordered " <<endl ;
	for ( const auto &i : itemSet)
		cout<<"name   "<<i.first<<"   frequency:   "<<i.second<<endl ;
		
	int total_item_counter = itemSet.size() ;
	double min_support ;
	int sup_threshold ;
	cout<<"input min support (min_sup < 1 )"<<endl;
	cin >> min_support ;

	sup_threshold = (int) (min_support*total_item_counter );
	cout<<"after calculation we got the threshold   "<<sup_threshold<<endl ;
	cout<<"itemset after  pruning" <<endl ;
        itemSet.prune(sup_threshold) ;

  <span style="white-space:pre">	</span> for ( const auto &i : itemSet)
		cout<<"name   "<<i.first<<"   frequency:   "<<i.second<<endl ;

  <span style="white-space:pre">	</span> cout<<"itemset after sorting "<<endl ;
  <span style="white-space:pre">	</span> fptSpace::ordered_itemset ordered_item_set = itemSet.get_ordered() ;

   <span style="white-space:pre">	</span>for ( const auto &i : ordered_item_set )
	   cout<<"name :  "<<i._item<<" frequency :"<<i.counter<<endl ;

	system("pause") ;
	return 0 ;
}


//a.dat

"Item1":"assert,beer,c"
"item2":"bread,deer,eraser,c"
"item3":"water,e-books,books,beer"
"item4":"water,e-books,books,beer"
"item5":"books"
"item6":"beer"
"item7":"beer,books"


实验运行结果:

如果整个程序运行没有错误的话,将会显示如下的结果:


     


实验不足与改进与收获:


后续文章将要接近fp-tree 的主题了,总体上来说打印的格式不好,不过通过写这个程序我学会了如何使用文件来向程序中导入测试数据。

这样今后就可以对程序进行更加复杂和大数据量的测试了。


至于实现这个程序的时候 itemset 是类型的unordered_map <item_type , size_t > 的,原先是这么写的,但是由于编译出错,

只能够将 item_type 修改为string。而由于 item 就是string类型的,所以暂时蒙混过关了,实际上按照我上面的这种写法,

一旦,item_type 不是 string 类型的,而是一个由 struct 描述复合变量类型的时候,unordered_map 就会在编译的时候,

就会报出 自定义的struct 变量类型是不能够做 unordered_map 的hash索引值这样的编译错误,得到写完之后打算写一个

unordered_map, unordered_set 这种复杂STL数据类型的使用专题,顺便研究一下c++ reference 上面的代码和说明文档。


实验二结束

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
非常抱歉,我犯了一个错误。确实,`create_access_token`函数没有`algorithm`和`private_key`这两个参数,因为`flask_jwt_extended`库默认使用HS256算法进行JWT签名和验证,所以我们需要先更改默认算法并添加我们自己的密钥。 下面是一个使用RSA非对称加密的示例: 1. 安装所需的依赖 ``` pip install flask flask_jwt_extended cryptography ``` 2. 生成RSA密钥对 ``` from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives import serialization private_key = rsa.generate_private_key( public_exponent=65537, key_size=2048, ) public_key = private_key.public_key() # 保存私钥 with open('private_key.pem', 'wb') as f: f.write(private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption() )) # 保存公钥 with open('public_key.pem', 'wb') as f: f.write(public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo )) ``` 3. 更改默认的JWT算法和密钥 ``` from flask import Flask from flask_jwt_extended import JWTManager from cryptography.hazmat.primitives import serialization from cryptography.hazmat.backends import default_backend app = Flask(__name__) app.config['JWT_SECRET_KEY'] = 'super-secret' # 这个密钥不用于签名,只用于加密 app.config['JWT_ALGORITHM'] = 'RS256' jwt = JWTManager(app) # 加载RSA密钥 with open('private_key.pem', 'rb') as f: private_key = serialization.load_pem_private_key( f.read(), password=None, backend=default_backend() ) with open('public_key.pem', 'rb') as f: public_key = serialization.load_pem_public_key( f.read(), backend=default_backend() ) # 设置JWT密钥和算法 @jwt.token_in_blacklist_loader def check_if_token_in_blacklist(decrypted_token): jti = decrypted_token['jti'] return jti in blacklist @jwt.user_identity_loader def user_identity_lookup(user): return user.id @jwt.user_loader_callback_loader def user_loader_callback(identity): return User.query.get(identity) @jwt.token_in_blacklist_loader def check_if_token_in_blacklist(decrypted_token): jti = decrypted_token['jti'] return jti in blacklist @jwt.additional_claims_loader def add_claims_to_access_token(identity): return { 'foo': 'bar' } if __name__ == '__main__': app.run() ``` 在这个示例中,我们使用RSA非对称加密算法来对JWT进行签名和验证。在加载RSA密钥时,我们使用`cryptography`库来读取PEM格式的密钥文件并转换为`cryptography`库可以使用的密钥对象。然后,我们使用`@jwt.token_in_blacklist_loader`装饰器来添加一个回调函数,以便我们可以在Token黑名单中检查Token是否存在。我们还使用`@jwt.user_identity_loader`和`@jwt.user_loader_callback_loader`装饰器来指定用户的身份和用户回调函数。最后,我们使用`@jwt.additional_claims_loader`装饰器来添加自定义的JWT声明。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值