PySpark合并数据框和计数值

时间:2019-10-18 16:57:18

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

我有两个不同的数据框,我想找出glueContext.sparkContext.hadoopConfiguration.set("fs.s3.canned.acl", "BucketOwnerFullControl") 的{​​{1}}列和m的{​​{1}}列之间的交点数。通过交集,我的意思是两个列共有的唯一值的数量。如果df1有10列,而df2有20列,那么我得到的交集数将是200。在这里我仅使用PySpark。

对于我来说,数据量很大,我运行了以下代码

df1

或此代码

n

或者这个

df2

但是在所有这些情况下,由于我正在运行两个dict = {} for a in df1.columns: i_u = df1.select(a).distinct() i_u = i_u.select(a).collect() for b in df2.columns: i_b = df2.select(b).distinct() i_b = i_b.select(b).collect() l = len(list(set(i_u) & set(i_b))) str = a + ","+b+"," dict[str] = l 循环,因此代码不够快。我要创建此字典或任何具有两个列名及其交点数的数据表示形式。我尝试使用dict = {} for a in df1.columns: if not "." in a: for b in df2.columns: l = df1.join(df2, df1[a] == df2[b], how="inner") l = l.select(a).distinct().count() str = a + ","+b+"," dict[str] = l ,但仍然不够好。

数据集:

dict = {}
for a in df1.columns:
    i_u = df1.select(a).distinct()
    for b in df2.columns:
            a_u = df2.select(b).distinct()
            l = i_u.join(a_u, i_u[a] == a_u[b], how="inner").count()
            str = a + ","+b+","
            dict[str] = l

只要有列名及其计数,输出字典应看起来像这样或其他任何格式。它可以是任何格式,我只关心输出。

dict = {“ col1,col9”:3,“ col1,col10”:0,“ col1,col11”:1,......

1 个答案:

答案 0 :(得分:2)

删除嵌套循环并让Spark为您完成应该大大提高性能。这需要两个步骤,在这里表示为函数。

第一步:在数组的每一列中收集唯一值并转置数据框。

from pyspark.sql import functions as F

def unique_and_transpose(df):
    df = df.select([F.collect_set(col).alias(col) for col in df.columns])
    params = []
    for col in df.columns:
        params.extend([F.lit(col), col])
    return df.select(F.explode(F.create_map(*params)).alias('column', 'values'))

如果保证所有列都没有重复值,则可以将F.collect_set(col)替换为F.collect_array(col)。严格来说,仅收集唯一值不是必需的,但可以加快第二步的速度。

此功能的作用最好通过一个示例来说明:

>>> df1.show()
+------+-----+----+
|  col1| col2|col3|
+------+-----+----+
|   red|  one| val|
| green|  two|   2|
|  blue|three| sda|
| black| nine| 452|
|purple|  ten| rww|
+------+-----+----+

>>> unique_and_transpose(df1).show(3, False)
+------+---------------------------------+
|column|values                           |
+------+---------------------------------+
|col3  |[sda, 452, rww, 2, val]          |
|col1  |[blue, green, red, black, purple]|
|col2  |[nine, one, three, two, ten]     |
+------+---------------------------------+

第二步::创建转置数据集的笛卡尔积并导出您要寻找的数量。

def cross_relate(df1, df2):
    return df1.alias('df1').crossJoin(df2.alias('df2')).select(
        F.col('df1.column').alias('col_1'),
        F.col('df2.column').alias('col_2'),
        F.size(F.array_intersect('df1.values', 'df2.values')).alias('nvals')
    )

笛卡尔乘积可以执行两个嵌套循环的操作,但是它只能按行工作,因此需要首先转置数据集。

借助这两个函数,您可以像这样计算每对列的唯一公共值的数量:

df1_ut = unique_and_transpose(df1).cache()
df2_ut = unique_and_transpose(df2).cache()
df = cross_relate(df1_ut, df2_ut)

结果是:

>>> df.show()
+-----+-----+-----+
|col_1|col_2|nvals|
+-----+-----+-----+
| col3|col10|    0|
| col3| col9|    0|
| col3|col11|    3|
| col1|col10|    0|
| col1| col9|    3|
| col1|col11|    1|
| col2|col10|    2|
| col2| col9|    0|
| col2|col11|    0|
+-----+-----+-----+

您想要一本字典,这又是一个步骤:

res = {f"{row.col_1},{row.col_2}": row.nvals for row in df.collect()}

>>> from pprint import pprint
>>> pprint(res)
{'col1,col10': 0,
 'col1,col11': 1,
 'col1,col9': 3,
 'col2,col10': 2,
 'col2,col11': 0,
 'col2,col9': 0,
 'col3,col10': 0,
 'col3,col11': 3,
 'col3,col9': 0}