- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个 spark 数据框( prof_student_df
),其中列出了学生/教授对的时间戳。每个时间戳有 4 位教授和 4 位学生,每个教授-学生对都有一个“分数”(因此每个时间段有 16 行)。对于每个时间范围,我需要找到教授/学生之间的一对一配对,以最大限度地提高总分。每个教授只能在一个时间范围内与一名学生配对。
例如,这是一个时间范围内的配对/分数。
+------------+--------------+------------+-------+----------+
| time | professor_id | student_id | score | is_match |
+------------+--------------+------------+-------+----------+
| 1596048041 | p1 | s1 | 0.7 | FALSE |
| 1596048041 | p1 | s2 | 0.5 | TRUE |
| 1596048041 | p1 | s3 | 0.3 | FALSE |
| 1596048041 | p1 | s4 | 0.2 | FALSE |
| 1596048041 | p2 | s1 | 0.9 | TRUE |
| 1596048041 | p2 | s2 | 0.1 | FALSE |
| 1596048041 | p2 | s3 | 0.15 | FALSE |
| 1596048041 | p2 | s4 | 0.2 | FALSE |
| 1596048041 | p3 | s1 | 0.2 | FALSE |
| 1596048041 | p3 | s2 | 0.3 | FALSE |
| 1596048041 | p3 | s3 | 0.4 | FALSE |
| 1596048041 | p3 | s4 | 0.8 | TRUE |
| 1596048041 | p4 | s1 | 0.2 | FALSE |
| 1596048041 | p4 | s2 | 0.3 | FALSE |
| 1596048041 | p4 | s3 | 0.35 | TRUE |
| 1596048041 | p4 | s4 | 0.4 | FALSE |
+------------+--------------+------------+-------+----------+
目标是获得这个 is_match 列。它可以是 bool 值或 0/1 位或任何有效的。
p5
,
p1
,
p7
,
p9
或类似的东西。学生可能仍然是
s1
,
s2
,
s3
,
s4
.
最佳答案
编辑:正如评论中所讨论的,为了解决您更新中提到的问题,我们可以使用dense_rank每次将student_id转换为广义序列id,执行步骤1到3(使用student列),然后每次使用join转换student回到他们原来的 student_id。见下文 Step-0 和 步骤 4 .如果 timeUnit 中的教授少于 4 个,则 Numpy-end 中的维度将调整为 4(使用 np_vstack() 和 np_zeros()),请参阅更新的函数 find_assigned
.
你可以试试pandas_udf和 scipy.optimize.linear_sum_assignment (注意:后端方法是匈牙利算法,如 @cronoik 在主要评论中提到的),见下文:
from pyspark.sql.functions import pandas_udf, PandasUDFType, first, expr, dense_rank
from pyspark.sql.types import StructType
from scipy.optimize import linear_sum_assignment
from pyspark.sql import Window
import numpy as np
df = spark.createDataFrame([
('1596048041', 'p1', 's1', 0.7), ('1596048041', 'p1', 's2', 0.5), ('1596048041', 'p1', 's3', 0.3),
('1596048041', 'p1', 's4', 0.2), ('1596048041', 'p2', 's1', 0.9), ('1596048041', 'p2', 's2', 0.1),
('1596048041', 'p2', 's3', 0.15), ('1596048041', 'p2', 's4', 0.2), ('1596048041', 'p3', 's1', 0.2),
('1596048041', 'p3', 's2', 0.3), ('1596048041', 'p3', 's3', 0.4), ('1596048041', 'p3', 's4', 0.8),
('1596048041', 'p4', 's1', 0.2), ('1596048041', 'p4', 's2', 0.3), ('1596048041', 'p4', 's3', 0.35),
('1596048041', 'p4', 's4', 0.4)
] , ['time', 'professor_id', 'student_id', 'score'])
N = 4
cols_student = [*range(1,N+1)]
第 0 步:添加额外的列
student
,并使用
time
的所有独特组合创建一个新的数据帧 df3 +
student_id
+
student
.
w1 = Window.partitionBy('time').orderBy('student_id')
df = df.withColumn('student', dense_rank().over(w1))
+----------+------------+----------+-----+-------+
| time|professor_id|student_id|score|student|
+----------+------------+----------+-----+-------+
|1596048041| p1| s1| 0.7| 1|
|1596048041| p2| s1| 0.9| 1|
|1596048041| p3| s1| 0.2| 1|
|1596048041| p4| s1| 0.2| 1|
|1596048041| p1| s2| 0.5| 2|
|1596048041| p2| s2| 0.1| 2|
|1596048041| p3| s2| 0.3| 2|
|1596048041| p4| s2| 0.3| 2|
|1596048041| p1| s3| 0.3| 3|
|1596048041| p2| s3| 0.15| 3|
|1596048041| p3| s3| 0.4| 3|
|1596048041| p4| s3| 0.35| 3|
|1596048041| p1| s4| 0.2| 4|
|1596048041| p2| s4| 0.2| 4|
|1596048041| p3| s4| 0.8| 4|
|1596048041| p4| s4| 0.4| 4|
+----------+------------+----------+-----+-------+
df3 = df.select('time','student_id','student').dropDuplicates()
+----------+----------+-------+
| time|student_id|student|
+----------+----------+-------+
|1596048041| s1| 1|
|1596048041| s2| 2|
|1596048041| s3| 3|
|1596048041| s4| 4|
+----------+----------+-------+
第一步:使用pivot找到教授与学生的矩阵,注意我们将分数的负数设置为pivot的值,以便我们可以使用scipy.optimize.linear_sum_assignment来找到分配问题的最小成本:
df1 = df.groupby('time','professor_id').pivot('student', cols_student).agg(-first('score'))
+----------+------------+----+----+-----+----+
| time|professor_id| 1| 2| 3| 4|
+----------+------------+----+----+-----+----+
|1596048041| p4|-0.2|-0.3|-0.35|-0.4|
|1596048041| p2|-0.9|-0.1|-0.15|-0.2|
|1596048041| p1|-0.7|-0.5| -0.3|-0.2|
|1596048041| p3|-0.2|-0.3| -0.4|-0.8|
+----------+------------+----+----+-----+----+
第 2 步:使用 pandas_udf 和 scipy.optimize.linear_sum_assignment 获取列索引,然后将相应的列名分配给新列
assigned
:
# returnSchema contains one more StringType column `assigned` than schema from the input pdf:
schema = StructType.fromJson(df1.schema.jsonValue()).add('assigned', 'string')
# since the # of students are always N, we can use np.vstack to set the N*N matrix
# below `n` is the number of professors/rows in pdf
# sz is the size of input Matrix, sz=4 in this example
def __find_assigned(pdf, sz):
cols = pdf.columns[2:]
n = pdf.shape[0]
n1 = pdf.iloc[:,2:].fillna(0).values
_, idx = linear_sum_assignment(np.vstack((n1,np.zeros((sz-n,sz)))))
return pdf.assign(assigned=[cols[i] for i in idx][:n])
find_assigned = pandas_udf(lambda x: __find_assigned(x,N), schema, PandasUDFType.GROUPED_MAP)
df2 = df1.groupby('time').apply(find_assigned)
+----------+------------+----+----+-----+----+--------+
| time|professor_id| 1| 2| 3| 4|assigned|
+----------+------------+----+----+-----+----+--------+
|1596048041| p4|-0.2|-0.3|-0.35|-0.4| 3|
|1596048041| p2|-0.9|-0.1|-0.15|-0.2| 1|
|1596048041| p1|-0.7|-0.5| -0.3|-0.2| 2|
|1596048041| p3|-0.2|-0.3| -0.4|-0.8| 4|
+----------+------------+----+----+-----+----+--------+
注:根据来自
的建议@OluwafemiSule ,我们可以使用参数
maximize
而不是否定分数值。此参数可用
SciPy 1.4.0+ :
_, idx = linear_sum_assignment(np.vstack((n1,np.zeros((N-n,N)))), maximize=True)
第 3 步:使用 SparkSQL
stack函数对上述 df2 进行归一化,否定分数值并过滤分数为 NULL 的行。想要的
is_match
列应该有
assigned==student
:
df_new = df2.selectExpr(
'time',
'professor_id',
'assigned',
'stack({},{}) as (student, score)'.format(len(cols_student), ','.join("int('{0}'), -`{0}`".format(c) for c in cols_student))
) \
.filter("score is not NULL") \
.withColumn('is_match', expr("assigned=student"))
df_new.show()
+----------+------------+--------+-------+-----+--------+
| time|professor_id|assigned|student|score|is_match|
+----------+------------+--------+-------+-----+--------+
|1596048041| p4| 3| 1| 0.2| false|
|1596048041| p4| 3| 2| 0.3| false|
|1596048041| p4| 3| 3| 0.35| true|
|1596048041| p4| 3| 4| 0.4| false|
|1596048041| p2| 1| 1| 0.9| true|
|1596048041| p2| 1| 2| 0.1| false|
|1596048041| p2| 1| 3| 0.15| false|
|1596048041| p2| 1| 4| 0.2| false|
|1596048041| p1| 2| 1| 0.7| false|
|1596048041| p1| 2| 2| 0.5| true|
|1596048041| p1| 2| 3| 0.3| false|
|1596048041| p1| 2| 4| 0.2| false|
|1596048041| p3| 4| 1| 0.2| false|
|1596048041| p3| 4| 2| 0.3| false|
|1596048041| p3| 4| 3| 0.4| false|
|1596048041| p3| 4| 4| 0.8| true|
+----------+------------+--------+-------+-----+--------+
第 4 步:使用 join 将 student 转换回 student_id (如果可能,使用广播 join):
df_new = df_new.join(df3, on=["time", "student"])
+----------+-------+------------+--------+-----+--------+----------+
| time|student|professor_id|assigned|score|is_match|student_id|
+----------+-------+------------+--------+-----+--------+----------+
|1596048041| 1| p1| 2| 0.7| false| s1|
|1596048041| 2| p1| 2| 0.5| true| s2|
|1596048041| 3| p1| 2| 0.3| false| s3|
|1596048041| 4| p1| 2| 0.2| false| s4|
|1596048041| 1| p2| 1| 0.9| true| s1|
|1596048041| 2| p2| 1| 0.1| false| s2|
|1596048041| 3| p2| 1| 0.15| false| s3|
|1596048041| 4| p2| 1| 0.2| false| s4|
|1596048041| 1| p3| 4| 0.2| false| s1|
|1596048041| 2| p3| 4| 0.3| false| s2|
|1596048041| 3| p3| 4| 0.4| false| s3|
|1596048041| 4| p3| 4| 0.8| true| s4|
|1596048041| 1| p4| 3| 0.2| false| s1|
|1596048041| 2| p4| 3| 0.3| false| s2|
|1596048041| 3| p4| 3| 0.35| true| s3|
|1596048041| 4| p4| 3| 0.4| false| s4|
+----------+-------+------------+--------+-----+--------+----------+
df_new = df_new.drop("student", "assigned")
关于python - 在 pyspark 中实现递归算法以查找数据帧内的配对,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63160595/
我在数据框中有一列月份数字,想将其更改为月份名称,所以我使用了这个: df['monthName'] = df['monthNumber'].apply(lambda x: calendar.mont
Pyspark 中是否有一个 input() 函数,我可以通过它获取控制台输入。如果是,请详细说明一下。 如何在 PySpark 中编写以下代码: directory_change = input("
我们正在 pyspark 中构建数据摄取框架,并想知道处理数据类型异常的最佳方法是什么。基本上,我们希望有一个拒绝表来捕获所有未与架构确认的数据。 stringDf = sparkSession.cr
我正在开发基于一组 ORC 文件的 spark 数据框的 sql 查询。程序是这样的: from pyspark.sql import SparkSession spark_session = Spa
我有一个 Pyspark 数据框( 原始数据框 )具有以下数据(所有列都有 字符串 数据类型): id Value 1 103 2
我有一台配置了Redis和Maven的服务器 然后我执行以下sparkSession spark = pyspark .sql .SparkSession .builder .master('loca
从一些简短的测试来看,pyspark 数据帧的列删除功能似乎不区分大小写,例如。 from pyspark.sql import SparkSession from pyspark.sql.funct
我有: +---+-------+-------+ | id| var1| var2| +---+-------+-------+ | a|[1,2,3]|[1,2,3]| | b|[2,
从一些简短的测试来看,pyspark 数据帧的列删除功能似乎不区分大小写,例如。 from pyspark.sql import SparkSession from pyspark.sql.funct
我有一个带有多个数字列的 pyspark DF,我想为每一列根据每个变量计算该行的十分位数或其他分位数等级。 这对 Pandas 来说很简单,因为我们可以使用 qcut 函数为每个变量创建一个新列,如
我有以下使用 pyspark.ml 包进行线性回归的代码。但是,当模型适合时,我在最后一行收到此错误消息: IllegalArgumentException: u'requirement failed
我有一个由 | 分隔的平面文件(管道),没有引号字符。示例数据如下所示: SOME_NUMBER|SOME_MULTILINE_STRING|SOME_STRING 23|multiline text
给定如下模式: root |-- first_name: string |-- last_name: string |-- degrees: array | |-- element: struc
我有一个 pyspark 数据框如下(这只是一个简化的例子,我的实际数据框有数百列): col1,col2,......,col_with_fix_header 1,2,.......,3 4,5,.
我有一个数据框 +------+--------------------+-----------------+---- | id| titulo |tipo | formac
我从 Spark 数组“df_spark”开始: from pyspark.sql import SparkSession import pandas as pd import numpy as np
如何根据行号/行索引值删除 Pyspark 中的行值? 我是 Pyspark(和编码)的新手——我尝试编码一些东西,但它不起作用。 最佳答案 您不能删除特定的列,但您可以使用 filter 或其别名
我有一个循环生成多个因子表的输出并将列名存储在列表中: | id | f_1a | f_2a | |:---|:----:|:-----| |1 |1.2 |0.95 | |2 |0.7
我正在尝试将 hql 脚本转换为 pyspark。我正在努力如何在 groupby 子句之后的聚合中实现 case when 语句的总和。例如。 dataframe1 = dataframe0.gro
我想添加新的 2 列值服务 arr 第一个和第二个值 但我收到错误: Field name should be String Literal, but it's 0; production_targe
我是一名优秀的程序员,十分优秀!