Измените свойство nullable столбца в строке искровых данных
Я вручную создаю 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)))
Итак, схема выглядит так:
root
|-- id: long (nullable = false)
|-- var1: integer (nullable = false)
|-- var2: integer (nullable = false)
|-- var3: double (nullable = false)
Я хочу сделать 'nullable = true' для каждой из этих переменных. Как объявить это с самого начала или переключить его в новый фреймворк после его создания?
Ответы
Ответ 1
Ответ
При импорте
import org.apache.spark.sql.types.{StructField, StructType}
import org.apache.spark.sql.{DataFrame, SQLContext}
import org.apache.spark.{SparkConf, SparkContext}
вы можете использовать
/**
* 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 )
}
непосредственно.
Также вы можете сделать этот метод доступным через шаблон библиотеки "pimp my library" (см. мой SO post Каков наилучший способ определения пользовательских методов в DataFrame?), чтобы вы могли вызвать
val df = ....
val df2 = df.setNullableStateOfColumn( "id", true )
Изменить
Альтернативное решение 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 )
}
Альтернативное решение 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()
}
Ответ 2
Это поздний ответ, но он хотел дать альтернативное решение для людей, которые приходят сюда. Вы можете автоматически сделать 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
Это даст:
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]
По существу, если вы объявляете поле как Option
, используя Some([element])
или None
в качестве фактических входов, тогда это поле должно быть нулевым. В противном случае поле не будет равно NULL. Надеюсь, это поможет!
Ответ 3
Более компактная версия установки всех столбцов параметр с нулевым значением
Вместо 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))))
}
Ответ 4
Другой вариант, если вам нужно изменить фрейм данных на месте, а воссоздание невозможно, вы можете сделать что-то вроде этого:
.withColumn("col_name", when(col("col_name").isNotNull, col("col_name")).otherwise(lit(null)))
Затем Spark подумает, что этот столбец может содержать null
, а значение nullability будет равно true
.
Кроме того, вы можете использовать udf
, чтобы обернуть ваши значения в Option
.
Прекрасно работает даже для потоковых случаев.
Ответ 5
Просто используйте java.lang.Integer вместо scala.Int в вашем классе case.
case class input(id:Long, var1:java.lang.Integer , var2:java.lang.Integer , var3:java.lang.Double)
Ответ 6
Спасибо Мартин Сенн. Просто небольшое дополнение. В случае внутренних структурных типов, вам может понадобиться установить nullable рекурсивно, например так:
def setNullableStateForAllColumns(df: DataFrame, nullable: Boolean): DataFrame = {
def set(st: StructType): StructType = {
StructType(st.map {
case StructField(name, dataType, _, metadata) =>
val newDataType = dataType match {
case t: StructType => set(t)
case _ => dataType
}
StructField(name, newDataType, nullable = nullable, metadata)
})
}
df.sqlContext.createDataFrame(df.rdd, set(df.schema))
}