我的数据集为(user, product, review)
,并希望将其提供给mllib的ALS算法。
该算法需要用户和产品为数字,而我的是String用户名和字符串SKU。
现在,我获得了不同的用户和SKU,然后在Spark之外为他们分配了数字ID。
我想知道是否有更好的方法可以做到这一点。我想到的一种方法是编写一个自定义RDD,基本上枚举1到n
,然后在两个RDD上调用zip。
答案 0 :(得分:41)
从 Spark 1.0 开始,您可以使用两种方法轻松解决此问题:
RDD.zipWithIndex
与Seq.zipWithIndex
类似,它会添加连续的(Long
)个数字。这需要先计算每个分区中的元素,因此您的输入将被评估两次。如果要使用它,请缓存输入RDD。RDD.zipWithUniqueId
还为您提供了唯一的Long
ID,但不保证它们是连续的。 (如果每个分区具有相同数量的元素,它们将只是连续的。)好处是,这不需要知道有关输入的任何信息,因此不会导致双重评估。答案 1 :(得分:15)
对于类似的用例示例,我只是对字符串值进行了哈希处理。见http://blog.cloudera.com/blog/2014/03/why-apache-spark-is-a-crossover-hit-for-data-scientists/
def nnHash(tag: String) = tag.hashCode & 0x7FFFFF
var tagHashes = postIDTags.map(_._2).distinct.map(tag =>(nnHash(tag),tag))
虽然哈希可以更容易管理,但听起来你已经做过类似的事了。
Matei在这里提出了一种在RDD上模仿zipWithIndex
的方法,相当于在每个区域内分配全局唯一的ID:https://groups.google.com/forum/#!topic/spark-users/WxXvcn2gl1E
答案 2 :(得分:8)
另一个简单的选择,如果使用DataFrames而只关注唯一性,则使用函数MonotonicallyIncreasingID
import org.apache.spark.sql.functions.monotonicallyIncreasingId
val newDf = df.withColumn("uniqueIdColumn", monotonicallyIncreasingId)
修改:MonotonicallyIncreasingID
已弃用并已移除since Spark 2.0;它现在称为monotonically_increasing_id
。
答案 3 :(得分:2)
monotonically_increasing_id() 出现作为答案,但不幸的是,它不适用于ALS,因为它产生64位数字而ALS需要32位数字(请参阅下面的评论radek1st的答案)对于deets)。
我找到的解决方案是使用zipWithIndex(),如Darabos的回答中所述。以下是如何实现它:
如果您已经有一个名为userids
的不同用户的单列DataFrame,您可以按如下方式创建查找表(LUT):
# PySpark code
user_als_id_LUT = sqlContext.createDataFrame(userids.rdd.map(lambda x: x[0]).zipWithIndex(), StructType([StructField("userid", StringType(), True),StructField("user_als_id", IntegerType(), True)]))
现在你可以:
显然,为物品做同样的事。
答案 4 :(得分:2)
人们已经建议monotonically_increasing_id(),并提到它创造了Longs的问题,而不是Ints。
但是,根据我的经验(警告 - Spark 1.6) - 如果您在单个执行程序上使用它(之前重新分配为1),则不会使用执行程序前缀,并且可以将数字安全地转换为Int。显然,您需要少于Integer.MAX_VALUE行。