J'essaie de convertir tous les en-têtes/noms de colonnes d'un DataFrame
en Spark-Scala. à partir de maintenant, je viens avec le code suivant qui ne remplace qu'un seul nom de colonne.
for( i <- 0 to origCols.length - 1) {
df.withColumnRenamed(
df.columns(i),
df.columns(i).toLowerCase
);
}
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 méthode 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)
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é à 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 métadonnées nullability
. Une autre possibilité est de renommer en lançant:
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)
Pour ceux d'entre vous intéressés par la version de PySpark (en fait, c'est la même chose dans Scala - voir le commentaire ci-dessous):
merchants_df_renamed = merchants_df.toDF(
'merchant_id', 'category', 'subcategory', 'merchant')
merchants_df_renamed.printSchema()
Résultat:
root
| - merchant_id: entier (nullable = true)
| - category: chaîne (nullable = true)
| - sous-catégorie: chaîne (nullable = true)
| - marchand: chaîne (nullable = true)
def aliasAllColumns(t: DataFrame, p: String = "", s: String = ""): DataFrame =
{
t.select( t.columns.map { c => t.col(c).as( p + c + s) } : _* )
}
Dans le cas contraire, cela ajoute un préfixe et un suffixe à chacun des noms de colonnes actuels. Cela peut être utile lorsque vous avez deux tables avec une ou plusieurs colonnes portant le même nom et que vous souhaitez les joindre tout en pouvant toujours distinguer les colonnes de la table résultante. Ce serait bien s'il y avait une façon similaire de faire cela avec du SQL "normal".