Python 使用PySpark数据帧的成对列操作(如点积)
很抱歉,但我对Spark还不熟悉,这似乎不是一个简单的操作 如果我有这样一个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.
+-----+-----+-----+-----+-----+
| 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将丢失,因为点积将在它们之间求和。还不确定我是否必须考虑到这一点才能使您的解决方案发挥作用。您能否提供足够的样本数据,以了解您想要做什么,而不是更多和预期的输出,以便我们能够验证我们的解决方案?我更正了原始帖子中的第二个数据框,以反映澄清。谢谢