如何在通勤数据帧上执行笛卡尔积?

时间:2021-02-02 11:08:20

标签: python apache-spark pyspark apache-spark-sql pyspark-dataframes

我尝试使用 pyspark.DataFramepandas.DataFrame 在同一个数据帧 (df) 上执行笛卡尔积。这意味着它是 commuting dataframe

下面是 pyspark:

>>> a = spark.createDataFrame(data=[(1,'a', '1a'), (2,'b', '2b'),(3,'c', '3c')], schema=['letter', 'number', 'ln'])
>>> b = spark.createDataFrame(data=[(1,'a', '1a'), (2,'b', '2b'),(3,'c', '3c')], schema=['letter', 'number', 'ln'])
>>> a.join(b).show()
+------+------+---+------+------+---+
|letter|number| ln|letter|number| ln|
+------+------+---+------+------+---+
|     1|     a| 1a|     1|     a| 1a|
|     1|     a| 1a|     2|     b| 2b|
|     1|     a| 1a|     3|     c| 3c|
|     2|     b| 2b|     1|     a| 1a|
|     2|     b| 2b|     2|     b| 2b|
|     2|     b| 2b|     3|     c| 3c|
|     3|     c| 3c|     1|     a| 1a|
|     3|     c| 3c|     2|     b| 2b|
|     3|     c| 3c|     3|     c| 3c|
+------+------+---+------+------+---+

为了解释,我在这里做了一个小欺骗。实际上,它们是两个数据框 ab,但我的问题是应用于一个数据框 a 与它本身。

这里我想删除两种结果:

  1. 具有相同 ln 的行(此处用作 ID)
  2. 重复行,例如:(1,a,1a,2,b,2b)(2,b,2b,1,a,1a) 相同

第一条规则可以做如下:

>>> a.join(b, a.nl != b.nl).show(truncate=False)
+------+------+---+------+------+---+
|number|letter|nl |number|letter|nl |
+------+------+---+------+------+---+
|1     |a     |1a |2     |b     |2b |
|1     |a     |1a |3     |c     |3c |
|2     |b     |2b |1     |a     |1a |
|2     |b     |2b |3     |c     |3c |
|3     |c     |3c |1     |a     |1a |
|3     |c     |3c |2     |b     |2b |
+------+------+---+------+------+---+

与交叉连接相同

>>> a.join(a.alias('a1'), a.nl != col('a1.nl'),  how='cross').show()
+------+------+---+------+------+---+
|number|letter| nl|number|letter| nl|
+------+------+---+------+------+---+
|     1|     a| 1a|     2|     b| 2b|
|     1|     a| 1a|     3|     c| 3c|
|     2|     b| 2b|     1|     a| 1a|
|     2|     b| 2b|     3|     c| 3c|
|     3|     c| 3c|     1|     a| 1a|
|     3|     c| 3c|     2|     b| 2b|
+------+------+---+------+------+---+

第二条规则应该可以通过 group by 子句实现。但这是一种有效的方法吗?

我尝试使用 itertools.product 但速度很慢,因为我的数据帧有 300 000 行。

下面是熊猫:

使用 pandas 和 NumPy 我尝试创建基于交叉连接的多索引但失败(内存不足)

>>> df1 = pd.DataFrame(data=[(1,'a', '1a'), (2,'b', '2b'),(3,'c', '3c')], columns=['letter', 'number', 'ln'])
>>> df = pd.DataFrame(data=[(1,'a', '1a'), (2,'b', '2b'),(3,'c', '3c')], columns=['letter', 'number', 'ln'])
>>> i1 = df.index
combine_sample = np.transpose([np.tile(i1, len(i1)), np.repeat(i1, len(i1))])
Traceback (most recent call last):
...
numpy.core._exceptions.MemoryError: Unable to allocate 721. GiB for an array with shape (311119, 311119) and data type int64
>>> index= pd.MultiIndex.from_product([df.index,df.index])
Traceback (most recent call last):
...
numpy.core._exceptions.MemoryError: Unable to allocate 361. GiB for an array with shape (96795032161,) and data type int32

预期结果

这是一个像这样的数据帧:

+------+------+---+------+------+---+
|number|letter| nl|number|letter| nl|
+------+------+---+------+------+---+
|     1|     a| 1a|     2|     b| 2b|
|     1|     a| 1a|     3|     c| 3c|
|     2|     b| 2b|     3|     c| 3c|
+------+------+---+------+------+---+

感谢您的见解

1 个答案:

答案 0 :(得分:1)

您可以在已排序的数组列上调用删除重复项:

import pyspark.sql.functions as F

result = a.join(b,
    F.array(*[a[i] for i in a.columns]) < F.array(*[b[i] for i in b.columns])
)

result.show()
+------+------+---+------+------+---+
|letter|number| ln|letter|number| ln|
+------+------+---+------+------+---+
|     1|     a| 1a|     2|     b| 2b|
|     1|     a| 1a|     3|     c| 3c|
|     2|     b| 2b|     3|     c| 3c|
+------+------+---+------+------+---+