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 类型的架构