考虑以下代码
question = spark.createDataFrame([{'A':1,'B':5},{'A':2,'B':5},
{'A':3,'B':5},{'A':3,'B':6}])
#+---+---+
#| A| B|
#+---+---+
#| 1| 5|
#| 2| 5|
#| 3| 5|
#| 3| 6|
#+---+---+
如何创建如下所示的spark数据框:
solution = spark.createDataFrame([{'C':1,'D':2},{'C':1,'D':3},
{'C':2,'D':3},{'C':5,'D':6}])
#+---+---+
#| C| D|
#+---+---+
#| 1| 2|
#| 1| 3|
#| 2| 3|
#| 5| 6|
#+---+---+
这是三元闭合的概念,我根据已连接的边连接三角形的第三条边。
我必须有(1,2)因为(1,5)和(2,5)存在,所以我必须有(1,3)因为(1,5)和(3,5)存在,并且我必须有(2,3)因为(2,5)和(3,5)存在。我必须有(5,6)因为(3,5)和(3,6)存在(两个方向的边缘)。 (5,6)不应该有一个额外的条目,因为从A映射到6没有两对。由于A中没有第二个映射到6的实例,(5,6)没有被添加。
答案 0 :(得分:0)
试试这个,
import pyspark.sql.functions as F
from pyspark.sql.types import *
from itertools import combinations
df = spark.createDataFrame([{'A':1,'B':5},{'A':2,'B':5},
{'A':3,'B':5},{'A':3,'B':6}])
def pairs(list_):
if len(set(list_)) > 1:
return [[int(x[0]),int(x[1])] for x in combinations(set(list_), r=2)]
else:
return None
triadic_udf = F.udf(pairs, ArrayType(ArrayType(IntegerType())))
cols = ['C','D']
splits = [F.udf(lambda val:val[0],IntegerType())\
,F.udf(lambda val:val[1],IntegerType())]
df1 = df.groupby('B').agg(F.collect_list('A').alias('A'))\
.withColumn('pairs',F.explode(triadic_udf(F.col('A'))))\
.dropna().select('pairs')
df2 = df.groupby('A').agg(F.collect_list('B').alias('B'))\
.withColumn('pairs',F.explode(triadic_udf(F.col('B'))))\
.dropna().select('pairs')
solution = df1.union(df2).select([s('pairs').alias(c) for s,c in zip(splits,cols)])
solution.show()
答案 1 :(得分:0)
val df = sc.parallelize(Array((1,5),(2,5),(3,5),(3,6),(1,7),(2,7))).toDF("A","B")
df.union(df.select("B","A"))
.groupByKey(r => r.getInt(0))
.flatMapGroups({
(K,Vs) => Vs.map(_.getInt(1)).toArray.combinations(2).map(a => (a(0), a(1)))
})
.dropDuplicates
.show
这是在Scala中,而不是Python,但应该很容易转换。我添加了一些额外的数据点来说明为什么dropDuplicates
是必要的。我基本上只是按照我上面在评论中写的步骤:
1)将原始数据帧附加到自身,但B和A切换为
2)按A组分组
3)flatmap组到所有成对组合(我认为这有scala函数)
4)将新列映射到单独的C和D列(我实际上并没有这样做)
5)如果需要,过滤重复