【问题标题】:Iterate Over a Dataframe as each time column is passing to do transformation在每次列传递以进行转换时迭代数据框
【发布时间】:2020-10-31 05:04:24
【问题描述】:

我有一个包含 100 列和 col 名称的数据框,例如 col1、col2、col3.... 我想根据条件匹配对列的值应用某些转换。我可以将列名存储在字符串数组中。并在 withColumn 中传递数组的每个元素的值,并根据何时条件,我可以垂直转换列的值。 但问题是,由于 Dataframe 是不可变的,因此每个更新的版本都需要存储在一个新变量中,并且新的 dataframe 还需要传入 withColumn 以进行下一次迭代的转换。 有什么方法可以创建数据帧数组,以便可以将新数据帧存储为数组元素,并且可以根据迭代器的值进行迭代。 或者有没有其他方法可以处理。

var arr_df : Array[DataFrame] = new Array[DataFrame](60)   

--> 这会抛出错误“未找到类型 DataFrame”

val df(0) = df1.union(df2)

for(i <- 1 to 99){
  val df(i) = df(i-1).withColumn(col(i), when(col(i)> 0, col(i) + 
   1).otherwise(col(i)))

这里 col(i) 是一个字符串数组,用于存储原始 datframe 的列名。

举个例子:


scala> val original_df = Seq((1,2,3,4),(2,3,4,5),(3,4,5,6),(4,5,6,7),(5,6,7,8),(6,7,8,9)).toDF("col1","col2","col3","col4")
original_df: org.apache.spark.sql.DataFrame = [col1: int, col2: int ... 2 more fields]

scala> original_df.show()

+----+----+----+----+
|col1|col2|col3|col4|
+----+----+----+----+
|   1|   2|   3|   4|
|   2|   3|   4|   5|
|   3|   4|   5|   6|
|   4|   5|   6|   7|
|   5|   6|   7|   8|
|   6|   7|   8|   9|
+----+----+----+----+

我想迭代3列:col1,col2,col3,如果该列的值大于3,那么它将更新+1

【问题讨论】:

  • 嗨,欢迎来到 SO。作为社区标准,您能否发布示例输入、您尝试过的内容、预期输出以及您遇到的问题?
  • @Raghu,如果您想让我更具体一些,请告诉我。我不能分享确切的代码,但如果需要,我会尝试举个例子
  • 是的,样本输入和预期输出会有所帮助。还可以尝试在编辑时使用格式选项 -{ 格式化您的代码。这提高了可读性
  • @Raghu ,希望这可以帮助您更具体地理解问题。
  • 如果能添加样本数据就好了,什么样的转换和预期的输出..

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


【解决方案1】:

检查下面的代码。

scala> df.show(false)
+----+----+----+----+
|col1|col2|col3|col4|
+----+----+----+----+
|1   |2   |3   |4   |
|2   |3   |4   |5   |
|3   |4   |5   |6   |
|4   |5   |6   |7   |
|5   |6   |7   |8   |
|6   |7   |8   |9   |
+----+----+----+----+
scala>  val requiredColumns = df.columns.zipWithIndex.filter(_._2 < 3).map(_._1).toSet
requiredColumns: scala.collection.immutable.Set[String] = Set(col1, col2, col3)
scala> val allColumns = df.columns
allColumns: Array[String] = Array(col1, col2, col3, col4)
scala> val columnExpr = allColumns.filterNot(requiredColumns(_)).map(col(_)) ++ requiredColumns.map(c => when(col(c) > 3, col(c) + 1).otherwise(col(c)).as(c))
scala> df.select(columnExpr:_*).show(false)
+----+----+----+----+
|col1|col2|col3|col4|
+----+----+----+----+
|1   |2   |3   |4   |
|2   |3   |5   |5   |
|3   |5   |6   |6   |
|5   |6   |7   |7   |
|6   |7   |8   |8   |
|7   |8   |9   |9   |
+----+----+----+----+

【讨论】:

  • 非常有帮助。现在在 100 列中需要在 Seq() 中提及 50 列,其余 50 列没有任何条件。我需要找到出路。
  • 是的,它们的顺序是 col1, col2, col3..... col30 到 col79 我想做转换。
  • 我在选择 API 的最终语句中遇到错误。如果您知道原因,请告诉我:错误:使用替代方法选择重载方法值:[U1](c1: org.apache.spark.sql.TypedColumn[org.apache.spark.sql.Row,UI]) org .apache.spark.sql.Dataset[UI] (col: String, cols:String*)org.apache.spark.DataFrame (cols*: org.apache.spark.sql.Column*)org .apache.spark.sql.DataFrame 不能应用于(对象)
  • 对不起,这实际上是不可能的。 :(
  • 非常感谢。你拯救了我的一天。它不是作为 map(col()) 工作的(对于固定列来说,值是不明确的)但它作为 map(lit()) 工作。如果你知道,请告诉我“col”和“lit”有什么区别
【解决方案2】:

如果我理解正确,您正在尝试执行数据帧明智的操作。你不需要为此迭代。我可以向您展示如何在 pyspark 中完成。也许它可以在scala中接管。

from pyspark.sql import functions as F
tst= sqlContext.createDataFrame([(1,7,0),(1,8,4),(1,0,10),(5,1,90),(7,6,0),(0,3,11)],schema=['col1','col2','col3'])
expr = [F.when(F.col(coln)>3,F.col(coln)+1).otherwise(F.col(coln)).alias(coln) for coln in tst.columns if 'col3' not in coln]
tst1= tst.select(*expr)

结果:

tst1.show()
+----+----+
|col1|col2|
+----+----+
|   1|   8|
|   1|   9|
|   1|   0|
|   6|   1|
|   8|   7|
|   0|   3|
+----+----+

这应该会给你想要的结果

【讨论】:

  • 我不想遍历所有列。例如,在 100 列中,我只迭代了 50 列。
  • 您有需要的列作为列表吗?那么您可以在列表理解中使用该列表而不是 tst.columns
【解决方案3】:

您可以遍历所有列并在单行中应用条件,如下所示,

original_df.select(original_df.columns.map(c => (when(col(c) > lit(3), col(c)+1).otherwise(col(c))).alias(c)):_*).show()


+----+----+----+----+
|col1|col2|col3|col4|
+----+----+----+----+
|   1|   2|   3|   5|
|   2|   3|   5|   6|
|   3|   5|   6|   7|
|   5|   6|   7|   8|
|   6|   7|   8|   9|
|   7|   8|   9|  10|
+----+----+----+----+

【讨论】:

  • @Ayan Chatterjee,检查一下,它甚至更时尚、更快速。
【解决方案4】:

您可以随时使用foldLeft 对多个columns 进行更改,如下所示

val original_df = Seq(
  (1,2,3,4),
  (2,3,4,5),
  (3,4,5,6),
  (4,5,6,7),
  (5,6,7,8),
  (6,7,8,9)
).toDF("col1","col2","col3","col4")

//Filter the columns that yuou want to update
val columns = original_df.columns

columns.foldLeft(original_df){(acc, colName) =>
  acc.withColumn(colName, when(col(colName) > 3, col(colName) + 1).otherwise(col(colName)))
}
.show(false)

输出:

+----+----+----+----+
|col1|col2|col3|col4|
+----+----+----+----+
|1   |2   |3   |5   |
|2   |3   |5   |6   |
|3   |5   |6   |7   |
|5   |6   |7   |8   |
|6   |7   |8   |9   |
|7   |8   |9   |10  |
+----+----+----+----+

【讨论】:

    猜你喜欢
    • 2021-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-13
    • 2016-07-07
    • 2021-10-21
    • 2023-01-16
    • 2018-06-06
    相关资源
    最近更新 更多