web-dev-qa-db-fra.com

PySpark - renommer plus d'une colonne en utilisant withColumnRenamed

Je veux changer le nom de deux colonnes en utilisant la fonction spark withColumnRenamed. Bien sûr, je peux écrire:

data = sqlContext.createDataFrame([(1,2), (3,4)], ['x1', 'x2'])
data = (data
       .withColumnRenamed('x1','x3')
       .withColumnRenamed('x2', 'x4'))

mais je veux le faire en une étape (avoir la liste/le nuplet de nouveaux noms). Malheureusement, ni ceci:

data = data.withColumnRenamed(['x1', 'x2'], ['x3', 'x4'])

ni ceci:

data = data.withColumnRenamed(('x1', 'x2'), ('x3', 'x4'))

travaille. Est-il possible de faire cela de cette façon?

12
user2280549

Il n'est pas possible d'utiliser un seul appel withColumnRenamed

  • Vous pouvez utiliser DataFrame.toDF method *

    data.toDF('x3', 'x4')
    

    ou 

    new_names = ['x3', 'x4']
    data.toDF(*new_names)
    
  • Il est également possible de renommer avec la simple select:

    from pyspark.sql.functions import col
    
    mapping = dict(Zip(['x1', 'x2'], ['x3', 'x4']))
    data.select([col(c).alias(mapping.get(c, c)) for c in data.columns])
    

De même à Scala, vous pouvez:

  • Renommez toutes les colonnes:

    val newNames = Seq("x3", "x4")
    
    data.toDF(newNames: _*)
    
  • Renommez à partir de la correspondance avec select:

    val  mapping = Map("x1" -> "x3", "x2" -> "x4")
    
    df.select(
      df.columns.map(c => df(c).alias(mapping.get(c).getOrElse(c))): _*
    )
    

    ou foldLeft + withColumnRenamed 

    mapping.foldLeft(data){
      case (data, (oldName, newName)) => data.withColumnRenamed(oldName, newName) 
    }
    

* Ne pas confondre avec RDD.toDF qui n'est pas une fonction variadique et prend les noms de colonne comme une liste,

26
zero323

Je ne trouvais pas non plus de solution facile pour pyspark, alors venez de construire la mienne, semblable au df.rename(columns={'old_name_1':'new_name_1', 'old_name_2':'new_name_2'}) de pandas.

def rename_columns(df, columns):
    if isinstance(columns, dict):
        for old_name, new_name in columns.items():
            df = df.withColumnRenamed(old_name, new_name)
        return df
    else:
        raise ValueError("'columns' should be a dict, like {'old_name_1':'new_name_1', 'old_name_2':'new_name_2'}")

Votre solution ressemblera donc à data = rename_columns(data, {'x1': 'x3', 'x2': 'x4'})

Cela me permet d'économiser quelques lignes de code, j'espère que cela vous aidera aussi.

5
proggeo

pourquoi voulez-vous l'exécuter sur une seule ligne si vous imprimez le plan d'exécution, il est en réalité effectué sur une seule ligne

data = spark.createDataFrame([(1,2), (3,4)], ['x1', 'x2'])
data = (data
   .withColumnRenamed('x1','x3')
   .withColumnRenamed('x2', 'x4'))
data.explain()

SORTIE

== Physical Plan ==
*(1) Project [x1#1548L AS x3#1552L, x2#1549L AS x4#1555L]
+- Scan ExistingRDD[x1#1548L,x2#1549L]

si vous voulez le faire avec un tuple de listevous pouvez utiliser une simple fonction de carte

data = spark.createDataFrame([(1,2), (3,4)], ['x1', 'x2'])
new_names = [("x1","x3"),("x2","x4")]
data = data.select(list(
       map(lambda old,new:F.col(old).alias(new),*Zip(*new_names))
       ))

data.explain()

a toujours le même plan 

SORTIE

== Physical Plan ==
*(1) Project [x1#1650L AS x3#1654L, x2#1651L AS x4#1655L]
+- Scan ExistingRDD[x1#1650L,x2#1651L]
1
Tushar Kolhe