Как сгладить структуру в кадре данных Spark?

У меня есть датафрейм со следующей структурой:

 |-- data: struct (nullable = true)
 |    |-- id: long (nullable = true)
 |    |-- keyNote: struct (nullable = true)
 |    |    |-- key: string (nullable = true)
 |    |    |-- note: string (nullable = true)
 |    |-- details: map (nullable = true)
 |    |    |-- key: string
 |    |    |-- value: string (valueContainsNull = true)

Как можно сгладить структуру и создать новый фрейм данных:

     |-- id: long (nullable = true)
     |-- keyNote: struct (nullable = true)
     |    |-- key: string (nullable = true)
     |    |-- note: string (nullable = true)
     |-- details: map (nullable = true)
     |    |-- key: string
     |    |-- value: string (valueContainsNull = true)

Есть ли что-то вроде взрыва, но для структур?

 TobiStraub23 июл. 2019 г., 15:25
хорошее решение также представлено здесь:stackoverflow.com/questions/47285871/...
 erwaman10 янв. 2018 г., 16:37
Ответы наstackoverflow.com/questions/37471346/... были также полезны.

Ответы на вопрос(4)

Вот функция, которая делает то, что вы хотите, и может иметь дело с несколькими вложенными столбцами, содержащими столбцы с одинаковым именем:

def flatten_df(nested_df):
    flat_cols = [c[0] for c in nested_df.dtypes if c[1][:6] != 'struct']
    nested_cols = [c[0] for c in nested_df.dtypes if c[1][:6] == 'struct']

    flat_df = nested_df.select(flat_cols +
                               [F.col(nc+'.'+c).alias(nc+'_'+c)
                                for nc in nested_cols
                                for c in nested_df.select(nc+'.*').columns])
    return flat_df

До:

root
 |-- x: string (nullable = true)
 |-- y: string (nullable = true)
 |-- foo: struct (nullable = true)
 |    |-- a: float (nullable = true)
 |    |-- b: float (nullable = true)
 |    |-- c: integer (nullable = true)
 |-- bar: struct (nullable = true)
 |    |-- a: float (nullable = true)
 |    |-- b: float (nullable = true)
 |    |-- c: integer (nullable = true)

После:

root
 |-- x: string (nullable = true)
 |-- y: string (nullable = true)
 |-- foo_a: float (nullable = true)
 |-- foo_b: float (nullable = true)
 |-- foo_c: integer (nullable = true)
 |-- bar_a: float (nullable = true)
 |-- bar_b: float (nullable = true)
 |-- bar_c: integer (nullable = true)
 Vijay Anand Pandian12 сент. 2018 г., 15:08
У меня отлично работает.

Самый простой способ - использовать SQL, вы можете построить строку запроса SQL, чтобы псевдоним вложенного столбца был плоским.

Получить схему фрейма данных (df.schema())Преобразовать схему в SQL (для (поле:schema().fields()) ...

Запрос:

val newDF = sqlContext.sql("SELECT " + sqlGenerated + " FROM source")

Вотпример на Java.

(Я предпочитаю SQL, так что вы можете легко протестировать его на Spark-shell, и это на разных языках).

Я обобщил решение от stecos немного больше, чтобы выровнять можно более чем на два структурных слоя:

def flatten_df(nested_df, layers):
    flat_cols = []
    nested_cols = []
    flat_df = []

    flat_cols.append([c[0] for c in nested_df.dtypes if c[1][:6] != 'struct'])
    nested_cols.append([c[0] for c in nested_df.dtypes if c[1][:6] == 'struct'])

    flat_df.append(nested_df.select(flat_cols[0] +
                               [col(nc+'.'+c).alias(nc+'_'+c)
                                for nc in nested_cols[0]
                                for c in nested_df.select(nc+'.*').columns])
                  )
    for i in range(1, layers):
        print (flat_cols[i-1])
        flat_cols.append([c[0] for c in flat_df[i-1].dtypes if c[1][:6] != 'struct'])
        nested_cols.append([c[0] for c in flat_df[i-1].dtypes if c[1][:6] == 'struct'])

        flat_df.append(flat_df[i-1].select(flat_cols[i] +
                                [col(nc+'.'+c).alias(nc+'_'+c)
                                    for nc in nested_cols[i]
                                    for c in flat_df[i-1].select(nc+'.*').columns])
        )

    return flat_df[-1]

просто позвоните с:

my_flattened_df = flatten_df(my_df_having_nested_structs, 3)

(второй параметр - уровень выравниваемых слоев, в моем случае это 3)

Решение Вопроса

Это должно работать в Spark 1.6 или новее:

df.select(df.col("data.*"))

или же

df.select(df.col("data.id"), df.col("data.keyNote"), df.col("data.details"))
 user602234104 авг. 2016 г., 00:01
Я просто редактировал, но это странно. Я могу использовать *. Может быть, какая-то версия проблемы?
 djWann04 авг. 2016 г., 00:07
Да, может быть. Я использую spark 1.6.1 и scala 2.10
 Alessandro S.12 дек. 2018 г., 15:28
Первый предложенный метод не работает для меня
 SparkleGoat27 сент. 2017 г., 17:19
Как бы вы выбрали ключ или примечание под вложенной структурой keyNote?
 djWann04 авг. 2016 г., 00:00
но использование select для всех столбцов, таких как df.select (df.col1, df.col2, df.col3), работает, поэтому я приму этот ответ
 djWann03 авг. 2016 г., 23:54
Исключение в потоке "main" org.apache.spark.sql.AnalysisException: Нет такого структурного поля *

Ваш ответ на вопрос