按列中的相似名称对行进行分组

Group rows by similar name in column

为了计算,我已经创建了这个数据框

df1:

+--------------+------+------------+
|name          |MG    |questions   |
+--------------+------+------------+
|toto          |MG1   |[Q1, Q2]    |
|toto          |MG3   |[Q4, Q6, Q7]|
|toto-123      |MG1   |[Q1, Q2]    |
|toto-456      |MG1   |[Q1, Q2]    |
|titi          |MG1   |[Q1, Q2]    |
|...           |...   |...         |
+--------------+------+------------+

到现在为止,这已经足够了,但是现在,为了新的需要,我遇到了一个我没有解决的问题:我必须按 nameMG 对行进行分组,而不是确切的 name,但与 name 相似,就像那样

df1:

+--------------+------+-----------------------------+
|name          |MG    |questions                    |
+--------------+------+-----------------------------+
|toto          |MG1   |[Q1, Q2] [Q1, Q2] [Q1, Q2]   |
|toto          |MG3   |[Q4, Q6, Q7]                 |
|titi          |MG1   |[Q1, Q2]                     |
|...           |...   |...                          |
+--------------+------+-----------------------------+

这里,toto的questionstoto, toto-123 and toto-456的问题。它们可以位于不同的阵列中,也可以位于同一阵列中。理想情况下,toto-123toto-456 的行仍然保留。

我尝试了 groupby 带有条件思想 SQL 表达式的函数,但没有成功

你有什么提示或解决办法吗? 非常感谢

您可以先使用自连接查找具有相似 namemg 值的行,在连接条件中使用 like 运算符:

import pyspark.sql.functions as F

df = spark.createDataFrame([
    ("toto", "MG1", ["Q1", "Q2"]), ("toto", "MG3", ["Q4", "Q6", "Q7"]),
    ("toto-123", "MG1", ["Q1", "Q2"]), ("toto-456", "MG1", ["Q1", "Q2"]),
    ("titi", "MG1", ["Q1", "Q2"])], ["name", "mg", "questions"])

similar_df = df.alias("a").join(
    df.alias("b"),
    F.expr("a.name like concat('%', b.name, '%') and a.mg = b.mg and a.name != b.name"),
).selectExpr("a.name", "a.mg", "b.name as similar_name")

similar_df.show()
#+--------+---+------------+
#|    name| mg|similar_name|
#+--------+---+------------+
#|toto-123|MG1|        toto|
#|toto-456|MG1|        toto|
#+--------+---+------------+

然后,再次与原始dataframe合并,并将对应名称的值更改为第一步找到的相似名称,最后按namemg分组,收集[=的列表17=]:

result = df.alias("df").join(
    similar_df.alias("s"),
    ["name", "mg"],
    'left'
).select(
    F.coalesce(F.col("similar_name"), F.col("df.name")).alias("name"),
    F.col("mg"),
    F.col("df.questions"),
).groupBy("name", "mg").agg(
    F.collect_list("questions").alias("questions")
)

result.show(truncate=False)
#+----+---+------------------------------+
#|name|mg |questions                     |
#+----+---+------------------------------+
#|toto|MG1|[[Q1, Q2], [Q1, Q2], [Q1, Q2]]|
#|titi|MG1|[[Q1, Q2]]                    |
#|toto|MG3|[[Q4, Q6, Q7]]                |
#+----+---+------------------------------+