先将总体的单位按某种特征分为若干次级总体(层),然后再从每一层内进行单纯随机抽样,组成一个样本的统计学计算方法叫做分层抽样。在spark.mllib中,用key来分层。
与存在于spark.mllib中的其它统计函数不同,分层采样方法sampleByKey和sampleByKeyExact可以在key-value对的RDD上执行。在分层采样中,可以认为key是一个标签,value是特定的属性。例如,key可以是男人或者女人或者文档id,它相应的value可能是一组年龄或者是文档中的词。sampleByKey方法通过掷硬币的方式决定是否采样一个观察数据,
因此它需要我们传递(pass over)数据并且提供期望的数据大小(size)。sampleByKeyExact比每层使用sampleByKey随机抽样需要更多的有意义的资源,但是它能使样本大小的准确性达到了99.99%。
sampleByKeyExact()允许用户准确抽取f_k * n_k个样本,
这里f_k表示期望获取键为k的样本的比例,n_k表示键为k的键值对的数量。下面是一个使用的例子:
1 | import org.apache.spark.SparkContext |
当withReplacement为true时,采用PoissonSampler取样器,当withReplacement为false使,采用BernoulliSampler取样器。
1 | def sampleByKey(withReplacement: Boolean, |
下面我们分别来看sampleByKey和sampleByKeyExact的实现。
sampleByKey的实现
当我们需要不重复抽样时,我们需要用泊松抽样器来抽样。当需要重复抽样时,用伯努利抽样器抽样。sampleByKey的实现比较简单,它就是统一的随机抽样。
泊松抽样器
我们首先看泊松抽样器的实现。
1 | def getPoissonSamplingFunction[K: ClassTag, V: ClassTag](rdd: RDD[(K, V)], |
getPoissonSamplingFunction返回的是一个函数,传递给mapPartitionsWithIndex处理每个分区的数据。这里RandomDataGenerator是一个随机生成器,它用于同时生成均匀值(uniform values)和泊松值(Poisson values)。
伯努利抽样器
1 | def getBernoulliSamplingFunction[K, V](rdd: RDD[(K, V)], |
sampleByKeyExact的实现
sampleByKeyExact获取更准确的抽样结果,它的实现也分为两种情况,重复抽样和不重复抽样。前者使用泊松抽样器,后者使用伯努利抽样器。
泊松抽样器
1 | val counts = Some(rdd.countByKey()) |
伯努利抽样
1 | def getBernoulliSamplingFunction[K, V](rdd: RDD[(K, V)], |