Apache spark 中的数据框示例 |斯卡拉

Posted

技术标签:

【中文标题】Apache spark 中的数据框示例 |斯卡拉【英文标题】:Dataframe sample in Apache spark | Scala 【发布时间】:2016-09-21 20:36:55 【问题描述】:

我正在尝试从两个数据帧中提取样本,其中我需要保持计数比率。例如

df1.count() = 10
df2.count() = 1000

noOfSamples = 10

我想以这样一种方式对数据进行采样,即我得到 10 个大小为 101 的样本(1 个来自 df1,100 个来自 df2)

现在这样做,

var newSample = df1.sample(true, df1.count() / noOfSamples)
println(newSample.count())

这里的分数意味着什么?它可以大于1吗?我检查了this 和this,但无法完全理解。

还有我们可以指定要采样的行数吗?

【问题讨论】:

【参考方案1】:

fraction 参数表示将返回的数据集的近似部分。例如,如果将其设置为0.1,将返回 10% (1/10) 的行。对于您的情况,我相信您想要执行以下操作:

val newSample = df1.sample(true, 1D*noOfSamples/df1.count)

但是,您可能会注意到 newSample.count 每次运行时都会返回不同的数字,这是因为 fraction 将是随机生成值的阈值(如您所见 here),因此生成的数据集大小可能会有所不同。解决方法可以是:

val newSample = df1.sample(true, 2D*noOfSamples/df1.count).limit(df1.count/noOfSamples)

一些可扩展性观察

您可能会注意到,执行df1.count 可能会很昂贵,因为它会评估整个DataFrame,并且您首先会失去采样的好处之一。

因此,根据您的应用程序的上下文,您可能希望使用已经已知个样本总数或一个近似值。

val newSample = df1.sample(true, 1D*noOfSamples/knownNoOfSamples)

或者假设您的 DataFrame 的大小很大,我仍然会使用 fraction 并使用 limit 来强制采样数。

val guessedFraction = 0.1
val newSample = df1.sample(true, guessedFraction).limit(noOfSamples)

至于你的问题:

可以大于 1 吗?

没有。它表示 0 到 1 之间的分数。如果将其设置为 1,它将带来 100% 的行,因此将其设置为大于 1 的数字是没有意义的。

还有我们可以指定要采样的行数吗?

您可以指定比您想要的行数更大的分数,然后使用限制,如我在第二个示例中所示。也许还有其他方法,但这是我使用的方法。

【讨论】:

虽然我更清楚分数的作用,但您建议的解决方案可能不适用于当前场景(参见编辑)。 df2.sample(true, noOfSample / df2.count).limit(df2.count/noOfSamples) 会给我 1% 的数据,即 10 行,这将少于我需要的 100 行。对此有什么想法吗? @hbabbar 所以你总是想要每个数据集的 10%?如果是这样,你可以这样做df2.sample(true, 0.15).limit(0.1*df2.count) 很好的解决方案!【参考方案2】:

也许你想试试下面的代码..

val splits = data.randomSplit(Array(0.7, 0.3))
val (trainingData, testData) = (splits(0), splits(1))

【讨论】:

【参考方案3】:

回答分数是否可以大于 1。是的,如果我们将替换为是,则可以。如果用 replace false 提供大于 1 的值,则会出现以下异常:

java.lang.IllegalArgumentException: requirement failed: Upper bound (2.0) must be <= 1.0.

【讨论】:

【参考方案4】:

我也发现计数功能缺乏样本令人不安。如果您对创建临时视图不挑剔,我发现下面的代码很有用(df 是您的数据框,count 是样本大小):

val tableName = s"table_to_sample_$System.currentTimeMillis"
df.createOrReplaceTempView(tableName)
val sampled = sqlContext.sql(s"select *, rand() as random from $tableName order by random limit $count")
sqlContext.dropTempTable(tableName)
sampled.drop("random")

只要您当前的行数与您的样本大小一样大,它就会返回一个准确的计数。

【讨论】:

如果我没记错的话,您可以使用 DataFrame API 执行完全相同的查询,无需创建临时视图并使用 SQL 查询。但是,这会触发排序操作,我相信它会比使用示例慢得多。【参考方案5】:

当需要准确的记录数时,我使用此函数进行随机抽样:

def row_count_sample (df, row_count, with_replacement=False, random_seed=113170):

    ratio = 1.08 * float(row_count) / df.count()  # random-sample more as dataframe.sample() is not a guaranteed to give exact record count
                                                  # it could be more or less actual number of records returned by df.sample()

    if ratio>1.0:
        ratio = 1.0

    result_df = (df
                    .sample(with_replacement, ratio, random_seed)
                    .limit(row_count)                                   # since we oversampled, make exact row count here
                )

    return result_df 

【讨论】:

【参考方案6】:

如果您想对数据框 df 的 70% 和 30% 进行随机拆分,则以下代码有效,

val Array(trainingDF, testDF) = df.randomSplit(Array(0.7, 0.3), seed = 12345)

【讨论】:

【参考方案7】:

为了回答您的问题,我们是否可以指定要采样的行数?

我最近需要从 spark 数据帧中采样一定数量的行。我遵循以下流程,

    将 spark 数据帧转换为 rdd。 示例:df_test.rdd

    RDD 有一个名为 takeSample 的功能,它允许您使用种子编号提供所需的样本数量。 示例:df_test.rdd.takeSample(withReplacement, Number of Samples, Seed)

    使用sqlContext.createDataFrame()将RDD转换回spark数据帧

以上流程合并为单步:

我需要从中采样的数据框(或人口)有大约 8,000 条记录: df_grp_1

df_grp_1
test1 = sqlContext.createDataFrame(df_grp_1.rdd.takeSample(False,125,seed=115))

test1 数据框将有 125 条采样记录。

【讨论】:

以上是关于Apache spark 中的数据框示例 |斯卡拉的主要内容,如果未能解决你的问题,请参考以下文章

在 spark 和 scala 中,如何将数据框转换或映射到特定列信息?

火花斯卡拉数据帧错误

Spark Mllib kmeans 示例,使用数据框而不是 textFile

在 spark 数据框中运行 UDF 时,不支持获取 org.apache.spark.sql.Column 类型的架构

Apache Spark:数据框中行值列表的百分比

在 Apache Spark 中为每行迭代添加范围变量