使用Hive SQL计算TF-IDF的方法和流程

TF-IDF的公式:

逆向文件频率 (inverse document frequency, IDF) IDF的主要思想是:如果包含词条t的文档越少, IDF越大,则说明词条具有很好的类别区分能力。某一特定词语的IDF,可以由总文件数目除以包含该词语之文件的数目,再将得到的商取对数得到。

  某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的TF-IDF。因此,TF-IDF倾向于过滤掉常见的词语,保留重要的词语。

公式很简单,假如没有python环境,spark环境,如何得到样本的TF-IDF呢?可以试着使用Hive SQL来实现:

例子:

目前,我们有一份用户的访问某种事件的次数的数据hive表为user_tags,这里次数可以相当于词频,事件可以表示是词条。数据表大致字段是这样的:month,userid,tags ,month为月份 格式入:yyyyMM ,userid 为字符串用户 ID,tags为事件次数,为map类型,例如:a1:12,a2:23 a1为事件,12为事件出现的次数。

(1)计算每月的|D|

 
  1. select count(1) as D,month from user_tags group by month;

(3)计算每月的DF(t,D)

 
  1. select month,tag,count(1) as DF from (select month,tag from user_tags lateral view explode(tags) tb_tags as tag,tag_num) a group by month,tag;

(3)计算每月标签的IDF(t,D)

 
  1. select i1.month,i1.tag,log10((i2.D)/(i1.DF+1)) as IDF from
  2. (select month,tag,count(1) as DF from (select month,tag from user_tags lateral view explode(tags) tb_tags as tag,tag_num) a group by month,tag) i1
  3. left join (select count(1) as D,month from user_tags group by month) i2 on i1.month = i2.month;

(4)计算每月标签的TF(t,d)

 
  1. select month,userid,tag,cast(tag_num as double) as TF from user_tags lateral view explode(tags) tb_tags as tag,tag_num;

(5)将上面的步骤汇总计算TFIDF(t,d,D),使用TF表关联IDF表,进行计算

 
  1. create table user_tags_tfidf as
  2. select month,userid,concat_ws(',',collect_set(concat(tag,':',TFIDF))) as tfidfs from
  3. (select a.month,a.userid,a.tag,round(a.TF*b.IDF,2) as TFIDF from
  4. (select month,userid,tag,cast(tag_num as double) as TF from user_tags lateral view explode(tags) tb_tags as tag,tag_num) a
  5. left join
  6. (
  7. select i1.month,i1.tag,log10((i2.D)/(i1.DF+1)) as IDF from
  8. (select month,tag,count(1) as DF from (select month,tag from user_tags lateral view explode(tags) tb_tags as tag,tag_num) a group by month,tag) i1
  9. left join
  10. (select count(1) as D,month from user_tags group by month) i2 on i1.month = i2.month
  11. ) b on a.month = b.month and a.tag = b.tag) c group by month,userid;

结论,这么简单的公式,SQL完全可以实现。

假设子任务一的结果已经保存在了Hudi的dwd库中的某个表中,下面给出Spark-Shell的代码示例: ```scala // 导入必要的库 import org.apache.spark.sql.functions._ import org.apache.spark.ml.feature.{HashingTF, IDF} import org.apache.spark.ml.linalg.{SparseVector, Vector} import org.apache.spark.sql.DataFrame // 读取子任务一的结果 val subtask1DF = spark.read.table("dwd.subtask1_result") // 筛选出与用户id为6708购买过相同商品的前10位用户id(不考虑相同商品购买次数) val top10UserDF = subtask1DF.filter($"user_id" =!= 6708) .groupBy($"user_id") .agg(countDistinct($"product_id").as("common_count")) .orderBy($"common_count".desc) .limit(10) // 获取这10位用户已购买过的商品 val productsDF = spark.read.jdbc( url = "jdbc:mysql://localhost:3306/shtd_store", table = "products", properties = Map("user" -> "username", "password" -> "password") ) val top10ProductsDF = top10UserDF.join(subtask1DF, Seq("user_id")) .select($"product_id") .distinct() val user6708ProductsDF = subtask1DF.filter($"user_id" === 6708) .select($"product_id") .distinct() val recommendedProductsDF = top10ProductsDF.join(productsDF, Seq("product_id")) .except(user6708ProductsDF.join(productsDF, Seq("product_id"))) // 计算这10位用户已购买的商品与用户6708已购买的商品数据集中商品的余弦相似度 val hashingTF = new HashingTF().setInputCol("product_id").setOutputCol("rawFeatures") val featurizedDF = subtask1DF.groupBy($"user_id").agg(collect_list($"product_id").as("product_list")) .select($"user_id", hashingTF($"product_list").as("rawFeatures")) val idf = new IDF().setInputCol("rawFeatures").setOutputCol("features") val idfModel = idf.fit(featurizedDF) val rescaledDF = idfModel.transform(featurizedDF).select($"user_id", $"features".as("user_features")) val user6708Features = rescaledDF.filter($"user_id" === 6708).select($"user_features").head.getAs[SparseVector](0) val dotProductUDF = udf((v1: Vector, v2: Vector) => v1.dot(v2)) val cosineSimilarityUDF = udf((v1: Vector, v2: Vector) => v1.dot(v2) / (v1.norm(2) * v2.norm(2))) val similarityDF = rescaledDF.filter($"user_id" =!= 6708) .withColumn("similarity", cosineSimilarityUDF($"user_features", lit(user6708Features))) .select($"user_id", $"similarity") // 获取均值前5的商品id val recommendedProducts = recommendedProductsDF.select($"product_id") .collect() .map(_.getString(0)) val top5Products = subtask1DF.filter($"product_id".isin(recommendedProducts:_*)) .groupBy($"product_id") .agg(sum($"quantity").as("total_quantity")) .orderBy($"total_quantity".desc) .limit(5) .select($"product_id") .collect() .map(_.getString(0)) ``` 上述代码中,我们先读取了子任务一的结果,并筛选出与用户id为6708购买过相同商品的前10位用户id。然后通过Hudi的dwd库中的products表或MySQL数据库shtd_store中的products表获取这10位用户已购买过的商品,并剔除用户6708已购买的商品。接着,我们将用户购买的商品列表转化为TF-IDF向量,并计算余弦相似度,得到每个用户与用户6708的相似度。最后,我们根据相似度计算出均值前5的商品id作为推荐使用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值