Si la structure est plate:
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)
la chose la plus simple que vous puissiez faire est d'utiliser la toDF
méthode:
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)
Si vous souhaitez renommer des colonnes individuelles, vous pouvez utiliser soit select
avec alias
:
df.select($"_1".alias("x1"))
qui peut être facilement généralisée à plusieurs colonnes:
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")
qui utilisent avec foldLeft
pour renommer plusieurs colonnes:
lookup.foldLeft(df)((acc, ca) => acc.withColumnRenamed(ca._1, ca._2))
Avec les structures imbriquées ( structs
), une option possible est de renommer en sélectionnant une structure entière:
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)
Notez que cela peut affecter les nullability
métadonnées. Une autre possibilité est de renommer en castant:
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)
: _*)
signifie dansdf.select(df.columns.map(c => col(c).as(lookup.getOrElse(c, c))): _*)
: _*
est l'opérateur scala dit "splat". Il explose fondamentalement un élément de type tableau en une liste non contenue, ce qui est utile lorsque vous voulez passer le tableau à une fonction qui prend un nombre arbitraire d'arguments, mais n'a pas de version qui prend unList[]
. Si vous êtes familier avec Perl, c'est la différence entresome_function(@my_array) # "splatted"
etsome_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))): _*)
. Pourriez-vous la décomposer s'il vous plaît? surtout lalookup.getOrElse(c,c)
partie.Pour ceux d'entre vous intéressés par la version PySpark (en fait c'est la même chose dans Scala - voir le commentaire ci-dessous):
Résultat:
la source
toDF()
pour renommer les colonnes dans DataFrame doit être prudent. Cette méthode fonctionne beaucoup plus lentement que d'autres. J'ai DataFrame contient 100 millions d'enregistrements et une requête de comptage simple prend ~ 3s, alors que la même requête avec latoDF()
méthode prend ~ 16s. Mais lorsque j'utilise uneselect col AS col_new
méthode pour renommer, j'obtiens à nouveau ~ 3s. Plus de 5 fois plus vite! Spark 2.3.2.3Si ce n'est pas évident, cela ajoute un préfixe et un suffixe à chacun des noms de colonne actuels. Cela peut être utile lorsque vous avez deux tables avec une ou plusieurs colonnes ayant le même nom et que vous souhaitez les joindre tout en étant capable de lever l'ambiguïté des colonnes dans la table résultante. Ce serait bien s'il y avait une manière similaire de faire cela en SQL "normal".
la source
Supposons que le dataframe df ait 3 colonnes id1, name1, price1 et que vous souhaitiez les renommer en id2, name2, price2
J'ai trouvé cette approche utile dans de nombreux cas.
la source
la jointure de table de remorquage ne renomme pas la clé jointe
travaux!
la source