在子字符串匹配(或包含)上加入PySpark数据帧

在子字符串匹配(或包含)上加入PySpark数据帧,pyspark,Pyspark,我想在两个数据帧之间执行左连接,但列不完全匹配。第一个数据帧中的联接列相对于第二个数据帧有一个额外的后缀 from pyspark import SparkContext import pyspark.sql.functions as f sc = SparkContext() df1 = sc.parallelize([ ['AB-101-1', 'el1', 1.5], ['ABC-1020-1', 'el2', 1.3], ['AC-1030-1', 'el3'

我想在两个数据帧之间执行左连接,但列不完全匹配。第一个数据帧中的联接列相对于第二个数据帧有一个额外的后缀

from pyspark import SparkContext
import pyspark.sql.functions as f

sc = SparkContext()

df1 = sc.parallelize([
    ['AB-101-1', 'el1', 1.5],
    ['ABC-1020-1', 'el2', 1.3],
    ['AC-1030-1', 'el3', 8.5]
]).toDF(('id1', 'el', 'v1'))

df2 = sc.parallelize([
    ['AB-101', 3],
    ['ABC-1020', 4]
]).toDF(('id2', 'v2'))
作为左连接的结果,我希望得到的数据帧是:

df_join = sc.parallelize([
    ['AB-101-1', 'el1', 1.5, 'AB-101', 3],
    ['ABC-1020-1', 'el2', 1.3, 'ABC-1020', 4],
    ['AC-103-1', 'el3', 8.5, None, None]
]).toDF(('id1', 'el', 'v1', 'id2', 'v2'))

我很乐意使用
pyspark.sql.substring
获取“除最后2个字符外的所有字符”,或者使用类似
pyspark.sql.like
的内容,但是我不知道如何使这两种方法在连接内部正常工作。

此解决方案使用
split
解构原始
id1
,然后
concat
重建较短的
id2

df1 = (
    df1
    .withColumn('id1_els', f.split('id1','-'))
    .withColumn('id2', 
                f.concat(f.col('id1_els').getItem(0)
                         , f.lit('-')
                         , f.col('id1_els').getItem(1)))
)

df_join = df1.join(df2, 'id2', 'left').show()

但是我宁愿使用带有
子字符串的解决方案
包含的解决方案
,因为这在很大程度上取决于我的ID字段的特定形式。

此解决方案使用
split
解构原始的
id1
,然后使用
concat
重新构建较短的
id2

df1 = (
    df1
    .withColumn('id1_els', f.split('id1','-'))
    .withColumn('id2', 
                f.concat(f.col('id1_els').getItem(0)
                         , f.lit('-')
                         , f.col('id1_els').getItem(1)))
)

df_join = df1.join(df2, 'id2', 'left').show()

但是我更愿意使用带有
子字符串的解决方案,或者
包含
,因为这在很大程度上取决于我的ID字段的特定形式。

如果
id1
&
id2
有一些类似于您在问题中说明的模式,那么我建议采用以下方法

from pyspark.sql.functions import regexp_extract

df1 = sc.parallelize([
    ['AB-101-1', 'el1', 1.5],
    ['ABC-1020-1', 'el2', 1.3],
    ['AC-1030-1', 'el3', 8.5]
]).toDF(('id1', 'el', 'v1'))

df2 = sc.parallelize([
    ['AB-101', 3],
    ['ABC-1020', 4]
]).toDF(('id2', 'v2'))

df1 = df1.withColumn("id1_transformed", regexp_extract('id1', '(.*-.*)(-.*)', 1))

df_join = df1.join(df2, df1.id1_transformed==df2.id2, 'left').drop("id1_transformed")
df_join.show()
输出为:

+----------+---+---+--------+----+
|       id1| el| v1|     id2|  v2|
+----------+---+---+--------+----+
|ABC-1020-1|el2|1.3|ABC-1020|   4|
|  AB-101-1|el1|1.5|  AB-101|   3|
| AC-1030-1|el3|8.5|    null|null|
+----------+---+---+--------+----+

希望这有帮助

如果
id1
id2
有一些类似于您在问题中所说明的模式,那么我建议采用以下方法

from pyspark.sql.functions import regexp_extract

df1 = sc.parallelize([
    ['AB-101-1', 'el1', 1.5],
    ['ABC-1020-1', 'el2', 1.3],
    ['AC-1030-1', 'el3', 8.5]
]).toDF(('id1', 'el', 'v1'))

df2 = sc.parallelize([
    ['AB-101', 3],
    ['ABC-1020', 4]
]).toDF(('id2', 'v2'))

df1 = df1.withColumn("id1_transformed", regexp_extract('id1', '(.*-.*)(-.*)', 1))

df_join = df1.join(df2, df1.id1_transformed==df2.id2, 'left').drop("id1_transformed")
df_join.show()
输出为:

+----------+---+---+--------+----+
|       id1| el| v1|     id2|  v2|
+----------+---+---+--------+----+
|ABC-1020-1|el2|1.3|ABC-1020|   4|
|  AB-101-1|el1|1.5|  AB-101|   3|
| AC-1030-1|el3|8.5|    null|null|
+----------+---+---+--------+----+

希望这有帮助

在您的特定情况下,
regexp\u extract
可能是您的最佳选择,但在一般情况下,您可以使用:

df_join = df1.join(df2, df2.id2.contains(df1.id1), how='left')

在您的特定情况下,
regexp\u extract
可能是您的最佳选择,但在一般情况下,您可以使用:

df_join = df1.join(df2, df2.id2.contains(df1.id1), how='left')

我在下面贴了一个有限的解决方案作为答案,但我很乐意接受一个更一般的答案。我在下面贴了一个有限的解决方案作为答案,但我很乐意接受一个更一般的答案。