我正在阅读许多图像,我想在它们的一小部分图像上进行开发。因此,我试图了解spark和python如何实现这一目标:
In [1]: d = sqlContext.read.parquet('foo')
In [2]: d.map(lambda x: x.photo_id).first()
Out[2]: u'28605'
In [3]: d.limit(1).map(lambda x: x.photo_id)
Out[3]: PythonRDD[31] at RDD at PythonRDD.scala:43
In [4]: d.limit(1).map(lambda x: x.photo_id).first()
// still running...
..那么发生了什么?我希望limit()的运行速度比[2]
的速度快得多,但情况并非如此 * 。
下面我将描述我的理解,请纠正我,因为显然我错过了一些东西:
d
是对的RDD(我从模式中知道)我说
使用地图功能:
i)取每一对(名为x
并返回photo_id
属性)。
ii)这将产生一个新的(匿名)RDD,我们正在应用first()
方法,我不知道它是如何工作的 $ ,但是应该给我匿名RDD的第一个元素。
在[3]
中,我们将d
RDD限制为1,这意味着尽管d
已Out [3]
许多元素,只使用1并将map函数应用于那个
仅元素。 [4]
应该是映射创建的RDD。
[3]
中,我希望遵循limit()
的逻辑,只打印有限RDD的唯一元素...... 正如预期的那样,在查看显示器后,[4]似乎处理整个数据集,而其他数据集不是,所以我似乎没有正确使用tiny_d = d.limit(1).map(lambda x: x.photo_id)
tiny_d.map(lambda x: x.photo_id).first()
,或者那不是我要找的东西:
编辑:
PipelinedRDD
第一个会提供<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApplication1" mc:Ignorable="d" Title="MainWindow" Height="500" Width="983.334">
<Button x:Name="Button1" Content="Database" HorizontalAlignment="Left"
Margin="10,427,0,0" VerticalAlignment="Top" Width="99" Click="Button1_Click"/>
</Window>
,如here所述,它实际上不会执行任何操作,只是转换。
但是,第二行也将处理整个数据集(事实上,现在的任务数量与以前一样多,加上一个!)。
* [2]立即执行,而[4]仍在运行且&gt; 3h已经过去了..
$ 由于名称,我在文档中找不到它。
答案 0 :(得分:4)
根据您的代码,这里是Spark 2.0上更简单的测试用例
case class my (x: Int)
val rdd = sc.parallelize(0.until(10000), 1000).map { x => my(x) }
val df1 = spark.createDataFrame(rdd)
val df2 = df1.limit(1)
df1.map { r => r.getAs[Int](0) }.first
df2.map { r => r.getAs[Int](0) }.first // Much slower than the previous line
实际上,Dataset.first相当于Dataset.limit(1).collect,所以检查这两种情况的实际计划:
scala> df1.map { r => r.getAs[Int](0) }.limit(1).explain
== Physical Plan ==
CollectLimit 1
+- *SerializeFromObject [input[0, int, true] AS value#124]
+- *MapElements <function1>, obj#123: int
+- *DeserializeToObject createexternalrow(x#74, StructField(x,IntegerType,false)), obj#122: org.apache.spark.sql.Row
+- Scan ExistingRDD[x#74]
scala> df2.map { r => r.getAs[Int](0) }.limit(1).explain
== Physical Plan ==
CollectLimit 1
+- *SerializeFromObject [input[0, int, true] AS value#131]
+- *MapElements <function1>, obj#130: int
+- *DeserializeToObject createexternalrow(x#74, StructField(x,IntegerType,false)), obj#129: org.apache.spark.sql.Row
+- *GlobalLimit 1
+- Exchange SinglePartition
+- *LocalLimit 1
+- Scan ExistingRDD[x#74]
对于第一种情况,它与CollectLimitExec物理运算符中的优化有关。也就是说,它将首先获取第一个分区以获得限制行数,在这种情况下为1,如果不满足,则获取更多分区,直到达到所需的限制。因此,通常,如果第一个分区不为空,则仅计算并获取第一个分区。甚至不会计算其他分区。
但是,在第二种情况下,CollectLimitExec中的优化没有帮助,因为先前的限制操作涉及随机操作。将计算所有分区,并在每个分区上运行LocalLimit(1)以获得1行,然后将所有分区混洗到单个分区中。 CollectLimitExec将从生成的单个分区中获取1行。