如何为Spark RDD中的元素分配唯一的连续数字

时间:2014-05-29 17:19:26

标签: apache-spark apache-spark-mllib

我的数据集为(user, product, review),并希望将其提供给mllib的ALS算法。

该算法需要用户和产品为数字,而我的是String用户名和字符串SKU。

现在,我获得了不同的用户和SKU,然后在Spark之外为他们分配了数字ID。

我想知道是否有更好的方法可以做到这一点。我想到的一种方法是编写一个自定义RDD,基本上枚举1到n,然后在两个RDD上调用zip。

5 个答案:

答案 0 :(得分:41)

Spark 1.0 开始,您可以使用两种方法轻松解决此问题:

  • RDD.zipWithIndexSeq.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)]))

现在你可以:

  • 使用此LUT获取ALS友好的整数ID以提供给ALS
  • 当您需要从ALS ID返回原始ID时,使用此LUT进行反向查找

显然,为物品做同样的事。

答案 4 :(得分:2)

人们已经建议monotonically_increasing_id(),并提到它创造了Longs的问题,而不是Ints。

但是,根据我的经验(警告 - Spark 1.6) - 如果您在单个执行程序上使用它(之前重新分配为1),则不会使用执行程序前缀,并且可以将数字安全地转换为Int。显然,您需要少于Integer.MAX_VALUE行。