Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/apache-spark/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用PySpark数据帧的成对列操作(如点积)_Python_Apache Spark_Pyspark_Apache Spark Sql - Fatal编程技术网

Python 使用PySpark数据帧的成对列操作(如点积)

Python 使用PySpark数据帧的成对列操作(如点积),python,apache-spark,pyspark,apache-spark-sql,Python,Apache Spark,Pyspark,Apache Spark Sql,很抱歉,但我对Spark还不熟悉,这似乎不是一个简单的操作 如果我有这样一个PySpark数据帧: +-----+-----+-----+-----+-----+ | id | A | B | ... | N | +-----+-----+-----+-----+-----+ | 0 | 0.1 | 0.5 | ... | 0.9 | | 1 | 0.2 | 0.2 | ... | 0.1 | | 2 | 0.4 | 0.

很抱歉,但我对Spark还不熟悉,这似乎不是一个简单的操作

如果我有这样一个PySpark数据帧:

    +-----+-----+-----+-----+-----+
    |  id |  A  |  B  | ... |  N  |
    +-----+-----+-----+-----+-----+
    |  0  | 0.1 | 0.5 | ... | 0.9 |
    |  1  | 0.2 | 0.2 | ... | 0.1 |
    |  2  | 0.4 | 0.4 | ... | 0.3 |
    |  3  | 0.7 | 0.2 | ... | 0.2 |
    +-----+-----+-----+-----+-----+
我想在数据框的列的所有列组合上运行一个两两列操作,就像点积一样,我该怎么做呢

我想要的是这样的数据帧:

+-------+--------+-------+
|  n_1  |  n_2   |  dot  |
+-------+--------+-------+
|   A   |   A    |  1.3  | 
|   A   |   B    |  1.9  |
|   A   |   C    |  3.6  |
|   A   |   D    |  0.7  |
...
|   B   |   A    |  4.6  |
...
+-------+--------+-------+
它包含nxn列乘积及其对应的点积的每个组合

为了记录在案,我有大约1800个N列和多达几百万个ID

谢谢大家!


更新:我在上面犯了一个错误。现在我已经澄清了,我想要列产品,而不是行产品。

< P>如果您只考虑数据,即没有行和COL头,它可以用NUMPY三行:

给出:

[[  0   0  14]
 [  0   1  32]
 [  0   2  50]
 [  0   3  68]
 [  1   1  77]
 [  1   2 122]
 [  1   3 167]
 [  2   2 194]
 [  2   3 266]
 [  3   3 365]]

当然,将PyStask DF转换为NUMPY数组是微不足道的…

< P>如果只考虑数据,即没有行和COL头,它可以用NUMPY三行:

给出:

[[  0   0  14]
 [  0   1  32]
 [  0   2  50]
 [  0   3  68]
 [  1   1  77]
 [  1   2 122]
 [  1   3 167]
 [  2   2 194]
 [  2   3 266]
 [  3   3 365]]

当然,将PySpark DF转换为Numpy数组或从Numpy数组转换为PySpark DF非常简单…

在PySpark中,它比在scala中要不那么优雅。然而,它是完全可以实现的,即使是以一种通用的方式,它也可以在不假设我们知道列的数量和名称的情况下工作

我们可以做的是数据帧的笛卡尔积与自身交叉连接,然后用map/reduce方案计算点积

我会这样做:

创建示例数据 数据=[0,1,5,9,1,2,2,1,2,4,4,3,3,7,2,2] df=spark.createDataFramedata,['id','A','B','C'] 除“id”之外的所有列 cols=[c表示df.columns中的c,如果c!=“id”] 相同的df,列名后缀为“\u 2” df2=df。在df.columns中为c选择*[df[c]。别名c+“\u 2” 点积 products=[F.colc*F.colc+''U 2'表示c在cols中] dot_product=reducelambda,b:a+b,products。别名“dot” 和交叉连接 df.交叉连接f2.选择F.col'id',F.col'id_2',dot_product.show +--+--+----------+ |id | id | 2 |点| +--+--+----------+ | 0| 0| 1.07| | 0| 1|0.21000000000000002| | 0| 2| 0.51| | 0| 3| 0.35| | 1| 0|0.21000000000000002| | 1| 1|0.09000000000000002| | 1| 2|0.19000000000000003| | 1| 3| 0.2| | 2| 0| 0.51| | 2| 1|0.19000000000000003| ....
在pyspark中,它没有scala中优雅。然而,它是完全可以实现的,即使是以一种通用的方式,它也可以在不假设我们知道列的数量和名称的情况下工作

我们可以做的是数据帧的笛卡尔积与自身交叉连接,然后用map/reduce方案计算点积

我会这样做:

创建示例数据 数据=[0,1,5,9,1,2,2,1,2,4,4,3,3,7,2,2] df=spark.createDataFramedata,['id','A','B','C'] 除“id”之外的所有列 cols=[c表示df.columns中的c,如果c!=“id”] 相同的df,列名后缀为“\u 2” df2=df。在df.columns中为c选择*[df[c]。别名c+“\u 2” 点积 products=[F.colc*F.colc+''U 2'表示c在cols中] dot_product=reducelambda,b:a+b,products。别名“dot” 和交叉连接 df.交叉连接f2.选择F.col'id',F.col'id_2',dot_product.show +--+--+----------+ |id | id | 2 |点| +--+--+----------+ | 0| 0| 1.07| | 0| 1|0.21000000000000002| | 0| 2| 0.51| | 0| 3| 0.35| | 1| 0|0.21000000000000002| | 1| 1|0.09000000000000002| | 1| 2|0.19000000000000003| | 1| 3| 0.2| | 2| 0| 0.51| | 2| 1|0.19000000000000003| .... 编辑 要计算更新中描述的所有列之间的组合, 您可以先将每一列与所有其他列相乘,然后用sum进行聚合

结果列的名称为X_Y=sumx*Y。在这之后,您只需要转置结果数据帧。以下示例基于与第一个答案相同的数据:

# get all possible combinations and calculate dot product
products = list()
for c in df.columns:
    if c != 'ID':
        for c2 in df.columns:
            if c2 != 'ID':
                products.append(sum(col(c) * col(c2)).alias(f"{c}_{c2}"))

dot_sums = df.select(*products)

# transpose columns to rows
col_values = explode(
    array(*[struct(lit(c).alias("col_name"), col(c).alias("val")) for c in dot_sums.columns])
).alias("cols_values")

# split the column name to get back the original columns
dot_sums.select(col_values) \
    .select(*[split(col("cols_values.col_name"), "_").getItem(0).alias("n_1"),
              split(col("cols_values.col_name"), "_").getItem(1).alias("n_2"),
              col("cols_values.val").alias("dot")]) \
    .show()

+---+---+------------------+
|n_1|n_2|               dot|
+---+---+------------------+
|  A|  A|               0.7|
|  A|  B|              0.39|
|  A|  C|              0.37|
|  B|  A|              0.39|
|  B|  B|0.4900000000000001|
|  B|  C|0.6300000000000001|
|  C|  A|              0.37|
|  C|  B|0.6300000000000001|
|  C|  C|0.9500000000000001|
+---+---+------------------+
原始答案 一种可能的方法是通过使用交叉连接获得id_1 id_2列的所有组合。 你可以把所有其他的A到N列放在一个数组中,这样你可以在以后计算点积

除了下面的解决方案之外,您可能还需要查看函数

以下是一个例子:

data = [(0, 0.1, 0.5, 0.9), (1, 0.2, 0.2, 0.1),
        (2, 0.4, 0.4, 0.3), (3, 0.7, 0.2, 0.2)
        ]

df = spark.createDataFrame(data, ["ID", "A", "B", "C"])
df.show()

# get all cols except the ID col
op_cols = [c for c in df.columns if c != 'ID']

# transform those cols to array
df1 = df.select(col("ID").alias("ID_1"), array(*op_cols).alias("other_cols_array1"))
df2 = df.select(col("ID").alias("ID_2"), array(*op_cols).alias("other_cols_array2"))

# crossJoin
matrix = df1.crossJoin(df2)
现在,您有了一个数据帧矩阵,其中列ID_1、other_cols_array1、ID_2、other_cols_array2

因此,您可以使用UDF计算每个组合ID_1-ID_2的点积,如下所示:

dot_product = udf(lambda v1, v2: sum([x*y for x, y in zip(v1, v2)]), DoubleType())

matrix.withColumn("dot", dot_product(col("other_cols_array1"), col("other_cols_array2")))\
      .select("ID_1", "ID_2", "dot")\
      .show()
编辑 要计算更新中描述的所有列之间的组合, 您可以先将每一列与所有其他列相乘,然后用sum进行聚合

结果列的名称为X_Y=sumx*Y。在这之后,你只需要转换结果 lt数据帧。以下示例基于与第一个答案相同的数据:

# get all possible combinations and calculate dot product
products = list()
for c in df.columns:
    if c != 'ID':
        for c2 in df.columns:
            if c2 != 'ID':
                products.append(sum(col(c) * col(c2)).alias(f"{c}_{c2}"))

dot_sums = df.select(*products)

# transpose columns to rows
col_values = explode(
    array(*[struct(lit(c).alias("col_name"), col(c).alias("val")) for c in dot_sums.columns])
).alias("cols_values")

# split the column name to get back the original columns
dot_sums.select(col_values) \
    .select(*[split(col("cols_values.col_name"), "_").getItem(0).alias("n_1"),
              split(col("cols_values.col_name"), "_").getItem(1).alias("n_2"),
              col("cols_values.val").alias("dot")]) \
    .show()

+---+---+------------------+
|n_1|n_2|               dot|
+---+---+------------------+
|  A|  A|               0.7|
|  A|  B|              0.39|
|  A|  C|              0.37|
|  B|  A|              0.39|
|  B|  B|0.4900000000000001|
|  B|  C|0.6300000000000001|
|  C|  A|              0.37|
|  C|  B|0.6300000000000001|
|  C|  C|0.9500000000000001|
+---+---+------------------+
原始答案 一种可能的方法是通过使用交叉连接获得id_1 id_2列的所有组合。 你可以把所有其他的A到N列放在一个数组中,这样你可以在以后计算点积

除了下面的解决方案之外,您可能还需要查看函数

以下是一个例子:

data = [(0, 0.1, 0.5, 0.9), (1, 0.2, 0.2, 0.1),
        (2, 0.4, 0.4, 0.3), (3, 0.7, 0.2, 0.2)
        ]

df = spark.createDataFrame(data, ["ID", "A", "B", "C"])
df.show()

# get all cols except the ID col
op_cols = [c for c in df.columns if c != 'ID']

# transform those cols to array
df1 = df.select(col("ID").alias("ID_1"), array(*op_cols).alias("other_cols_array1"))
df2 = df.select(col("ID").alias("ID_2"), array(*op_cols).alias("other_cols_array2"))

# crossJoin
matrix = df1.crossJoin(df2)
现在,您有了一个数据帧矩阵,其中列ID_1、other_cols_array1、ID_2、other_cols_array2

因此,您可以使用UDF计算每个组合ID_1-ID_2的点积,如下所示:

dot_product = udf(lambda v1, v2: sum([x*y for x, y in zip(v1, v2)]), DoubleType())

matrix.withColumn("dot", dot_product(col("other_cols_array1"), col("other_cols_array2")))\
      .select("ID_1", "ID_2", "dot")\
      .show()

所以基本上你要计算每一列的点积,每一列和它自己

一种解决方案是,首先为每一行i计算所有k和j的col[j]*col[k]的每个组合的一列。如果您有数千个列,我不推荐使用这种方法,因为Spark SQL往往要处理这么多的列

我要做的是首先分解数据帧,并为每个id和列名创建一行。然后,我将根据id将数据框与其自身连接起来。这将导致一个数据框,每个id和两个列名的组合各有一行

最后,我将两列的值相乘,用两个列名分组,求和得到点积

代码如下所示:

dot_product = udf(lambda v1, v2: sum([x*y for x, y in zip(v1, v2)]), DoubleType())

matrix.withColumn("dot", dot_product(col("other_cols_array1"), col("other_cols_array2")))\
      .select("ID_1", "ID_2", "dot")\
      .show()
从pyspark.sql导入函数为F 数据=[0,1,5,9,1,2,2,1,2,4,4,3,3,7,2,2] df=spark.createDataFramedata,['id','A','B','C'] cols=[c表示df.columns中的c,如果c!=“id”] 平面图df=df.selectF.col'id', F.explodeF.array*[F.structF.litc.alias'name', F.colc.c的别名'value'(单位:cols] 第一部分已完成,数据已按如下方式展平:

dot_product = udf(lambda v1, v2: sum([x*y for x, y in zip(v1, v2)]), DoubleType())

matrix.withColumn("dot", dot_product(col("other_cols_array1"), col("other_cols_array2")))\
      .select("ID_1", "ID_2", "dot")\
      .show()
>>>平展 +--+----+ |id|col| +--+----+ |0 |[A,0.1]| |0 |[B,0.5]| |0 |[C,0.9]| |1 |[A,0.2]| |1 |[B,0.2]| |1 |[C,0.1]| |2 |[A,0.4]| |2 |[B,0.4]| |2 |[C,0.3]| |3 |[A,0.7]| |3 |[B,0.2]| |3 |[C,0.2]| +--+----+ 然后是第二部分:连接、乘法、分组和求和:

flat_df_2=flat_df.选择'id', F.col'col.name'。别名'name2', F.col'col.value'。别名'value2' 结果=平坦度\ .joinflat_df_2,['id']\ .带有列'm',F.col'col.value'*F.col'value2'\ .groupByF.col'col.name'。别名'n_1',F.col'name2'。别名'n_2'\ .aggF.sum'm',别名'dot' 这将产生:

>>>结果显示 +--+--+---------+ |n|u 1 | n|u 2 |点| +--+--+---------+ |B | C | 0.63| |A | A | 0.7| |A | C | 0.37| |C | B | 0.63| |C | C | 0.9500000000000001| |C | A | 0.37| |B | B | 0.490000000000001| |B | A | 0.39| |A | B | 0.39| +--+--+---------+
所以基本上你要计算每一列的点积,每一列和它自己

一种解决方案是,首先为每一行i计算所有k和j的col[j]*col[k]的每个组合的一列。如果您有数千个列,我不推荐使用这种方法,因为Spark SQL往往要处理这么多的列

我要做的是首先分解数据帧,并为每个id和列名创建一行。然后,我将根据id将数据框与其自身连接起来。这将导致一个数据框,每个id和两个列名的组合各有一行

最后,我将两列的值相乘,用两个列名分组,求和得到点积

代码如下所示:

dot_product = udf(lambda v1, v2: sum([x*y for x, y in zip(v1, v2)]), DoubleType())

matrix.withColumn("dot", dot_product(col("other_cols_array1"), col("other_cols_array2")))\
      .select("ID_1", "ID_2", "dot")\
      .show()
从pyspark.sql导入函数为F 数据=[0,1,5,9,1,2,2,1,2,4,4,3,3,7,2,2] df=spark.createDataFramedata,['id','A','B','C'] cols=[c表示df.columns中的c,如果c!=“id”] 平面图df=df.selectF.col'id', F.explodeF.array*[F.structF.litc.alias'name', F.colc.c的别名'value'(单位:cols] 第一部分已完成,数据已按如下方式展平:

dot_product = udf(lambda v1, v2: sum([x*y for x, y in zip(v1, v2)]), DoubleType())

matrix.withColumn("dot", dot_product(col("other_cols_array1"), col("other_cols_array2")))\
      .select("ID_1", "ID_2", "dot")\
      .show()
>>>平展 +--+----+ |id|col| +--+----+ |0 |[A,0.1]| |0 |[B,0.5]| |0 |[C,0.9]| |1 |[A,0.2]| |1 |[B,0.2]| |1 |[C,0.1]| |2 |[A,0.4]| |2 |[B,0.4]| |2 |[C,0.3]| |3 |[A,0.7]| |3 |[B,0.2]| |3 |[C,0.2]| +--+----+ 然后是第二部分:连接、乘法、分组和求和:

flat_df_2=flat_df.选择'id', F.col'col.name'。别名'name2', F.col'col.value'。别名'value2' 结果=平坦度\ .joinflat_df_2,['id']\ .带有列'm',F.col'col.value'*F.col'value2'\ .groupByF.col'col.name'。别名'n_1',F.col'name2'。别名'n_2'\ .aggF.sum'm',别名'dot' 这将产生:

>>>结果显示 +--+--+---------+ |n|u 1 | n|u 2 |点| +--+--+---------+ |B | C | 0.63| |A | A | 0.7| |A | C | 0.37| |C | B | 0.63| |C | C| 0.9500000000000001| |C | A | 0.37| |B | B | 0.490000000000001| |B | A | 0.39| |A | B | 0.39| +--+--+---------+
如果它在内存中工作,这将是一个非常聪明的解决方案-如果内存不是问题,我更喜欢numpy/pandas而不是PySpark。如果它在内存中工作,这将是一个非常聪明的解决方案-如果内存不是问题,我更喜欢numpy/pandas而不是PySpark。抱歉,我在上面犯了一个错误,我现在澄清了:我想要垂直列的点积,而不是行积。原始数据帧第一列中的ID将丢失,因为点积将在它们之间求和。还不确定我是否必须考虑到这一点才能使您的解决方案生效。@KristianD'Amato我不确定我是否理解您的更新。你能添加一个小的例子,有几列和所需的输出吗?我已经这样做了。请参阅OP中的第二个数据帧。ID是A、B等,我原来的0…3是错误的。抱歉,我犯了一个错误,我现在澄清了:我想要垂直列点积,而不是行积。原始数据帧第一列中的ID将丢失,因为点积将在它们之间求和。还不确定我是否必须考虑到这一点才能使您的解决方案生效。@KristianD'Amato我不确定我是否理解您的更新。你能添加一个小的例子,有几列和所需的输出吗?我已经这样做了。请参阅OP中的第二个数据帧。ID是A、B等,我原来的0…3是错误的。抱歉,我犯了一个错误,我现在澄清了:我想要垂直列点积,而不是行积。原始数据帧第一列中的ID将丢失,因为点积将在它们之间求和。还不确定我是否必须考虑到这一点才能使您的解决方案发挥作用。您能否提供足够的样本数据,以了解您想要做什么,而不是更多和预期的输出,以便我们能够验证我们的解决方案?我更正了原始帖子中的第二个数据框,以反映澄清。谢谢抱歉,我犯了一个错误,我现在澄清了:我想要垂直列的点产品,而不是行产品。原始数据帧第一列中的ID将丢失,因为点积将在它们之间求和。还不确定我是否必须考虑到这一点才能使您的解决方案发挥作用。您能否提供足够的样本数据,以了解您想要做什么,而不是更多和预期的输出,以便我们能够验证我们的解决方案?我更正了原始帖子中的第二个数据框,以反映澄清。谢谢