Spark Scala에서 DataFrame의 열 이름 이름 바꾸기


93

DataFrameSpark-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 2017-06-09

1
무엇을 않는 : _*)에서 의미df.select(df.columns.map(c => col(c).as(lookup.getOrElse(c, c))): _*)
안톤 김

1
Anton Kim의 질문에 답하기 위해 : _*"splat"연산자라고하는 scala가 있습니다. 기본적으로 배열과 같은 것을 포함되지 않은 목록으로 분해합니다. 이는 임의의 수의 인수를 사용하지만 .NET Framework를 사용하는 버전이없는 함수에 배열을 전달하려는 경우에 유용합니다 List[]. 당신이 펄 모든 친숙한에 있다면, 그것은 사이의 차이 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

19

PySpark 버전에 관심이있는 분들을 위해 (실제로 Scala에서도 동일합니다-아래 주석 참조) :

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

    merchants_df_renamed.printSchema()

결과:

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


1
toDF()DataFrame에서 열 이름을 바꾸는 데 사용 하는 경우주의해야합니다. 이 방법은 다른 방법보다 훨씬 느리게 작동합니다. DataFrame에는 100M 레코드와 간단한 카운트 쿼리가 ~ 3 초가 걸리는 반면 toDF()메서드가 있는 동일한 쿼리 는 ~ 16 초가 걸립니다. 그러나 select col AS col_new이름을 바꾸는 방법을 사용 하면 ~ 3s가 다시 나타납니다. 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) } : _* )
}

명확하지 않은 경우 현재 열 이름 각각에 접두사와 접미사를 추가합니다. 이것은 동일한 이름을 가진 하나 이상의 열이있는 두 개의 테이블이 있고 이들을 조인하고 싶지만 결과 테이블의 열을 명확하게 할 수있는 경우에 유용 할 수 있습니다. "일반적인"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)
}

공장!


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: _*)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.