Scala Spark中的分层抽样

Scala Spark中的分层抽样,scala,apache-spark,Scala,Apache Spark,我有一个包含用户和购买数据的数据集。下面是一个示例,其中第一个元素是userId,第二个元素是productId,第三个元素表示boolean (2147481832,23355149,1) (2147481832,973010692,1) (2147481832,2134870842,1) (2147481832,541023347,1) (2147481832,1682206630,1) (2147481832,1138211459,1) (2147481832,852202566,1) (

我有一个包含用户和购买数据的数据集。下面是一个示例,其中第一个元素是userId,第二个元素是productId,第三个元素表示boolean

(2147481832,23355149,1)
(2147481832,973010692,1)
(2147481832,2134870842,1)
(2147481832,541023347,1)
(2147481832,1682206630,1)
(2147481832,1138211459,1)
(2147481832,852202566,1)
(2147481832,201375938,1)
(2147481832,486538879,1)
(2147481832,919187908,1)
... 
我想确保我只获取每个用户80%的数据并构建一个RDD,而其余20%的数据则构建另一个RDD。让我们打电话训练和测试。首先,我不想使用groupBy,因为它会造成内存问题,因为数据集很大。最好的方法是什么

我可以这样做,但这不会给每个用户的80%

val percentData = data.map(x => ((math.random * 100).toInt, x._1. x._2, x._3)
val train = percentData.filter(x => x._1 < 80).values.repartition(10).cache()
val percentData=data.map(x=>((math.random*100).toInt,x.。_1.x._2,x._3)
val train=percentData.filter(x=>x._1<80).values.repartition(10).cache()

类似这样的问题可能非常适合“Blink DB”这样的问题,但让我们看看这个问题。有两种方法可以解释您所问的问题,一种是:

1) 您需要80%的用户,并且需要他们的所有数据。 2) 您需要每个用户80%的数据

对于#1,您可以进行映射以获取用户ID,调用distinct,然后对其中80%进行采样(您可能希望查看
MLUtils
BernoulliCellSampler
中的
kFold
)。然后,您可以将输入数据过滤到所需的ID集


对于#2,您可以查看
伯努利采样器
并直接应用它。

霍尔顿的答案中有一个可能的解决方案,下面是一些其他解决方案:

使用RDD:

您可以使用类中的sampleByKeyExact转换

sampleByKeyExact(带替换的布尔值,scala.collection.Map分数,长种子) 返回此RDD的子集,该RDD按键采样(通过分层采样),精确包含每个层(具有相同键的对组)的math.ceil(numItems*samplingRate)

我会这样做:

考虑到下列清单:

val seq = Seq(
                (2147481832,23355149,1),(2147481832,973010692,1),(2147481832,2134870842,1),(2147481832,541023347,1),
                (2147481832,1682206630,1),(2147481832,1138211459,1),(2147481832,852202566,1),(2147481832,201375938,1),
                (2147481832,486538879,1),(2147481832,919187908,1),(214748183,919187908,1),(214748183,91187908,1)
           )
我将创建一个
RDD
对,将所有用户映射为密钥:

val data = sc.parallelize(seq).map(x => (x._1,(x._2,x._3)))
然后我将为每个键设置
分数,如下所示,因为
sampleByKeyExact
为每个键获取分数贴图:

val fractions = data.map(_._1).distinct.map(x => (x,0.8)).collectAsMap
我在这里所做的是在键上映射以找到不同的键,然后将每个键关联到一个等于
0.8
的分数。我收集了整张地图

要立即取样:

import org.apache.spark.rdd.PairRDDFunctions
val sampleData = data.sampleByKeyExact(false, fractions, 2L)

您可以检查密钥、数据或数据样本的计数:

scala > data.count
// [...]
// res10: Long = 12

scala > sampleData.count
// [...]
// res11: Long = 10
scala > df.count
// [...]
// res9: Long = 12

scala > sampleDataDF.count
// [...]
// res10: Long = 10
使用数据帧:

让我们从前一节考虑相同的数据(<代码> SEQ)。< /P>

val df = seq.toDF("keyColumn","value1","value2")
df.show
// +----------+----------+------+
// | keyColumn|    value1|value2|
// +----------+----------+------+
// |2147481832|  23355149|     1|
// |2147481832| 973010692|     1|
// |2147481832|2134870842|     1|
// |2147481832| 541023347|     1|
// |2147481832|1682206630|     1|
// |2147481832|1138211459|     1|
// |2147481832| 852202566|     1|
// |2147481832| 201375938|     1|
// |2147481832| 486538879|     1|
// |2147481832| 919187908|     1|
// | 214748183| 919187908|     1|
// | 214748183|  91187908|     1|
// +----------+----------+------+
我们需要底层的
RDD
来完成这项工作,通过将键定义为第一列,我们可以在此
RDD
中创建元素的元组:

val data: RDD[(Int, Row)] = df.rdd.keyBy(_.getInt(0))
val fractions: Map[Int, Double] = data.map(_._1)
                                      .distinct
                                      .map(x => (x, 0.8))
                                      .collectAsMap

val sampleData: RDD[Row] = data.sampleByKeyExact(withReplacement = false, fractions, 2L)
                               .values

val sampleDataDF: DataFrame = spark.createDataFrame(sampleData, df.schema) // you can use sqlContext.createDataFrame(...) instead for spark 1.6)
现在,您可以检查钥匙或
df
或数据样本的计数:

scala > data.count
// [...]
// res10: Long = 12

scala > sampleData.count
// [...]
// res11: Long = 10
scala > df.count
// [...]
// res9: Long = 12

scala > sampleDataDF.count
// [...]
// res10: Long = 10
由于Spark 1.5.0您可以使用
DataFrameStatFunctions.sampleBy
方法:

df.stat.sampleBy("keyColumn", fractions, seed)

这很好,我能做减法得到20%吗?或者你推荐一个更好的解决方案吗?一旦你有了新的rdd,你可以用它做任何事情。我看到rdd上有示例方法,但没有SampleByKeyExactor。你使用的是什么版本的spark?它实际上是一个
“org.apache.spark”%%“spark core”%%“1.4.1”
“org.apache.spark”%%“spark mllib”%%“1.4.1”