Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 如何在不从数据帧转换并访问列的情况下将列添加到数据集?_Scala_Apache Spark - Fatal编程技术网

Scala 如何在不从数据帧转换并访问列的情况下将列添加到数据集?

Scala 如何在不从数据帧转换并访问列的情况下将列添加到数据集?,scala,apache-spark,Scala,Apache Spark,我知道使用.withColumn()和返回数据帧的UDF向Spark数据集添加新列的方法。我还知道,我们可以将结果数据帧转换为数据集 我的问题是: 如果我们仍然遵循传统的DF方法(即将列名作为字符串传递给UDF的输入),那么DataSet的类型安全性在这里是如何发挥作用的 是否有一种“面向对象的方式”来访问列(不以字符串形式传递列名),就像我们使用RDD那样,用于附加一个新列 如何在映射、筛选等正常操作中访问新列 例如: scala> case class Temp(a : Int

我知道使用
.withColumn()
和返回数据帧的
UDF向Spark数据集添加新列的方法。我还知道,我们可以将结果数据帧转换为数据集

我的问题是:

  • 如果我们仍然遵循传统的DF方法(即将列名作为字符串传递给UDF的输入),那么DataSet的类型安全性在这里是如何发挥作用的
  • 是否有一种“面向对象的方式”来访问列(不以字符串形式传递列名),就像我们使用RDD那样,用于附加一个新列
  • 如何在映射、筛选等正常操作中访问新列
  • 例如:

        scala> case class Temp(a : Int, b : String)    //creating case class
        scala> val df = Seq((1,"1str"),(2,"2str),(3,"3str")).toDS    // creating DS
        scala> val appendUDF = udf( (b : String) => b + "ing")      // sample UDF
    
        scala> df.withColumn("c",df("b"))   // adding a new column
        res5: org.apache.spark.sql.DataFrame = [a: int, b: string ... 1 more field]
    
        scala> res5.as[Temp]   // converting to DS
        res6: org.apache.spark.sql.Dataset[Temp] = [a: int, b: string ... 1 more field]
    
        scala> res6.map( x =>x.  
        // list of autosuggestion :
        a   canEqual   equals     productArity     productIterator   toString   
        b   copy       hashCode   productElement   productPrefix 
    
    我使用
    .withColumn()
    添加的新列
    c
    ,不可访问,因为列
    c
    不在case类
    Temp
    中(它只包含
    a
    b
    ),当它使用
    res5.as[Temp]
    转换为DS时


    如何访问列
    c

    数据集的类型安全世界中,您可以将一个结构映射到另一个结构

    也就是说,对于每个转换,我们都需要数据的模式表示(正如RDD所需要的那样)。要访问上面的“c”,我们需要创建一个新的模式来提供对它的访问

    case class A(a:String)
    case class BC(b:String, c:String)
    val f:A => BC = a=> BC(a.a,"c") // Transforms an A into a BC
    
    val data = (1 to 10).map(i => A(i.toString))
    val dsa = spark.createDataset(data)
    // dsa: org.apache.spark.sql.Dataset[A] = [a: string]
    
    val dsb = dsa.map(f)
    //dsb: org.apache.spark.sql.Dataset[BC] = [b: string, c: string]
    

    只是为了补充@maasg的优秀答案

    如果我们仍然遵循传统的DF方法(即将列名作为字符串传递给UDF的输入),那么DataSet的类型安全性在这里是如何发挥作用的

    让我用另一个问题来回答这个问题:“我们在‘我们仍在追随……’中是谁?”?如果你认为我是对的,我不同意,只有在我懒得创建case类来描述要使用的数据集时才使用DataFrames

    我对UDF的回答是远离UDF,除非它们非常简单,并且Spark Optimizer无法优化它们。是的,我确实相信UDF太容易定义和使用,以至于我自己太多次(过度)使用它们。Spark SQL 2.0中大约有239个可用函数,您可能很难想到一个没有UDF但没有标准函数的解决方案

    scala> spark.version
    res0: String = 2.1.0-SNAPSHOT
    
    scala> spark.catalog.listFunctions.count
    res1: Long = 240
    
    (240以上是因为我注册了一个自定义项)

    您应该始终使用标准函数,因为它们可以优化。Spark可以控制您的操作,从而优化您的查询

    您还应该使用数据集(而不是
    Dataset[Row]
    ,即
    DataFrame
    ),因为它们允许您访问字段的类型安全访问

    (然而,一些数据集“好东西”也无法优化,因为数据集编程都是关于Scala自定义代码的,Spark无法像基于数据帧的代码那样优化)

    是否有一种“面向对象的方式”来访问列(不以字符串形式传递列名),就像我们使用RDD那样,用于附加一个新列

    对。当然用例类定义数据集的模式并使用字段。访问和添加(这是@maasg很好的回应,所以我不想在这里重复他的话)

    如何在映射、筛选等正常操作中访问新列

    再简单一次。使用描述数据集(架构)的case类。如何向现有对象添加新的“某物”?除非它以某种方式接受了一个新的专栏,否则你不能,不是吗

    在访问列或追加新列的“面向对象方式”中,“如果列是case类的属性,则不能说“这是一个描述数据的类,同时说这是一个可能具有新属性的类”。在OOP/FP中是不可能的,是吗


    这就是为什么添加一个新列最终要使用另一个case类或使用
    with column
    。怎么了?我认为这是……简单地说……没什么错。

    有没有其他方法可以添加一个列而不传递字符串?@vdep
    String
    只是一个遵循问题脉络的示例。不,我的意思是我们可以在这里不传递列名
    b
    作为字符串:
    df.withColumn(“c”,df(“b”))
    @vdep您能想象一个用于此的API吗?如果你负责数据集的API,你会怎么做?我很好奇为什么你这么需要让它与众不同。@JacekLaskowski举个例子,如果我能像这样使用它:
    df.withColumn(“c”,df.b)
    而不是
    df.withColumn(“c”,df(“b”)
    ,我将能够在编译阶段捕捉到错误,比如拼写错误的列名或访问不存在的列等,而不是在编译阶段运行时的“column not found exception”(列未找到异常)。这只是我的拙见,不是主要的需求或要求。