Перейменування назв стовпців DataFrame у Spark Scala


93

Я намагаюся перетворити всі заголовки / імена стовпців a DataFrameу Spark-Scala. на даний момент я придумав наступний код, який замінює лише одне ім'я стовпця.

for( i <- 0 to origCols.length - 1) {
  df.withColumnRenamed(
    df.columns(i), 
    df.columns(i).toLowerCase
  );
}

Відповіді:


239

Якщо структура рівна:

val df = Seq((1L, "a", "foo", 3.0)).toDF
df.printSchema
// root
//  |-- _1: long (nullable = false)
//  |-- _2: string (nullable = true)
//  |-- _3: string (nullable = true)
//  |-- _4: double (nullable = false)

найпростіше, що ви можете зробити, це скористатися toDFметодом:

val newNames = Seq("id", "x1", "x2", "x3")
val dfRenamed = df.toDF(newNames: _*)

dfRenamed.printSchema
// root
// |-- id: long (nullable = false)
// |-- x1: string (nullable = true)
// |-- x2: string (nullable = true)
// |-- x3: double (nullable = false)

Якщо ви хочете перейменувати окремі стовпці, ви можете використовувати будь-який selectіз alias:

df.select($"_1".alias("x1"))

які можна легко узагальнити на кілька стовпців:

val lookup = Map("_1" -> "foo", "_3" -> "bar")

df.select(df.columns.map(c => col(c).as(lookup.getOrElse(c, c))): _*)

або withColumnRenamed:

df.withColumnRenamed("_1", "x1")

які використовують, foldLeftщоб перейменовувати кілька стовпців:

lookup.foldLeft(df)((acc, ca) => acc.withColumnRenamed(ca._1, ca._2))

У вкладених структурах ( structs) одним із можливих варіантів є перейменування, вибравши цілу структуру:

val nested = spark.read.json(sc.parallelize(Seq(
    """{"foobar": {"foo": {"bar": {"first": 1.0, "second": 2.0}}}, "id": 1}"""
)))

nested.printSchema
// root
//  |-- foobar: struct (nullable = true)
//  |    |-- foo: struct (nullable = true)
//  |    |    |-- bar: struct (nullable = true)
//  |    |    |    |-- first: double (nullable = true)
//  |    |    |    |-- second: double (nullable = true)
//  |-- id: long (nullable = true)

@transient val foobarRenamed = struct(
  struct(
    struct(
      $"foobar.foo.bar.first".as("x"), $"foobar.foo.bar.first".as("y")
    ).alias("point")
  ).alias("location")
).alias("record")

nested.select(foobarRenamed, $"id").printSchema
// root
//  |-- record: struct (nullable = false)
//  |    |-- location: struct (nullable = false)
//  |    |    |-- point: struct (nullable = false)
//  |    |    |    |-- x: double (nullable = true)
//  |    |    |    |-- y: double (nullable = true)
//  |-- id: long (nullable = true)

Зверніть увагу, що це може вплинути на nullabilityметадані. Інша можливість - перейменувати за допомогою кастингу:

nested.select($"foobar".cast(
  "struct<location:struct<point:struct<x:double,y:double>>>"
).alias("record")).printSchema

// root
//  |-- record: struct (nullable = true)
//  |    |-- location: struct (nullable = true)
//  |    |    |-- point: struct (nullable = true)
//  |    |    |    |-- x: double (nullable = true)
//  |    |    |    |-- y: double (nullable = true)

або:

import org.apache.spark.sql.types._

nested.select($"foobar".cast(
  StructType(Seq(
    StructField("location", StructType(Seq(
      StructField("point", StructType(Seq(
        StructField("x", DoubleType), StructField("y", DoubleType)))))))))
).alias("record")).printSchema

// root
//  |-- record: struct (nullable = true)
//  |    |-- location: struct (nullable = true)
//  |    |    |-- point: struct (nullable = true)
//  |    |    |    |-- x: double (nullable = true)
//  |    |    |    |-- y: double (nullable = true)

Привіт @ zero323 При використанні withColumnRenamed я отримую AnalysisException не може вирішити 'CC8. 1 'дані введені стовпці ... Це не вдається, хоча CC8.1 доступний у DataFrame, будь ласка, керівництво.
unk1102

@ u449355 Для мене незрозуміло, чи є це вкладеним стовпцем або стовпцем, що містить крапки. У пізнішому випадку зворотні посилання повинні працювати (принаймні в деяких основних випадках).
zero323

1
що : _*)означає вdf.select(df.columns.map(c => col(c).as(lookup.getOrElse(c, c))): _*)
Антон Кім

1
Щоб відповісти на запитання Антона Кіма: чи : _*є так званий оператор шкали "splat"? В основному він вибудовує подібну до масиву річ у невміщений список, що корисно, коли ви хочете передати масив функції, яка приймає довільну кількість аргументів, але не має версії, яка приймає List[]. Якщо ви взагалі знайомі з Perl, це різниця між some_function(@my_array) # "splatted"і some_function(\@my_array) # not splatted ... in perl the backslash "\" operator returns a reference to a thing.
Майло Стоун

1
Це твердження для мене справді незрозуміле df.select(df.columns.map(c => col(c).as(lookup.getOrElse(c, c))): _*).. Не могли б ви його розкласти? особливо lookup.getOrElse(c,c)частина.
Етос,

19

Для тих, хто цікавиться версією PySpark (насправді це те саме в Scala - див. Коментар нижче):

    merchants_df_renamed = merchants_df.toDF(
        'merchant_id', 'category', 'subcategory', 'merchant')

    merchants_df_renamed.printSchema()

Результат:

корінь
| - ідентифікатор продавця: ціле число (з нульовим значенням = істина)
| - категорія: рядок (з нульовим значенням = істина)
| - підкатегорія: рядок (з нульовим значенням = істинне значення)
| - торговець: рядок (з нульовим значенням = істинне значення)


1
З використанням toDF()для перейменування стовпців у DataFrame потрібно бути обережним. Цей метод працює набагато повільніше, ніж інші. У мене DataFrame містить 100 мільйонів записів і простий запит підрахунку займає ~ 3 секунди, тоді як той самий запит із toDF()методом займає ~ 16 секунд. Але при використанні select col AS col_newметоду для перейменування я знову отримую ~ 3 с. Понад 5 разів швидше! Іскра 2.3.2.3
Ігор Коноваленко

6
def aliasAllColumns(t: DataFrame, p: String = "", s: String = ""): DataFrame =
{
  t.select( t.columns.map { c => t.col(c).as( p + c + s) } : _* )
}

Якщо це не очевидно, це додає префікс та суфікс до кожного з поточних імен стовпців. Це може бути корисно, якщо у вас є дві таблиці з одним або кількома стовпцями з однаковим ім'ям, і ви хочете приєднатись до них, але все одно зможете роз'єднати стовпці в результуючій таблиці. Звичайно, було б непогано, якби подібний спосіб це зробити в "звичайному" SQL.


сподобається напевно, приємно та елегантно
bluephantom

1

Припустимо, фрейм даних df має 3 стовпці id1, name1, price1, і ви хочете перейменувати їх на id2, name2, price2

val list = List("id2", "name2", "price2")
import spark.implicits._
val df2 = df.toDF(list:_*)
df2.columns.foreach(println)

Я знайшов такий підхід корисним у багатьох випадках.


0

приєднання таблиці буксирування не перейменовує приєднаний ключ

// method 1: create a new DF
day1 = day1.toDF(day1.columns.map(x => if (x.equals(key)) x else s"${x}_d1"): _*)

// method 2: use withColumnRenamed
for ((x, y) <- day1.columns.filter(!_.equals(key)).map(x => (x, s"${x}_d1"))) {
    day1 = day1.withColumnRenamed(x, y)
}

працює!


0
Sometime we have the column name is below format in SQLServer or MySQL table

Ex  : Account Number,customer number

But Hive tables do not support column name containing spaces, so please use below solution to rename your old column names.

Solution:

val renamedColumns = df.columns.map(c => df(c).as(c.replaceAll(" ", "_").toLowerCase()))
df = df.select(renamedColumns: _*)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.