我做了一些研究,据我所知,有两种方法可以使用Apache Spark
创建推荐系统,一种方法是使用随附的MLLib
一个非常好的example,我尝试过并且很容易,另一方面,你可以使用ML
中的ALS。我觉得与RDD
合作非常舒服,但我正在尝试更频繁地使用DataFrames
以获得更多经验。
为了练习,我开始使用一些标准化ratings
的疯狂数据,并且我有超过4000条记录,只有5种可能的产品(如下所示)。所以我的第一个挑战是如何将此DataFrame
转换为所需的结构;几个小时后我读到源代码时猜到的结构。
val df = sqlContext.createDataFrame(sc.parallelize(List(Row(0.0, 0.12, 0.1, 0.0, 0.16),
Row(0.1, 0.0, 0.3, 0.52, 0.67))),
StructType(StructField("product1", DoubleType, true) ::
StructField("product2", DoubleType, true) ::
StructField("product3", DoubleType, true) ::
StructField("product4", DoubleType, true) ::
StructField("product5", DoubleType, true) :: Nil))
df.show
+--------+--------+--------+--------+--------+
|product1|product2|product3|product4|product5|
+--------+--------+--------+--------+--------+
| 0.0| 0.12| 0.1| 0.0| 0.16|
| 0.1| 0.0| 0.3| 0.52| 0.67|
+--------+--------+--------+--------+--------+
我做了几次,不知何故复杂的转换,我想看看是否有更好的方法来获得所需的结构。
val rdd = df.rdd.zipWithIndex.map {
case (row, index) => row.toSeq.zipWithIndex.map(x => Row(index.toInt, x._2.toInt, x._1))
}.flatMap{x => x}
val (train, testing) = rdd.partitionBy(_.get(2) != 0.0)
val rdds = List(train, testing)
然后我将这些RDD
转换为DataFrame
s。
val dfs = rdds.map(sqlContext.createDataFrame(_, StructType(StructField("user", IntegerType, true) ::
StructField("product", IntegerType, true) ::
StructField("rating", DoubleType, true) :: Nil)))
在完成所有这些步骤后,我终于可以使用ALS
算法,当事情如此冗长时,可能是因为你做错了。
val rec = (new ALS().setUserCol("user")
.setItemCol("product")
.setRatingCol("rating")
.setPredictionCol("value")
.setSeed(17)
.setMaxIter(20))
val model = rec.fit(dfs(0))
model.transform(dfs(1)).collect
Array([0,0,0.0,0.022231804], [1,1,0.0,0.102589644], [0,3,0.0,0.11560536])
答案 0 :(得分:1)
一些评论:
user
和rating
是userCol
和ratingCol
的默认参数。如果您将product
重命名为item
,也可以省略此项。您可以将Rank替换为Rating并稍后省略架构:
case (row, u) =>
row.toSeq.zipWithIndex.map{ case (r: Double, i: Int) => Rating(u, i, r) }
...
.toDF
id
似乎无关紧要,您可以使用zipWithUniqueId
uniqueId
可以接受,您可以将monotonically_increasing_id
与DataFrame
可以通过将数据包装为一个爆炸:
来避免将数据传递给RDDval exprs = explode(array(df.columns.map(c =>
struct(lit(c).alias("item"), col(c).alias("rating"))): _*
))
df
.withColumn("user", monotonically_increasing_id)
.withColumn("tmp", exprs)
.select($"user", $"tmp.item", $"tmp.rating")
并用ids替换名称。
尽管如此,我相信在这里使用DataFrames
并没有多大好处。将这种或另一种数据传递回MLlib
模型,该模型需要RDD[Rating]
。