我在Pyspark中有一个包含15个列的数据框。
这些列的名称是id
、name
、emp.dno
、emp.sal
、state
、emp.city
、zip
等等。
现在我想将其中名称带有'.'
的列名替换为'_'
例如将'emp.dno'
替换为'emp_dno'
我希望能够动态地实现它
在Pyspark中我该如何做到这一点?
我在Pyspark中有一个包含15个列的数据框。
这些列的名称是id
、name
、emp.dno
、emp.sal
、state
、emp.city
、zip
等等。
现在我想将其中名称带有'.'
的列名替换为'_'
例如将'emp.dno'
替换为'emp_dno'
我希望能够动态地实现它
在Pyspark中我该如何做到这一点?
df.toDF(*(c.replace('.', '_') for c in df.columns))
或者说:
from pyspark.sql.functions import col
replacements = {c:c.replace('.','_') for c in df.columns if '.' in c}
df.select([col(c).alias(replacements.get(c, c)) for c in df.columns])
replacement
字典将会是这样的:
{'emp.city': 'emp_city', 'emp.dno': 'emp_dno', 'emp.sal': 'emp_sal'}
更新:
如果我有一个带有空格的列名的数据框,如何用
'_'
替换'.'
和空格?
import re
df.toDF(*(re.sub(r'[\.\s]+', '_', c) for c in df.columns))
我为您编写了一个简单快捷的函数,希望您能享受使用它! :)
def rename_cols(rename_df):
for column in rename_df.columns:
new_column = column.replace('.','_')
rename_df = rename_df.withColumnRenamed(column, new_column)
return rename_df
最简单的方法如下:
解释:
from pyspark.sql import functions as F
(df
.select(*[F.col(c).alias(c.replace('.',"_")) for c in df.columns])
.toPandas().head()
)
希望这有所帮助
MaxU的回答很好而且高效。这篇文章介绍了另一种同样高效并有助于保持代码干净的方法(使用quinn库)。
假设您有以下DataFrame:
+---+-----+--------+-------+
| id| name|emp.city|emp.sal|
+---+-----+--------+-------+
| 12| bob|New York| 80|
| 99|alice| Atlanta| 90|
+---+-----+--------+-------+
以下是如何在所有列中将点号替换为下划线的方法。
import quinn
def dots_to_underscores(s):
return s.replace('.', '_')
actual_df = df.transform(quinn.with_columns_renamed(dots_to_underscores))
actual_df.show()
actual_df
:+---+-----+--------+-------+
| id| name|emp_city|emp_sal|
+---+-----+--------+-------+
| 12| bob|New York| 80|
| 99|alice| Atlanta| 90|
+---+-----+--------+-------+
让我们使用 explain()
来验证该函数是否执行高效:
actual_df.explain(True)
== Parsed Logical Plan ==
'Project ['id AS id#50, 'name AS name#51, '`emp.city` AS emp_city#52, '`emp.sal` AS emp_sal#53]
+- LogicalRDD [id#29, name#30, emp.city#31, emp.sal#32], false
== Analyzed Logical Plan ==
id: string, name: string, emp_city: string, emp_sal: string
Project [id#29 AS id#50, name#30 AS name#51, emp.city#31 AS emp_city#52, emp.sal#32 AS emp_sal#53]
+- LogicalRDD [id#29, name#30, emp.city#31, emp.sal#32], false
== Optimized Logical Plan ==
Project [id#29, name#30, emp.city#31 AS emp_city#52, emp.sal#32 AS emp_sal#53]
+- LogicalRDD [id#29, name#30, emp.city#31, emp.sal#32], false
== Physical Plan ==
*(1) Project [id#29, name#30, emp.city#31 AS emp_city#52, emp.sal#32 AS emp_sal#53]
您可以看到,解析的逻辑计划几乎与物理计划相同,因此Catalyst优化器不需要进行太多的优化工作。它将id AS id#50
转换为id#29
,但这并不需要太多的工作。
with_some_columns_renamed
方法生成了一个更高效的解析计划。
def dots_to_underscores(s):
return s.replace('.', '_')
def change_col_name(s):
return '.' in s
actual_df = df.transform(quinn.with_some_columns_renamed(dots_to_underscores, change_col_name))
actual_df.explain(True)
这个解析计划仅使用点别名列。
== Parsed Logical Plan ==
'Project [unresolvedalias('id, None), unresolvedalias('name, None), '`emp.city` AS emp_city#42, '`emp.sal` AS emp_sal#43]
+- LogicalRDD [id#34, name#35, emp.city#36, emp.sal#37], false
== Analyzed Logical Plan ==
id: string, name: string, emp_city: string, emp_sal: string
Project [id#34, name#35, emp.city#36 AS emp_city#42, emp.sal#37 AS emp_sal#43]
+- LogicalRDD [id#34, name#35, emp.city#36, emp.sal#37], false
== Optimized Logical Plan ==
Project [id#34, name#35, emp.city#36 AS emp_city#42, emp.sal#37 AS emp_sal#43]
+- LogicalRDD [id#34, name#35, emp.city#36, emp.sal#37], false
== Physical Plan ==
*(1) Project [id#34, name#35, emp.city#36 AS emp_city#42, emp.sal#37 AS emp_sal#43]
更多信息:为什么在DataFrame上循环并多次调用withColumnRenamed
会创建过于复杂的解析计划,应该避免。
df.toDF(c.replace('.', '_') for c in df.columns)
或者df.toDF((c.replace('.', '_') for c in df.columns))
或者df.toDF(*(c.replace('.', '_') for c in df.columns))
。 - MaxU - stand with Ukrainereplacements = {c:c.replace('.','_').replace(' ','_') for c in df.columns}
- rp1df.select
是正确的方法。请查看此链接:https://dev59.com/U1gQ5IYBdhLWcg3wMhBJ#62728542 - Krunal Patel