pyspark大型矩阵运算

时间:2017-09-21 19:47:52

标签: apache-spark pyspark matrix-multiplication

我有一个矩阵A,有9密耳行和80 K列。矩阵本身非常稀疏。现在,我想得到比率

ratio = (A.T x A)/(n-((1-A).T x (1-A)))

想象一下,用户是我的行,电影是列,我想计算的是人们看起来像是电影(m1或m2),它们的百分比似乎都是电影(m1和m2)。

我通过将A转换为rdd找到了第一部分(A.T x A)的解决方案。

 def coordinateMatrixMultiply(leftmatrix, rightmatrix):
        left = leftmatrix.map(lambda (i, j, v): (j, (i, v)))
        right = rightmatrix.map(lambda (j, k, w): (j, (k, w)))
        productEntries = left   \
                        .join(right)    \
                        .map(lambda (x, ((i, v), (k, w))): ((i, k), (v * w)))   \
                        .reduceByKey(lambda x,y: x+y)   \
                        .map(lambda ((i, k), sum): (i, k, sum))
        return productEntries

然而,我发现分母(1-A).T x(1-A)由于尺寸而难以解决。由于记忆的原因,尝试创建一个密集的矩阵是不可能的。我也尝试使用union all将大量数据帧连接在一起,但速度非常慢。有一个更好的方法吗?谢谢!

1 个答案:

答案 0 :(得分:0)

您不需要矩阵产品来执行此类计算:

首先,让我们创建一个0和1的样本数据框:

import numpy as np
import pyspark.sql.funtions as psf
from pyspark.sql import Window

df = spark.createDataFrame(
    sc.parallelize([["id" + str(n)] + np.random.randint(0, 2, 10).tolist() for n in range(100)]), 
    ["id"] + ["movie" + str(i) for i in range(10)])
df.show()

    +----+------+------+------+------+------+------+------+------+------+------+
    |  id|movie0|movie1|movie2|movie3|movie4|movie5|movie6|movie7|movie8|movie9|
    +----+------+------+------+------+------+------+------+------+------+------+
    | id0|     1|     0|     1|     1|     1|     0|     0|     0|     0|     1|
    | id1|     0|     1|     1|     0|     0|     0|     1|     0|     1|     0|
    | id2|     1|     1|     1|     0|     0|     1|     1|     0|     0|     1|
    | id3|     1|     0|     0|     1|     0|     1|     0|     0|     0|     1|
    | id4|     0|     0|     0|     1|     1|     0|     0|     1|     0|     1|
    | id5|     1|     1|     1|     0|     1|     0|     0|     0|     1|     0|
    | id6|     0|     1|     1|     0|     1|     0|     0|     1|     0|     1|
    | id7|     1|     1|     1|     1|     0|     1|     1|     0|     1|     0|
    | id8|     0|     1|     0|     0|     1|     1|     1|     0|     1|     1|
    | id9|     1|     0|     1|     0|     0|     1|     1|     0|     1|     0|
    |id10|     1|     0|     1|     1|     0|     1|     0|     1|     0|     0|
    |id11|     1|     0|     0|     0|     0|     0|     1|     1|     0|     0|
    |id12|     0|     1|     0|     0|     1|     1|     1|     0|     1|     0|
    |id13|     0|     0|     1|     1|     1|     1|     0|     0|     0|     1|
    |id14|     1|     0|     0|     0|     0|     1|     0|     1|     0|     0|
    |id15|     1|     1|     0|     1|     0|     0|     0|     0|     0|     0|
    |id16|     1|     0|     1|     1|     0|     1|     1|     0|     1|     0|
    |id17|     1|     0|     1|     0|     0|     1|     0|     0|     1|     0|
    |id18|     1|     0|     1|     1|     1|     1|     0|     0|     0|     0|
    |id19|     0|     1|     1|     0|     0|     1|     0|     0|     1|     0|
    +----+------+------+------+------+------+------+------+------+------+------+

第一步是展开电影栏,因此我们只有2列,一列用于id,另一列用于movie

df_expl = df\
    .select(
        "id", 
        psf.explode(
            psf.array(
                [psf.when(df[c] == 1, psf.lit(c)).otherwise(None) for c in df.columns if c != "id"]
            )).alias("movie"))\
    .filter("movie IS NOT NULL")

对于每个movie,我们将使用window函数计算看到它的人数:

w = Window.partitionBy("movie")
df_count = df_expl.withColumn("count", psf.count("*").over(w))

现在我们将自己加入这个数据帧以获得电影对:

df1 = df_count.select([df_count[c].alias(c + "1") for c in df_count.columns])
df2 = df_count.select([df_count[c].alias(c + "2") for c in df_count.columns])

df12 = df1.join(df2, (df1.id1 == df2.id2) & (df1.movie1 < df2.movie2)).drop("id2")

最后,对于每个(movie1, movie2)对,我们将计算在看过这两者的人中看到过的人的比例:

res = df12\
    .groupBy("movie1", "movie2")\
    .agg(
        psf.count("id1").alias("count12"), 
        psf.max("count1").alias("count1"), 
        psf.max("count2").alias("count2"))\
    .withColumn("pct", psf.col("count12")/(psf.col("count1") + psf.col("count2") - psf.col("count12")))
res.sort("movie1", "movie2").show()

    +------+------+-------+------+------+-------------------+
    |movie1|movie2|count12|count1|count2|                pct|
    +------+------+-------+------+------+-------------------+
    |movie0|movie1|     20|    43|    48|0.28169014084507044|
    |movie0|movie2|     24|    43|    50|0.34782608695652173|
    |movie0|movie3|     27|    43|    52|0.39705882352941174|
    |movie0|movie4|     17|    43|    47| 0.2328767123287671|
    |movie0|movie5|     18|    43|    46| 0.2535211267605634|
    |movie0|movie6|     20|    43|    44|0.29850746268656714|
    |movie0|movie7|     13|    43|    35|                0.2|
    |movie0|movie8|     20|    43|    44|0.29850746268656714|
    |movie0|movie9|     15|    43|    47|                0.2|
    |movie1|movie2|     26|    48|    50| 0.3611111111111111|
    |movie1|movie3|     25|    48|    52| 0.3333333333333333|
    |movie1|movie4|     27|    48|    47|0.39705882352941174|
    |movie1|movie5|     23|    48|    46|  0.323943661971831|
    |movie1|movie6|     25|    48|    44|  0.373134328358209|
    |movie1|movie7|     17|    48|    35|0.25757575757575757|
    |movie1|movie8|     24|    48|    44|0.35294117647058826|
    |movie1|movie9|     21|    48|    47|0.28378378378378377|
    |movie2|movie3|     25|    50|    52| 0.3246753246753247|
    |movie2|movie4|     28|    50|    47| 0.4057971014492754|
    |movie2|movie5|     27|    50|    46|  0.391304347826087|
    +------+------+-------+------+------+-------------------+