Spark ScalaでのDataFrameの列名の変更


93

DataFrameSpark-Scalaのすべてのヘッダー/列名を変換しようとしています。現在のところ、単一の列名のみを置き換える次のコードが考えられます。

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

回答:


237

構造が平らな場合:

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)で可能なオプションの1つは、構造全体を選択して名前を変更することです。

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メタデータに影響を与える可能性があることに注意してください。もう1つの可能性は、キャストして名前を変更することです。

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 2017年

@ u449355これがネストされた列なのか、ドットを含む列なのかは、私にはわかりません。後者の場合、バックティックは機能します(少なくともいくつかの基本的なケースでは)。
zero323

1
: _*)意味は何df.select(df.columns.map(c => col(c).as(lookup.getOrElse(c, c))): _*)
アントン・キム

1
キムアントンの質問に答えるに: _*は、これはいわゆる「splat」演算子であるscalaです。基本的には、配列のようなものを含まれていないリストに分解します。これは、任意の数の引数をとる関数に配列を渡したいが、をとるバージョンがない場合に便利ですList[]。あなたはPerlでおなじみのすべてであれば、それは間の差であるsome_function(@my_array) # "splatted"some_function(\@my_array) # not splatted ... in perl the backslash "\" operator returns a reference to a thing
Mylo Stone、

1
この文は私には本当にあいまいですdf.select(df.columns.map(c => col(c).as(lookup.getOrElse(c, c))): _*)...分解していただけませんか?特にそのlookup.getOrElse(c,c)部分。
Aetos 2018

18

PySparkバージョン(実際にはScalaでも同じです-下記のコメントを参照してください)に興味がある人のために:

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

    merchants_df_renamed.printSchema()

結果:

root
|-merchant_id:integer(nullable = true)
|-category:string(nullable = true)
|-subcategory:string(nullable = true)
|-merchant:string(nullable = true)


1
使用してtoDF()データフレームの列の名前を変更するために注意しなければなりません。この方法は、他の方法よりもかなり遅くなります。DataFrameに100Mレコードが含まれていて、それを超える単純なカウントクエリには最大3秒かかりますが、toDF()メソッドを使用した同じクエリには最大16秒かかります。しかし、select col AS col_new名前の変更にメソッドを使用すると、再び〜3秒になります。5倍以上速く!Spark 2.3.2.3
Ihor Konovalenko

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

明らかでない場合は、現在の各列名に接頭辞と接尾辞が追加されます。これは、同じ名前の列が1つ以上ある2つのテーブルがあり、それらを結合したいが、結果のテーブルの列を明確にすることができる場合に役立ちます。「通常の」SQLでこれを行うための同様の方法があれば、きっといいでしょう。


確かに、素敵でエレガント
thebluephantom

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)
}

動作します!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.