Фрейм данных Spark возвращает значение NULL для всей строки, когда одно значение столбца этой строки равно NULL

#apache-spark #pyspark #apache-spark-sql #azure-databricks

Вопрос:

Входные данные —

 {"driverId":1,"driverRef":"hamilton","number":44,"code":"HAM","name":{"forename":"Lewis","surname":"Hamilton"},"dob":"1985-01-07","nationality":"British","url":"http://en.wikipedia.org/wiki/Lewis_Hamilton"}
{"driverId":2,"driverRef":"heidfeld","number":"\N","code":"HEI","name":{"forename":"Nick","surname":"Heidfeld"},"dob":"1977-05-10","nationality":"German","url":"http://en.wikipedia.org/wiki/Nick_Heidfeld"}
{"driverId":3,"driverRef":"rosberg","number":6,"code":"ROS","name":{"forename":"Nico","surname":"Rosberg"},"dob":"1985-06-27","nationality":"German","url":"http://en.wikipedia.org/wiki/Nico_Rosberg"}
{"driverId":4,"driverRef":"alonso","number":14,"code":"ALO","name":{"forename":"Fernando","surname":"Alonso"},"dob":"1981-07-29","nationality":"Spanish","url":"http://en.wikipedia.org/wiki/Fernando_Alonso"}
{"driverId":5,"driverRef":"kovalainen","number":"\N","code":"KOV","name":{"forename":"Heikki","surname":"Kovalainen"},"dob":"1981-10-19","nationality":"Finnish","url":"http://en.wikipedia.org/wiki/Heikki_Kovalainen"}
{"driverId":6,"driverRef":"nakajima","number":"\N","code":"NAK","name":{"forename":"Kazuki","surname":"Nakajima"},"dob":"1985-01-11","nationality":"Japanese","url":"http://en.wikipedia.org/wiki/Kazuki_Nakajima"}
{"driverId":7,"driverRef":"bourdais","number":"\N","code":"BOU","name":{"forename":"Sébastien","surname":"Bourdais"},"dob":"1979-02-28","nationality":"French","url":"http://en.wikipedia.org/wiki/Sébastien_Bourdais"}
 

После считывания этих данных в фрейм данных spark при отображении этого df я смог увидеть, что вся строка для идентификатора драйвера 2,5,6,7 равна НУЛЮ. Я мог видеть, что значение номера столбца равно НУЛЮ для этого идентификатора драйвера.

Вот мой код. Здесь есть какие-нибудь ошибки?

 from pyspark.sql.types import StructType, StructField, IntegerType, StringType, DateType

name_field = StructType(fields =[
  StructField("forename", StringType(), True),
  StructField("surname", StringType(), True)
])

driver_schema = StructType(fields =[
  StructField("driverId", IntegerType(), False),
  StructField("driverRef", StringType(), True),
  StructField("number", IntegerType(), True),
  StructField("code", StringType(), True),
  StructField("name", name_field),
  StructField("dob", DateType(), True),
  StructField("nationality", StringType(),True),
  StructField("url", StringType(), True)
])
 
driver_df = spark.read
.schema(driver_schema)
.json('dbfs:/mnt/databrickslearnf1azure/raw/drivers.json')

driver_df.printSchema()
root
 |-- driverId: integer (nullable = true)
 |-- driverRef: string (nullable = true)
 |-- number: integer (nullable = true)
 |-- code: string (nullable = true)
 |-- name: struct (nullable = true)
 |    |-- forename: string (nullable = true)
 |    |-- surname: string (nullable = true)
 |-- dob: date (nullable = true)
 |-- nationality: string (nullable = true)
 |-- url: string (nullable = true)

display(driver_df)
 

введите описание изображения здесь

Комментарии:

1. medium.com/swlh/…

2. вы должны изменить тип данных числового столбца на строку в явной схеме, которую вы создаете, потому что то, что вы называете null, на самом деле не является null, это какое-то строковое значение, и spark не может проанализировать его как целое число, поэтому он показывает все столбцы как null. если вы хотите, чтобы тип данных был целочисленным, вы можете преобразовать его в целое число после его прочтения.

Ответ №1:

Вы можете изменить свою начальную схему следующим образом, предполагая, что число имеет тип string.

 from pyspark.sql.types import StructType, StructField, IntegerType, StringType, DateType

name_field = StructType(fields =[
  StructField("forename", StringType(), True),
  StructField("surname", StringType(), True)
])

driver_schema = StructType(fields =[
  StructField("driverId", IntegerType(), False),
  StructField("driverRef", StringType(), True),
  StructField("number", StringType(), True),
  StructField("code", StringType(), True),
  StructField("name", name_field),
  StructField("dob", DateType(), True),
  StructField("nationality", StringType(),True),
  StructField("url", StringType(), True)
])
 

затем вы можете прочитать данные из файла json, используя тот же код, который вы используете, следующим образом:

 driver_df = spark.read
.schema(driver_schema)
.json('dbfs:/mnt/databrickslearnf1azure/raw/drivers.json')

driver_df.printSchema()
 

После того, как вы прочитали данные, вы можете применить логику для преобразования «N» в null, а затем изменить тип данных столбца со строки на целое число, как показано ниже :

 from pyspark.sql.functions import *
df = driver_df.withColumn("number", when(driver_df.number=="\N","null").otherwise(driver_df.number))
finaldf = df.withColumn("number",df.number.cast(IntegerType()))
finaldf.printSchema()
 

Теперь, если вы выполняете отображение или показ в кадре данных, вы можете увидеть вывод, как показано ниже :

введите описание изображения здесь

Ответ №2:

Вы видите это, потому что, согласно официальным документам databricks: Причина

Spark 3.0 и выше (Databricks Runtime 7.3 LTS и выше) не может анализировать массивы JSON как структуры. Вы должны передать схему как тип массива, а не как тип структуры.

Решение: Передайте схему как тип массива, а не как тип структуры.

 driver_schema = ArrayType(StructType(fields =[
  StructField("driverId", IntegerType(), False),
  StructField("driverRef", StringType(), True),
  StructField("number", IntegerType(), True),
  StructField("code", StringType(), True),
  StructField("name", name_field),
  StructField("dob", DateType(), True),
  StructField("nationality", StringType(),True),
  StructField("url", StringType(), True)
]))