将数据框的第一行切成Array [String]

时间:2018-07-10 06:51:56

标签: arrays scala apache-spark dataframe

import org.apache.spark.sql.functions.broadcast
import org.apache.spark.sql.SparkSession._
import org.apache.spark.sql.SparkSession
import org.apache.spark.sql._
import org.apache.spark.sql.functions._
import org.apache.spark.SparkContext._
import org.apache.spark.{SparkConf,SparkContext}
import java.io.File
import org.apache.commons.io.FileUtils
import org.apache.hadoop.fs.FileSystem
import org.apache.hadoop.fs.Path
import org.apache.spark.sql.expressions.Window
import scala.runtime.ScalaRunTime.{array_apply, array_update}
import scala.collection.mutable.Map

object SimpleApp {
    def main(args: Array[String]){
    val conf = new SparkConf().setAppName("SimpleApp").setMaster("local")
    val sc = new SparkContext(conf)
    val input = "file:///home/shahid/Desktop/sample1.csv"
    val hdfsOutput = "hdfs://localhost:9001/output.csv"
    val localOutput = "file:///home/shahid/Desktop/output"
    val sqlContext = new SQLContext(sc)
    val df = sqlContext.read.format("com.databricks.spark.csv").load(input)
    var colLen = df.columns.length
    val df1 = df.filter(!(col("_c1") === ""))
  

我将第一行捕获到名为headerArr的val中。

    val headerArr = df1.head
  

我希望这个值是Array [String]。

    println("class = "+headerArr.getClass)
  

该如何将这个headerArr类型转换为Array [String]或直接将此顶部行转换为Array [String]。

    val fs = org.apache.hadoop.fs.FileSystem.get(new java.net.URI("hdfs://localhost:9001"), sc.hadoopConfiguration)
    fs.delete(new org.apache.hadoop.fs.Path("/output.csv"),true)
    df1.write.csv(hdfsOutput)
    val fileTemp = new File("/home/shahid/Desktop/output/")
    if (fileTemp.exists)
        FileUtils.deleteDirectory(fileTemp)
    df1.write.csv(localOutput)
    sc.stop()
    }
}
  

我也尝试使用df1.first,但是两者都返回相同的类型。

     

以上代码在控制台上的结果如下:-

class = class org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema
  

需要帮助。   谢谢您的时间。 xD

2 个答案:

答案 0 :(得分:1)

给出以下数据框:

val df = spark.createDataFrame(Seq(("a", "hello"), ("b", "world"))).toDF("id", "word")
df.show()

+---+-----+
| id| word|
+---+-----+
|  a|hello|
|  b|world|
+---+-----+

您可以像已经提到的那样获得第一行,然后将结果转换为Seq,该结果实际上由Array的子类型支持,然后您可以“广播”到数组无需复制:

// returns: WrappedArray(a, hello)
df.first.toSeq.asInstanceOf[Array[_]]

在具有很好静态类型的Scala语言中,铸造通常不是一个好习惯,因此除非您确实需要Seq,否则您可能希望坚持使用Array

请注意,到目前为止,由于Spark中的Row对象必须适应各种类型,因此,我们最终始终不是以字符串数组而是以对象数组结尾。如果要获取字符串集合,可以迭代字段并提取字符串:

// returns: Vector(a, hello)
for (i <- 0 until df.first.length) yield df.first.getString(i)

如果ClassCastException包含非字符串,这当然会导致Row。根据您的需求,您可能还需要考虑使用Try默默地将非字符串放入for理解范围内:

import scala.util.Try

// same return type as before
// non-string members will be filtered out of the end result
for {
  i <- 0 until df.first.length
  field <- Try(df.first.getString(i)).toOption
} yield field

直到现在我们返回了IndexedSeq,它适用于有效的随机访问(即,对集合中任何项目的访问时间都恒定),尤其是Vector。同样,您可能确实需要返回Array。要返回Array[String],您可能想调用toArray上的Vector,很遗憾,这会复制整个内容。

您可以跳过此步骤,通过显式使用Array[String]而不是依靠flatMap-理解并使用for来直接输出collection.breakOut

// returns: Array[String] -- silently keeping strings only
0.until(df.first.length).
  flatMap(i => Try(df.first.getString(i)).toOption)(collection.breakOut)

要了解有关构建器和collection.breakOut的更多信息,您可能需要阅读here

答案 1 :(得分:1)

好吧,我的问题没有用最佳方法解决,但我尝试了一条出路:-

    val headerArr = df1.first
    var headerArray = new Array[String](colLen)
    for(i <- 0 until colLen){
        headerArray(i)=headerArr(i).toString
    }
  

但是我仍然愿意提出新的建议。   尽管我将数据帧切成 class = org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema 的变量,然后通过迭代将元素传输到Array [String]。