Se a estrutura for plana:
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)
a coisa mais simples que você pode fazer é usar o toDF
método:
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)
Se você deseja renomear colunas individuais que você pode usar select
com alias
:
df.select($"_1".alias("x1"))
que pode ser facilmente generalizado para várias colunas:
val lookup = Map("_1" -> "foo", "_3" -> "bar")
df.select(df.columns.map(c => col(c).as(lookup.getOrElse(c, c))): _*)
ou withColumnRenamed
:
df.withColumnRenamed("_1", "x1")
que usa com foldLeft
para renomear várias colunas:
lookup.foldLeft(df)((acc, ca) => acc.withColumnRenamed(ca._1, ca._2))
Com estruturas aninhadas ( structs
), uma opção possível é renomear selecionando uma estrutura inteira:
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)
Observe que isso pode afetar os nullability
metadados. Outra possibilidade é renomear lançando:
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)
ou:
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)
: _*)
significa emdf.select(df.columns.map(c => col(c).as(lookup.getOrElse(c, c))): _*)
: _*
é o chamado operador "splat" da escala. Basicamente, ele explode algo parecido com um array em uma lista não contida, o que é útil quando você deseja passar o array para uma função que recebe um número arbitrário de args, mas não tem uma versão que leva aList[]
. Se você está familiarizado com Perl, essa é a diferença entresome_function(@my_array) # "splatted"
esome_function(\@my_array) # not splatted ... in perl the backslash "\" operator returns a reference to a thing
.df.select(df.columns.map(c => col(c).as(lookup.getOrElse(c, c))): _*)
.. Você poderia decompor, por favor? especialmente alookup.getOrElse(c,c)
parte.Para aqueles que estão interessados na versão PySpark (na verdade, é o mesmo no Scala - veja o comentário abaixo):
Resultado:
fonte
toDF()
para renomear colunas no DataFrame, deve-se ter cuidado. Este método funciona muito mais devagar do que outros. Tenho o DataFrame que contém 100 milhões de registros e uma consulta de contagem simples leva cerca de 3s, enquanto a mesma consulta com otoDF()
método leva cerca de 16s. Mas quando uso oselect col AS col_new
método para renomear, recebo ~ 3s novamente. Mais de 5 vezes mais rápido! Spark 2.3.2.3Caso não seja óbvio, isso adiciona um prefixo e um sufixo a cada um dos nomes das colunas atuais. Isso pode ser útil quando você tem duas tabelas com uma ou mais colunas com o mesmo nome e deseja juntá-las, mas ainda assim ser capaz de eliminar a ambigüidade das colunas na tabela resultante. Com certeza, seria bom se houvesse uma maneira semelhante de fazer isso no SQL "normal".
fonte
Suponha que o dataframe df tenha 3 colunas id1, nome1, preço1 e você deseja renomeá-las para id2, nome2, preço2
Achei essa abordagem útil em muitos casos.
fonte
junção da tabela de reboque não renomear a chave associada
trabalho!
fonte