如何将Spark数据帧的“第一”行复制到另一个数据帧?为什么我的最小示例失败了?

时间:2019-09-09 07:08:02

标签: scala apache-spark pyspark apache-spark-sql

基本问题:

我想将Spark数据帧**的“第一行”复制到另一个Spark数据帧sdf

我不明白下面的代码出了什么问题。 因此,我期待着一个解决方案和一个解释,在我的最小示例中失败了。

一个最小的示例:

sdfEmpty

// create a spark data frame import org.apache.spark.sql._ val sdf = Seq( (1, "a"), (12, "b"), (234, "b") ).toDF("A", "B") sdf.show() +---+---+ | A| B| +---+---+ | 1| a| | 2| b| | 3| b| +---+---+ // create an empty spark data frame to store the row // declare it as var, such that I can change it later var sdfEmpty = spark.createDataFrame(sc.emptyRDD[Row], sdf.schema) sdfEmpty.show() +---+---+ | A| B| +---+---+ +---+---+ // take the "first" row of sdf as a spark data frame val row = sdf.limit(1) // combine the two spark data frames sdfEmpty = sdfEmpty.union(row) 为:

row

row.show() +---+---+ | A| B| +---+---+ | 1| a| +---+---+ 的预期结果是:

sdfEmpty

但是我得到了:

+---+---+
|  A|  B|
+---+---+
|  1|  a|
+---+---+

问题: 我感到困惑的是:使用val row = sdf.limit(1)我以为我创建了一个永久性/不可更改/定义明确的对象。这样,当我打印一次并将其添加到某物时,我得到的结果相同。

备注:(非常感谢Daniel的评论)

我知道在scala的分布式世界中,没有明确定义的“第一行”概念。我把它放在这里是为了简单起见,我希望那些挣扎于类似事物的人们会“偶然地”使用“第一”一词。

我尝试实现的目标如下:(在一个简化的示例中) 我有一个带有2列A和B列的数据框。A列部分排序,B列完全排序。 我想过滤数据列。因此,这种想法是一种分而治之:将数据帧分割成两列,然后将它们完全按照顺序进行过滤,然后再进行过滤。 (并进行明显的迭代)

要实现这一点,我需要选择一个定义明确的行,并将日期拆分为w.r.t。行但是,如最小的示例所示,我的命令没有产生定义明确的对象。

非常感谢

3 个答案:

答案 0 :(得分:1)

火花是分布式的,因此“第一”的概念不是我们可以依靠的。根据分区的不同,调用limitfirst时会得到不同的结果。

要获得一致的结果,您的数据必须具有我们可以使用的基本顺序-这很有意义,因为除非对您的数据进行逻辑排序,否则我们无法真正说出采用第一行。

假设要相对于A列采用第一行,只需运行orderBy("A").first()(*)即可。尽管如果A列包含多个具有相同最小值的行,则无法保证将获得哪一行。

(**我假设scala API与Python的命名相同,所以如果它们的名称不同,请更正我的意思)

答案 1 :(得分:0)

  

@Christian,您可以通过使用take函数来实现此结果。   take(num)取RDD的前num个元素。它的工作方式是先扫描一个分区,然后使用该分区的结果来估算满足限制所需的其他分区的数量。
  这里是代码段。

scala> import org.apache.spark.sql.types._

scala> val sdf = Seq(
 (1, "a"),
 (12, "b"),
 (234, "b")
).toDF("A", "B")

scala> import org.apache.spark.sql._

scala> var sdfEmpty = spark.createDataFrame(sc.emptyRDD[Row], sdf.schema)

scala> var first1  =sdf.rdd.take(1)

scala> val first_row = spark.createDataFrame(sc.parallelize(first1), sdf.schema)

scala> sdfEmpty.union(first_row).show
+---+---+
|  A|  B|
+---+---+
|  1|  a|
+---+---+

有关take()和first()函数的更多信息,请阅读spark Documentation。如果您有与此相关的任何查询,请告诉我。

答案 2 :(得分:0)

我正在发布此答案,因为它包含丹尼尔(Daniel)建议的解决方案。一旦我浏览了通过mahesh gupta提供的文献或进行了更多测试,我将更新此答案并评论“现实生活”中不同方法的运行时。

基本问题:

我想将Spark数据帧@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = inflater.inflate(R.layout.activity_bottom_fragment, container, false); return root; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); BottomNavigationView navView = root.findViewById(R.id.nav_view); AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder( R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications) .build(); NavController navController = Navigation.findNavController(activity, R.id.nav_host_fragment);//error here NavigationUI.setupActionBarWithNavController((AppCompatActivity) activity, navController, appBarConfiguration); NavigationUI.setupWithNavController(navView, navController); } 的“第一行”复制到另一个Spark数据帧sdf

就像在火花的分布式世界中一样,首先没有一个明确的概念,但是由于sdfEmpty可以实现类似的目的。

一个最小的工作示例:

orderBy

// create a spark data frame import org.apache.spark.sql._ val sdf = Seq( (1, "a"), (12, "b"), (234, "b") ).toDF("A", "B") sdf.show() +---+---+ | A| B| +---+---+ | 1| a| | 2| b| | 3| b| +---+---+ // create an empty spark data frame to store the row // declare it as var, such that I can change it later var sdfEmpty = spark.createDataFrame(sc.emptyRDD[Row], sdf.schema) sdfEmpty.show() +---+---+ | A| B| +---+---+ +---+---+ // take the "first" row of sdf as a spark data frame val row = sdf.limit(1).collect() // combine the two spark data frames sdfEmpty = sdfEmpty.union(row) 是:

row

**,row.show() +---+---+ | A| B| +---+---+ | 1| a| +---+---+ 的结果是:**

sdfEmpty

备注:Daniel给出的解释(请参见上面的评论)+---+---+ | A| B| +---+---+ | 1| a| +---+---+ 是一种转换-在运行诸如show或collect之类的操作之前,不会对其进行评估。因此,根据上下文,它可以返回不同的值。要始终使用.limit(n)的结果,可以将.limit的结果用于驱动程序并将其用作局部变量。