依赖公共列的两个数据框之间的交叉联接

时间:2019-04-19 12:43:18

标签: apache-spark pyspark pyspark-sql

crossJoin可以按以下方式完成:

df1 = pd.DataFrame({'subgroup':['A','B','C','D']})
df2 = pd.DataFrame({'dates':pd.date_range(date_today, date_today + timedelta(3), freq='D')})
sdf1 = spark.createDataFrame(df1)
sdf2 = spark.createDataFrame(df2)

sdf1.crossJoin(sdf2).toPandas()

在此示例中,有两个数据帧,每个数据帧包含4行,最后,我得到16行。

但是,对于我的问题,我想对每个用户进行交叉联接,并且该用户是两个数据框中的另一列,例如:

df1 = pd.DataFrame({'user':[1,1,1,1,2,2,2,2],'subgroup':['A','B','C','D','A','B','D','E']})
df2 = pd.DataFrame({'user':[1,1,1,1,2,2,2,2],'dates':np.hstack([np.array(pd.date_range(date_today, date_today + timedelta(3), freq='D')),np.array(pd.date_range(date_today+timedelta(1), date_today + timedelta(4), freq='D'))])})

应用每用户crossJoin的结果应该是包含32行的数据框。在pyspark中可行吗?

1 个答案:

答案 0 :(得分:0)

交叉联接是一种生成行的乘法的联接,因为联接键不能唯一地标识行(在我们的情况下,联接键是微不足道的,或者根本没有联接键)

让我们从示例数据帧开始:

import pyspark.sql.functions as psf
import pyspark.sql.types as pst
df1 = spark.createDataFrame(
    [[user, value] for user, value in zip(5 * list(range(2)), np.random.randint(0, 100, 10).tolist())], 
    schema=pst.StructType([pst.StructField(c, pst.IntegerType()) for c in ['user', 'value1']]))
df2 = spark.createDataFrame(
    [[user, value] for user, value in zip(5 * list(range(2)), np.random.randint(0, 100, 10).tolist())], 
    schema=pst.StructType([pst.StructField(c, pst.IntegerType()) for c in ['user', 'value2']]))

        +----+------+
        |user|value1|
        +----+------+
        |   0|    76|
        |   1|    59|
        |   0|    14|
        |   1|    71|
        |   0|    66|
        |   1|    61|
        |   0|     2|
        |   1|    22|
        |   0|    16|
        |   1|    83|
        +----+------+

        +----+------+
        |user|value2|
        +----+------+
        |   0|    65|
        |   1|    81|
        |   0|    60|
        |   1|    69|
        |   0|    21|
        |   1|    61|
        |   0|    98|
        |   1|    76|
        |   0|    40|
        |   1|    21|
        +----+------+

让我们尝试在常数列上联接数据帧,以查看交叉联接和在常数(平凡)列上进行常规联接之间的等效性:

df = df1.withColumn('key', psf.lit(1)) \
    .join(df2.withColumn('key', psf.lit(1)), on=['key'])

我们从spark> 2中得到一个错误,因为它意识到我们正在尝试进行交叉联接(笛卡尔积)

  

Py4JJavaError:调用o1865.showString时发生错误。   :org.apache.spark.sql.AnalysisException:检测到逻辑计划之间的内部联接的隐式笛卡尔积   LogicalRDD [user#1538,value1#1539],否   和   LogicalRDD [user#1542,value2#1543],否   连接条件丢失或微不足道。   要么:使用CROSS JOIN语法允许这些之间的笛卡尔积   关系,或:通过设置配置启用隐式笛卡尔积   变量spark.sql.crossJoin.enabled = true;

如果您的加入键(此处为user)不是唯一标识行的列,那么在每个user组中,您也将得到多行的行:

df = df1.join(df2, on='user')
print("Number of rows : \tdf1: {} \tdf2: {} \tdf: {}".format(df1.count(), df2.count(), df.count()))

        Number of rows :    df1: 10     df2: 10     df: 50

        +----+------+------+
        |user|value1|value2|
        +----+------+------+
        |   1|    59|    81|
        |   1|    59|    69|
        |   1|    59|    61|
        |   1|    59|    76|
        |   1|    59|    21|
        |   1|    71|    81|
        |   1|    71|    69|
        |   1|    71|    61|
        |   1|    71|    76|
        |   1|    71|    21|
        |   1|    61|    81|
        |   1|    61|    69|
        |   1|    61|    61|
        |   1|    61|    76|
        |   1|    61|    21|
        |   1|    22|    81|
        |   1|    22|    69|
        |   1|    22|    61|
        |   1|    22|    76|
        |   1|    22|    21|
        +----+------+------+

用户0 5 * 5行+用户1 5 * 5行,因此50

注意:通常使用self join后跟filter表示您应该使用 窗口功能