在pyspark中旋转行的值
我目前正在清理一个数据集,我一直在尝试使用pyspark。数据从csv读取到数据帧中,我需要的值在它们各自的行中,但对于某些行,值是混合的。我需要旋转这些行的值,以便这些值位于正确的列中。例如,假设我有以下数据集:在pyspark中旋转行的值,pyspark,Pyspark,我目前正在清理一个数据集,我一直在尝试使用pyspark。数据从csv读取到数据帧中,我需要的值在它们各自的行中,但对于某些行,值是混合的。我需要旋转这些行的值,以便这些值位于正确的列中。例如,假设我有以下数据集: +-------+-------+-------+ | A | B | C | +-------+-------+-------+ | 2 | 3 | 1 | +-------+-------+-------+ 但是第一行中的值应该是
+-------+-------+-------+
| A | B | C |
+-------+-------+-------+
| 2 | 3 | 1 |
+-------+-------+-------+
但是第一行中的值应该是
+-------+-------+-------+
| A | B | C |
+-------+-------+-------+
| 1 | 2 | 3 |
+-------+-------+-------+
我当前的解决方案是添加一个临时列,并为每个列重新赋值,重命名临时列,同时删除旧列:
// Add temporary column C
+-------+-------+-------+-------+
| A | B | C | tmp_C |
+-------+-------+-------+-------+
| 2 | 3 | 1 | 1 |
+-------+-------+-------+-------+
// Shift values
+-------+-------+-------+-------+
| A | B | C | tmp_C |
+-------+-------+-------+-------+
| 2 | 2 | 3 | 1 |
+-------+-------+-------+-------+
// Drop old column
+-------+-------+-------+
| B | C | tmp_C |
+-------+-------+-------+
| 2 | 3 | 1 |
+-------+-------+-------+
// Rename new column
+-------+-------+-------+
| B | C | A |
+-------+-------+-------+
| 2 | 3 | 1 |
+-------+-------+-------+
我在pyspark中实现此功能的方式如下:
from pyspark.sql import SparkSession
from pyspark.sql.function import when, col
def clean_data(spark_session, file_path):
df = (
spark_session
.read
.csv(file_path, header='true')
)
df = (
df
.withColumn(
"tmp_C",
when(
col("C") == 1,
col("C")
).otherwise("A")
)
.withColumn(
"C",
when(
col("C") == 1,
col("B")
).otherwise("C")
)
.withColumn(
"B",
when(
col("C") == 1,
col("A")
).otherwise("B")
)
)
df = df.drop("A")
df = df.withColumnRenamed("tmp_C", "A")
return df
对我来说,这看起来不太好,我也不确定这是解决这个问题的最好方法。我是Spark的新手,我想知道解决这种情况的最佳方法,尽管这确实有效。此外,我还想知道这是否是Spark的一个很好的用例(请注意,我使用的数据集很大,并且有更多的字段。上面的示例大大简化了)。是的,如果数据集很大,您应该使用Spark 您最好只是重新命名列,而不是移动实际数据?假设此数据问题是系统性的,如您的示例中所示。这有点复杂,因为基于列名而不是位置重新命名存在问题,所以您必须先更改为临时名称
from functools import reduce
old_cols = df.columns
new_cols = old_cols[1:] + [old_cols[0]]
temp_cols = [col + "_" for col in new_cols]
# Rename columns with temporary names
df_temp = reduce(lambda df, idx: df.withColumnRenamed(old_cols[idx], temp_cols[idx]), range(len(old_cols)), df)
# Rename columns to align with correct data
df = reduce(lambda df_temp, idx: df_temp.withColumnRenamed(temp_cols[idx], new_cols[idx]), range(len(temp_cols)), df_temp)
# Then revert back to original column order
df = df.select(old_cols)
如果按旋转顺序将每个列映射到对应的列中,速度可能会更快
// generate columns map
maps = dict(zip(['C', 'A', 'B'], ['A', 'B', 'C']))
// regular approach:
// select columns with alias maps
df.select([col(c).alias(maps.get(c, c)) for c in df.columns])
// row scan approach:
// select columns with alias maps that satisfied specific condition
df.select([when(<map-condition>, col(c).alias(maps.get(c, c))).otherwise(col(c)) for c in df.columns])
//生成列映射
maps=dict(zip(['C','A','B'],['A','B','C']))
//经常办法:
//选择具有别名映射的列
为df.columns中的c选择([col(c).alias(maps.get(c,c)))
//行扫描方法:
//选择别名映射满足特定条件的列
选择([when(,col(c).alias(maps.get(c,c)))。否则(col(c))为df.columns中的c选择])
希望这能有所帮助。我忘记在代码中添加的一件事是,在每次调用
withColumn
时,我都有一个参数检查行是否有错误(是的,错误是系统性的)。因此,如果所有行都显示相同的错误,但其中一些行是正确的,则此解决方案将起作用。我将进行编辑以反映这一点。是否有一种方法来实现这一点,即只有当行显示的格式不正确时才会发生旋转(存在旋转的所有行上的错误都相同)?由于某些行的格式正确,因此别名将导致数据反向错误。在仔细考虑了这个问题之后,听起来我除了扫描所有行并使用临时列执行旋转之外,没有其他选择。这听起来正确吗?确切地说,这种方法可以完美地扫描满足与不正确的显示格式相关联的映射条件的行。您可以检查我的附加更新,并将更改为您自己的自定义映射条件。非常感谢。不客气。自信地继续编码和部署!