更改spark数据帧中列的可空属性

J *_*ath 21 scala apache-spark spark-dataframe

我正在为某些测试手动创建数据帧.创建它的代码是:

case class input(id:Long, var1:Int, var2:Int, var3:Double)
val inputDF = sqlCtx
  .createDataFrame(List(input(1110,0,1001,-10.00),
    input(1111,1,1001,10.00),
    input(1111,0,1002,10.00)))
Run Code Online (Sandbox Code Playgroud)

架构看起来像这样:

root
 |-- id: long (nullable = false)
 |-- var1: integer (nullable = false)
 |-- var2: integer (nullable = false)
 |-- var3: double (nullable = false)
Run Code Online (Sandbox Code Playgroud)

我想为这些变量中的每一个制作'nullable = true'.如何从一开始就声明它或在创建新数据帧后将其切换?

Mar*_*nne 39

回答

随着进口

import org.apache.spark.sql.types.{StructField, StructType}
import org.apache.spark.sql.{DataFrame, SQLContext}
import org.apache.spark.{SparkConf, SparkContext}
Run Code Online (Sandbox Code Playgroud)

您可以使用

/**
 * Set nullable property of column.
 * @param df source DataFrame
 * @param cn is the column name to change
 * @param nullable is the flag to set, such that the column is  either nullable or not
 */
def setNullableStateOfColumn( df: DataFrame, cn: String, nullable: Boolean) : DataFrame = {

  // get schema
  val schema = df.schema
  // modify [[StructField] with name `cn`
  val newSchema = StructType(schema.map {
    case StructField( c, t, _, m) if c.equals(cn) => StructField( c, t, nullable = nullable, m)
    case y: StructField => y
  })
  // apply new schema
  df.sqlContext.createDataFrame( df.rdd, newSchema )
}
Run Code Online (Sandbox Code Playgroud)

直.

您也可以通过"pimp my library"库模式使该方法可用(请参阅我的帖子,在DataFrame上定义自定义方法的最佳方法是什么?),这样您就可以调用

val df = ....
val df2 = df.setNullableStateOfColumn( "id", true )
Run Code Online (Sandbox Code Playgroud)

编辑

替代解决方案1

使用稍微修改过的版本 setNullableStateOfColumn

def setNullableStateForAllColumns( df: DataFrame, nullable: Boolean) : DataFrame = {
  // get schema
  val schema = df.schema
  // modify [[StructField] with name `cn`
  val newSchema = StructType(schema.map {
    case StructField( c, t, _, m) ? StructField( c, t, nullable = nullable, m)
  })
  // apply new schema
  df.sqlContext.createDataFrame( df.rdd, newSchema )
}
Run Code Online (Sandbox Code Playgroud)

替代解决方案2

明确定义架构.(使用反射创建更通用的解决方案)

configuredUnitTest("Stackoverflow.") { sparkContext =>

  case class Input(id:Long, var1:Int, var2:Int, var3:Double)

  val sqlContext = new SQLContext(sparkContext)
  import sqlContext.implicits._


  // use this to set the schema explicitly or
  // use refelection on the case class member to construct the schema
  val schema = StructType( Seq (
    StructField( "id", LongType, true),
    StructField( "var1", IntegerType, true),
    StructField( "var2", IntegerType, true),
    StructField( "var3", DoubleType, true)
  ))

  val is: List[Input] = List(
    Input(1110, 0, 1001,-10.00),
    Input(1111, 1, 1001, 10.00),
    Input(1111, 0, 1002, 10.00)
  )

  val rdd: RDD[Input] =  sparkContext.parallelize( is )
  val rowRDD: RDD[Row] = rdd.map( (i: Input) ? Row(i.id, i.var1, i.var2, i.var3))
  val inputDF = sqlContext.createDataFrame( rowRDD, schema ) 

  inputDF.printSchema
  inputDF.show()
}
Run Code Online (Sandbox Code Playgroud)


Sid*_*gal 15

这是一个迟到的答案,但希望为来到这里的人们提供替代解决方案.您可以DataFrame Column通过对代码进行以下修改,从一开始就自动构建一个可为空的:

case class input(id:Option[Long], var1:Option[Int], var2:Int, var3:Double)
val inputDF = sqlContext
  .createDataFrame(List(input(Some(1110),Some(0),1001,-10.00),
    input(Some(1111),Some(1),1001,10.00),
    input(Some(1111),Some(0),1002,10.00)))
inputDF.printSchema
Run Code Online (Sandbox Code Playgroud)

这将产生:

root
 |-- id: long (nullable = true)
 |-- var1: integer (nullable = true)
 |-- var2: integer (nullable = false)
 |-- var3: double (nullable = false)

defined class input
inputDF: org.apache.spark.sql.DataFrame = [id: bigint, var1: int, var2: int, var3: double]
Run Code Online (Sandbox Code Playgroud)

实质上,如果您将字段声明为Option使用Some([element])None作为实际输入,则该字段可以为空.否则,该字段将不可为空.我希望这有帮助!


mat*_*iek 8

更紧凑的版本设置所有列可为空的参数

而不是case StructField( c, t, _, m) ? StructField( c, t, nullable = nullable, m)一个可以使用_.copy(nullable = nullable).然后整个函数可以写成:

def setNullableStateForAllColumns( df: DataFrame, nullable: Boolean) : DataFrame = {
  df.sqlContext.createDataFrame(df.rdd, StructType(df.schema.map(_.copy(nullable = nullable))))
}
Run Code Online (Sandbox Code Playgroud)


Ray*_*Ral 5

另一个选择是,如果您需要就地更改数据帧,并且无法重新创建,则可以执行以下操作:

.withColumn("col_name", when(col("col_name").isNotNull, col("col_name")).otherwise(lit(null)))
Run Code Online (Sandbox Code Playgroud)

然后,Spark会认为此列可能包含null,并且可空性将设置为true。另外,您可以使用udf将值包装在中Option。即使在流媒体情况下也可以正常工作。

  • 知道如何在结构化流数据帧中实现逆(将列设置为不可为空)吗? (3认同)
  • 好的!PySpark 版本是 `.withColumn("col_name", when(col("col_name").isNotNull(), col("col_name")).otherwise(lit(None)))` (3认同)
  • 这应该是公认的答案 (2认同)