PySpark - переименовать более одного столбца, используя withColumnRenamed
Я хочу изменить имена двух столбцов, используя искру с функцией ColumnRenamed. Конечно, я могу написать:
data = sqlContext.createDataFrame([(1,2), (3,4)], ['x1', 'x2'])
data = (data
.withColumnRenamed('x1','x3')
.withColumnRenamed('x2', 'x4'))
но я хочу сделать это за один шаг (имея список/кортеж новых имен). К сожалению, ни это:
data = data.withColumnRenamed(['x1', 'x2'], ['x3', 'x4'])
и это:
data = data.withColumnRenamed(('x1', 'x2'), ('x3', 'x4'))
работает. Можно ли так сделать?
Ответы
Ответ 1
Невозможно использовать один вызов с withColumnRenamed
.
-
Вы можете использовать метод DataFrame.toDF
*
data.toDF('x3', 'x4')
или же
new_names = ['x3', 'x4']
data.toDF(*new_names)
-
Также можно переименовать с помощью простого 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])
Аналогично в Scala вы можете:
-
Переименуйте все столбцы:
val newNames = Seq("x3", "x4")
data.toDF(newNames: _*)
-
Переименовать из отображения с помощью select
:
val mapping = Map("x1" -> "x3", "x2" -> "x4")
df.select(
df.columns.map(c => df(c).alias(mapping.get(c).getOrElse(c))): _*
)
или foldLeft
+ withColumnRenamed
mapping.foldLeft(data){
case (data, (oldName, newName)) => data.withColumnRenamed(oldName, newName)
}
* Не путать с RDD.toDF
который не является переменным функционалом, и принимает имена столбцов в виде списка,
Ответ 2
Я тоже не мог найти легкое решение для pyspark, поэтому просто создал свой собственный, похожий на pandas 'df.rename(columns={'old_name_1':'new_name_1', 'old_name_2':'new_name_2'})
.
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'}")
Итак, ваше решение будет выглядеть как data = rename_columns(data, {'x1': 'x3', 'x2': 'x4'})
Это сэкономит мне несколько строк кода, надеюсь, что это тоже поможет.
Ответ 3
почему вы хотите выполнить его в одной строке, если вы распечатываете план выполнения, он фактически выполняется только в одной строке
data = spark.createDataFrame([(1,2), (3,4)], ['x1', 'x2'])
data = (data
.withColumnRenamed('x1','x3')
.withColumnRenamed('x2', 'x4'))
data.explain()
ВЫХОД
== Physical Plan ==
*(1) Project [x1#1548L AS x3#1552L, x2#1549L AS x4#1555L]
+- Scan ExistingRDD[x1#1548L,x2#1549L]
если вы хотите сделать это с кортежем списка, вы можете использовать простую функцию карты
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()
по-прежнему имеет тот же план
ВЫХОД
== Physical Plan ==
*(1) Project [x1#1650L AS x3#1654L, x2#1651L AS x4#1655L]
+- Scan ExistingRDD[x1#1650L,x2#1651L]