利用关联规则实现推荐算法

关联规则是以规则的方式呈现项目之间的相关性:关联规则(Association Rules)是反映一个事物与其他事物之间的相互依存性和关联性,是数据挖掘的一个重要技术,用于从大量数据中挖掘出有价值的数据项之间的相关关系。
关联规则的经典例子是通过发现顾客放入其购物篮中的不同商品之间的联系,可分析顾客的购买习惯。通过了解哪些商品频繁地被顾客同时购买,可以帮助零售商制定营销策略。在医学方面,研究人员希望能够从已有的成千上万份病历中找到患某种疾病的病人的共同特征,寻找出更好的预防措施。
利用关联规则实现推荐算法
文章图片

Apriori Algorithm(先验) 它是一种购物车的分析方法,用于揭示产品之间的关联关系。
他有三个简单的公式:
Support(X, Y) = Freq(X, Y) / N :它表示 X 和 Y 一起出现的概率。它是 X 和 Y 一起出现的频率除以 N。
Confidence(X, Y) = Freq(X, Y) / Freq(X) :表示购买产品X时购买产品Y的概率。X 和 Y 一起出现的频率除以 X 出现的频率。
Lift = Support(X, Y) / (Support(x) * Support (Y)) :当购买X时,购买Y的概率增加了lift的倍数。X 和 Y 一起出现的概率是 X 和 Y 分别出现的概率的乘积。它陈述了一个表达式,例如当我们购买一种产品时,购买另一种产品的概率会增加多少倍。
下面我们将使用Apriori Algorithm向用户推荐相应的产品
数据预处理 【利用关联规则实现推荐算法】这里我们使用的数据集是online retail II dataset

!pip install mlxtend import pandas as pdpd.set_option('display.max_columns', None) pd.set_option('display.max_rows', None) pd.set_option('display.width', 500) # It ensures that the output is on one line. pd.set_option('display.expand_frame_repr', False) from mlxtend.frequent_patterns import apriori, association_rulesdf_ = pd.read_excel("datasets/online_retail_II.xlsx", sheet_name = "Year 2010-2011") df = df_.copy() df.info() # ColumnNon-Null CountDtype # ---------------------------- #0Invoice541910 non-nullobject #1StockCode541910 non-nullobject #2Description540456 non-nullobject #3Quantity541910 non-nullint64 #4InvoiceDate541910 non-nulldatetime64[ns] #5Price541910 non-nullfloat64 #6Customer ID406830 non-nullfloat64 #7Country541910 non-nullobjectdf.head() #Invoice StockCodeDescriptionQuantityInvoiceDatePriceCustomer IDCountry # 053636585123AWHITE HANGING HEART T-LIGHT HOLDER6 2010-12-01 08:26:002.5517850.0United Kingdom # 153636571053WHITE METAL LANTERN6 2010-12-01 08:26:003.3917850.0United Kingdom # 253636584406BCREAM CUPID HEARTS COAT HANGER8 2010-12-01 08:26:002.7517850.0United Kingdom # 353636584029GKNITTED UNION FLAG HOT WATER BOTTLE6 2010-12-01 08:26:003.3917850.0United Kingdom # 453636584029ERED WOOLLY HOTTIE WHITE HEART.6 2010-12-01 08:26:003.3917850.0United Kingdom

我们使用这个函数来确定数据的阈值。
def outlier_thresholds(dataframe, variable): quartile1 = dataframe[variable].quantile(0.01) quartile3 = dataframe[variable].quantile(0.99) interquantile_range = quartile3 - quartile1 up_limit = quartile3 + 1.5 * interquantile_range low_limit = quartile1 - 1.5 * interquantile_range return low_limit, up_limit

下面这个函用阈值替换了异常值。
def replace_with_thresholds(dataframe, variable): low_limit, up_limit = outlier_thresholds(dataframe, variable) dataframe.loc[(dataframe[variable] < low_limit), variable] = low_limit dataframe.loc[(dataframe[variable] > up_limit), variable] = up_limit

第三个函数中我们从数据中提取包含“C”的值。“C”表示退回的物品。要计算总价,变量数量和价格必须大于零。在这个函数中还调用了 Outlier 和 Threshold 函数。
def retail_data_prep(dataframe): dataframe.dropna(inplace=True) dataframe = dataframe[~dataframe["Invoice"].str.contains("C", na=False)] dataframe = dataframe[dataframe["Quantity"] > 0] dataframe = dataframe[dataframe["Price"] > 0] replace_with_thresholds(dataframe, "Quantity") replace_with_thresholds(dataframe, "Price") return dataframedf = retail_data_prep(df)

准备购买-物品的矩阵 数据集中的收据(Invoice)包含了产品的购买,所以我们先处理这个
df_gr = df[df['Country'] == 'Germany'] df_gr.head() Invoice StockCodeDescriptionQuantityInvoiceDatePriceCustomer IDCountry 110953652722809SET OF 6 T-LIGHTS SANTA6.0 2010-12-01 13:04:002.9512662.0Germany 111053652784347ROTATING SILVER ANGELS T-LIGHT HLDR6.0 2010-12-01 13:04:002.5512662.0Germany 111153652784945MULTI COLOUR SILVER T-LIGHT HOLDER12.0 2010-12-01 13:04:000.8512662.0Germany 1112536527222425 HOOK HANGER MAGIC TOADSTOOL12.0 2010-12-01 13:04:001.6512662.0Germany 1113536527222443 HOOK HANGER MAGIC GARDEN12.0 2010-12-01 13:04:001.9512662.0Germany

根据 Invoice 和 Description,我们通过 groupby 计算 Quantities,可以计算产品的数量。
df_gr.groupby(['Invoice', 'Description']).agg({"Quantity": "sum"}).head(20)# Invoice Description # 5365273 HOOK HANGER MAGIC GARDEN12.0 #5 HOOK HANGER MAGIC TOADSTOOL12.0 #5 HOOK HANGER RED MAGIC TOADSTOOL12.0 #ASSORTED COLOUR LIZARD SUCTION HOOK24.0 #CHILDREN'S CIRCUS PARADE MUG12.0 #HOMEMADE JAM SCENTED CANDLES12.0 #HOT WATER BOTTLE BABUSHKA4.0 #JUMBO BAG OWLS10.0 #JUMBO BAG WOODLAND ANIMALS10.0 #MULTI COLOUR SILVER T-LIGHT HOLDER12.0 #PACK 3 FIRE ENGINE/CAR PATCHES12.0 #PICTURE DOMINOES12.0 #POSTAGE1.0 #ROTATING SILVER ANGELS T-LIGHT HLDR6.0 #SET OF 6 T-LIGHTS SANTA6.0 # 5368406 RIBBONS RUSTIC CHARM12.0 #60 CAKE CASES VINTAGE CHRISTMAS24.0 #60 TEATIME FAIRY CAKE CASES24.0 #CAKE STAND WHITE TWO TIER LACE2.0 #JAM JAR WITH GREEN LID12.0

我们使用 unstack 来避免重复的索引,使用 iloc 来显示前 5 个观察结果。如果产品不在收据中,则 使用NA 表示。
df_gr.groupby(['Invoice', 'Description']).agg({"Quantity": "sum"}).unstack().iloc[0:5, 0:5]# Description50'S CHRISTMAS GIFT BAG LARGEDOLLY GIRL BEAKERI LOVE LONDON MINI BACKPACKRED SPOT GIFT BAG LARGESET 2 TEA TOWELS I LOVE LONDON # Invoice # 536527NaNNaNNaNNaNNaN # 536840NaNNaNNaNNaNNaN # 536861NaNNaNNaNNaNNaN # 536967NaNNaNNaNNaNNaN # 536983NaNNaNNaNNaNNaN

进行独热编码。把 NA 的地方写 0。
df_gr.groupby(['Invoice', 'Description']).agg({"Quantity": "sum"}).unstack().fillna(0).iloc[0:5, 0:5]# Description50'S CHRISTMAS GIFT BAG LARGEDOLLY GIRL BEAKERI LOVE LONDON MINI BACKPACKRED SPOT GIFT BAG LARGESET 2 TEA TOWELS I LOVE LONDON # Invoice # 5365270.00.00.00.00.0 # 5368400.00.00.00.00.0 # 5368610.00.00.00.00.0 # 5369670.00.00.00.00.0 # 5369830.00.00.00.00.0

如果发票中的产品数量大于0,我们就写1,如果小于0或0,我们就写0。用apply对行或列进行操作。这里将通过应用 applymap 并执行操作来遍历所有单元格。
df_gr.groupby(['Invoice', 'Description']).agg({"Quantity": "sum"}).unstack().fillna(0).applymap(lambda x: 1 if x > 0 else 0).iloc[0:5, 0:5]# Description50'S CHRISTMAS GIFT BAG LARGEDOLLY GIRL BEAKERI LOVE LONDON MINI BACKPACKRED SPOT GIFT BAG LARGESET 2 TEA TOWELS I LOVE LONDON # Invoice # 53652700000 # 53684000000 # 53686100000 # 53696700000 # 53698300000

我们创建了一个名为 create_invoice_df 的函数。如果想根据id变量搜索并得到结果,它会根据stockcode进行与上述相同的操作。如果我们输入的id为False,它会根据Description执行上面的操作。
def create_invoice_product_df(dataframe, id=False): if id: return dataframe.groupby(['Invoice', "StockCode"])['Quantity'].sum().unstack().fillna(0). \ applymap(lambda x: 1 if x > 0 else 0) else: return dataframe.groupby(['Invoice', 'Description'])['Quantity'].sum().unstack().fillna(0). \ applymap(lambda x: 1 if x > 0 else 0)gr_inv_pro_df = create_invoice_product_df(df_gr) gr_inv_pro_df.head(20)gr_inv_pro_df = create_invoice_product_df(df_gr, id=True) gr_inv_pro_df.head()def check_id(dataframe, stock_code): product_name = dataframe[dataframe["StockCode"] == stock_code][["Description"]].values[0].tolist() print(product_name)check_id(df_gr, 16016) # ['LARGE CHINESE STYLE SCISSOR']

所有可能的产品组合
frequent_itemsets = apriori(gr_inv_pro_df, min_support=0.01, use_colnames=True) frequent_itemsets.sort_values("support", ascending=False).head()#supportitemsets # 5380.818381(POST) # 1890.245077(22326) # 18640.225383(POST, 22326) # 1910.157549(22328) # 19310.150985(22328, POST)check_id(df_gr, 22328) #['ROUND SNACK BOXES SET OF 4 FRUITS ']

通过将我们用 Apriori 找到的Support插入到 association_rules 函数中,找到一些其他的统计数据,例如置信度和提升度。
rules = association_rules(frequent_itemsets, metric="support", min_threshold=0.01)rules.sort_values("support", ascending=False).head()

POST产品和编号为22326的产品同时出现的概率为0.225383。被一起买的概率是0.275401。同时购买这两种产品的概率增加为1.123735。
# antecedents consequentsantecedent supportconsequent supportsupportconfidenceliftleverageconviction # 2650(POST)(22326)0.8183810.2450770.2253830.2754011.1237350.0248171.041850 # 2651(22326)(POST)0.2450770.8183810.2253830.9196431.1237350.0248172.260151 # 2784(22328)(POST)0.1575490.8183810.1509850.9583331.1710120.0220494.358862 # 2785(POST)(22328)0.8183810.1575490.1509850.1844921.1710120.0220491.033038 # 2414(22328)(22326)0.1575490.2450770.1312910.8333333.4002980.0926794.529540

数据集地址:https://archive.ics.uci.edu/m...
作者:Cem B?kmaz

    推荐阅读