Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/17.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 有没有办法为Spark数据帧添加额外的元数据?_Scala_Apache Spark_Apache Spark Sql - Fatal编程技术网

Scala 有没有办法为Spark数据帧添加额外的元数据?

Scala 有没有办法为Spark数据帧添加额外的元数据?,scala,apache-spark,apache-spark-sql,Scala,Apache Spark,Apache Spark Sql,是否可以向数据帧添加额外的元数据 理由 我有SparkDataFrames,我需要为其保留额外信息。示例:DataFrame,我想“记住”整数id列中使用的最高索引 当前解决方案 我使用一个单独的数据帧来存储此信息。当然,将这些信息分开保存是乏味且容易出错的 是否有更好的解决方案将此类额外信息存储在数据帧上?我会在您的数据帧周围存储一个包装器。例如: case class MyDFWrapper(dataFrame: DataFrame, metadata: Map[String, Long])

是否可以向数据帧添加额外的元数据

理由 我有Spark
DataFrame
s,我需要为其保留额外信息。示例:
DataFrame
,我想“记住”整数id列中使用的最高索引

当前解决方案 我使用一个单独的
数据帧
来存储此信息。当然,将这些信息分开保存是乏味且容易出错的


是否有更好的解决方案将此类额外信息存储在
数据帧上?

我会在您的数据帧周围存储一个包装器。例如:

case class MyDFWrapper(dataFrame: DataFrame, metadata: Map[String, Long])
val maxIndex = df1.agg("index" ->"MAX").head.getLong(0)
MyDFWrapper(df1, Map("maxIndex" -> maxIndex))

如果您想减少繁琐的工作,我认为您可以在DataFrame和自定义包装器之间添加隐式转换(尽管尚未测试)

如果隐式包装器在DataFrame的作用域中,您可以像使用包装器一样使用普通DataFrame,即:

df.addtoMetaData("size", 100)

这种方式还可以使元数据可变,因此不应强迫您只计算一次并随身携带。

从Spark 1.2开始,StructType模式有一个
metadata
属性,它可以为数据帧中的每一列保存任意的信息映射/字典。例如(与单独的库一起使用时):

这是在中添加的,并设计用于机器学习管道,以跟踪存储在列中的功能的信息,如分类/连续、数字类别、类别到索引映射。请参阅设计文档

我希望看到它被更广泛地使用,例如,用于列的描述和文档、列中使用的测量单位、坐标轴信息等

问题包括如何在转换列时适当地保留或操作元数据信息,如何处理多种元数据,如何使所有元数据都可扩展,等等

为了让那些想在Spark数据帧中扩展此功能的人受益,我参考了一些关于Pandas的类似讨论

例如,请参见哪个支持标记数组的元数据

请参阅上的熊猫元数据讨论


另请参见与单元相关的讨论:

要扩展和缩放nealmcb的答案(问题标记为Scala,而不是python,因此我认为这个答案不会偏离主题或多余),假设您有一个数据帧:

import org.apache.spark.sql
val df = sc.parallelize(Seq.fill(100) { scala.util.Random.nextInt() }).toDF("randInt")
val randIntMax = df.rdd.map { case sql.Row(randInt: Int) => randInt }.reduce(math.max)
还有一些方法可以在数据帧上获得最大值或任何您想要记忆的内容:

import org.apache.spark.sql
val df = sc.parallelize(Seq.fill(100) { scala.util.Random.nextInt() }).toDF("randInt")
val randIntMax = df.rdd.map { case sql.Row(randInt: Int) => randInt }.reduce(math.max)
sql.types.Metadata
只能保存字符串、布尔值、某些类型的数字和其他元数据结构。因此,我们必须使用长时间:

val metadata = new sql.types.MetadataBuilder().putLong("columnMax", randIntMax).build()
DataFrame.withColumn()实际上有一个重载,允许在末尾提供元数据参数,但它被莫名其妙地标记为[private],所以我们只做它所做的事情-使用
Column.as(别名,元数据)

dfWithMax
现在有了(一列)您想要的元数据

dfWithMax.schema.foreach(field => println(s"${field.name}: metadata=${field.metadata}"))
> randInt: metadata={}
> randInt_withMax: metadata={"columnMax":2094414111}
或以编程方式并安全地键入(排序;Metadata.getLong()和其他不返回选项,并可能引发“找不到键”异常):


在您的情况下,将max附加到列是有意义的,但是在将元数据附加到数据帧而不是特定的列的一般情况下,似乎您必须采用其他答案所描述的包装路径。

很多人看到了“元数据”这个词,直接转到了“列元数据”。这似乎不是你想要的,也不是我遇到类似问题时想要的。最终,这里的问题是,数据帧是一种不可变的数据结构,每当对其执行操作时,数据都会传递,但数据帧的其余部分不会。这意味着您不能简单地在其上放一个包装器,因为一旦您执行一个操作,您就会得到一个全新的数据帧(可能是一种全新的类型,特别是Scala/Spark倾向于隐式转换)。最后,如果数据帧逃逸了包装器,就无法从数据帧重建元数据

我在Spark Streaming中遇到了这个问题,它关注RDD(数据帧的底层数据结构),并得出了一个简单的结论:存储元数据的唯一位置是RDD的名称。除了报告之外,core Spark系统从不使用RDD名称,因此重新调整其用途是安全的。然后,您可以基于RDD名称创建包装,在任何数据帧和包装之间进行显式转换,并完成元数据

不幸的是,这仍然会给您带来不变性的问题,并且每个操作都会创建新的RDD。RDD名称(我们的元数据字段)随每个新RDD一起丢失。这意味着您需要一种将名称重新添加到新RDD的方法。这可以通过提供一个将函数作为参数的方法来解决。它可以在函数之前提取元数据,调用函数并获取新的RDD/DataFrame,然后用元数据命名:

def withMetadata(fn: (df: DataFrame) => DataFrame): MetaDataFrame = {
  val meta = df.rdd.name
  val result = fn(wrappedFrame)
  result.rdd.setName(meta)
  MetaDataFrame(result)
}

包装类(MetaDataFrame)可以提供解析和设置元数据值的方便方法,以及Spark DataFrame和MetaDataFrame之间的隐式转换。只要您通过withMetadata方法运行所有的突变,您的元数据将贯穿整个转换管道。对每个调用使用此方法都有点麻烦,是的,但简单的现实是Spark中没有一流的元数据概念。

是否可以向目标数据帧添加一个额外的列?总的来说,我对每个数据帧存储大约1-10个额外的值感兴趣。即使可以将这些信息存储在其他列中,我仍然担心内存使用情况。(不确定,
Column(…).lit(…)
在这种情况下的行为。)是否需要持久化元数据,或者可以轻松地重新计算元数据?需要持久化元数据。啊哈-Spark似乎有
dfWithMax.schema("randInt_withMax").metadata.getLong("columnMax")
> res29: Long = 209341992
def withMetadata(fn: (df: DataFrame) => DataFrame): MetaDataFrame = {
  val meta = df.rdd.name
  val result = fn(wrappedFrame)
  result.rdd.setName(meta)
  MetaDataFrame(result)
}