【发布时间】:2021-04-22 07:22:49
【问题描述】:
TL;DR:我知道 .apply() 在 pandas 中很慢。但是,我有一个作用于索引的函数,我无法弄清楚如何对其进行矢量化。我希望这个函数作用于两组参数(分别为 500,000 和 1,500 长)。我想生成一个数据框,其中第一个参数作为行索引,第二个参数作为列名,以及包含该特定行和列的函数输出的单元格。就目前而言,代码看起来需要几天时间才能运行。更多细节和最小可重复示例如下:
输入数据:
我有一系列唯一的学生 ID,有 500,000 名学生。我有一个由这些学生 ID 索引的 df (exam_score_df),其中包含每个学生在数学、语言、历史和科学方面的相应分数。
我还有一系列学校代码(每个学校代码对应一所学校),有 1,500 所学校。我有一个由学校代码索引的 df (school_weight_df),其中包含学校在数学、语言、历史和科学方面的权重,用于计算学生的分数。每行还包含一个“Y”或“N”索引的“Alternative_Score”,因为有些学校允许您在历史和科学之间取最好的科目分数来计算您的总分。
我写的要矢量化的函数:
def calc_score(student_ID, program_code):
'''
For a given student and program, returns students score for that program.
'''
if school_weight_df.loc[program_code]['Alternative_Score'] == 'N':
return np.dot(np.array(exam_score_df.loc[student_ID][['LANG', 'MAT', 'HIST', 'SCI']]),
np.array(school_weight_df.loc[program_code][['%LANG','%MAT','%HIST','%SCI']]))
elif school_weight_df.loc[program_code]['Alternative_Score'] == 'Y':
history_score = np.dot(np.array(exam_score_df.loc[student_ID][['LANG', 'MAT', 'HIST']]),
np.array(school_weight_df.loc[program_code][['%LANG','%MAT','%HIST']]))
science_score = np.dot(np.array(exam_score_df.loc[student_ID][['LANG', 'MAT', 'SCI']]),
np.array(school_weight_df.loc[program_code][['%LANG','%MAT','%SCI']]))
return max(history_score, science_score)
示例 DF:
以下是exam_score_df 和school_weight_df 的示例df:
student_data = [[3, 620, 688, 638, 688], [5, 534, 602, 606, 700], [9, 487, 611, 477, 578]]
exam_score_df = pd.DataFrame(student_data, columns = ['student_ID', 'LANG', 'MAT', 'HIST', 'SCI'])
exam_score_df.set_index('student_ID')
program_data = [[101, 20, 30, 25, 25, 'N'], [102, 40, 10, 50, 50, 'Y']]
school_weight_df = pd.DataFrame(program_data, columns = ['program_code', '%LANG','%MAT','%HIST','%SCI', 'Alternative_Score'])
school_weight_df.set_index('program_code', inplace = True)
这是用于索引以下代码的系列:
series_student_IDs = pd.Series(exam_score_df.index, inplace = True)
series_program_codes = pd.Series(school_weight_df.index, inplace = True)
使用函数创建 DF 的代码:
为了创建每个程序中所有学生分数的 df,我使用了嵌套的 .apply():
new_df = pd.DataFrame(series_student_IDs.apply(lambda x: series_program_codes.apply(lambda y: calc_score(x, y))))
我已经阅读了几本关于在 pandas 中优化代码的入门书,包括写得很好的Guide by Sofia Heisler。我最关心的问题,也是我不知道如何向量化这段代码的原因,是我的函数需要对索引进行操作。我还有一个次要问题是,即使我进行矢量化,np.dot on large matrices 也存在这个问题,我无论如何都想循环。
感谢大家的帮助!我才编码几个月,所以非常感谢所有有帮助的 cmets。
【问题讨论】:
-
你能解释一下你想要达到的目标吗?我看到你已经添加了这些细节,但它与你如何尝试这样做混淆了,因此令人困惑。不讨论how部分,你能简单谈谈目标吗?
-
目标:我想要一个 df,其中包含每个学生的行和每个学校的列,以及包含该学校学生分数的单元格。这有帮助吗?
-
如果您可以根据
school_weight_df.loc[program_code]['Alternative_Score']值将行分成两组,则“矢量化”可能会更容易。group_by可能会为您服务。然后您可能能够执行多行dot。在这一点上,我不会担心你链接的 large_matrix 问题。 -
对一批学生进行矢量化计算的批量方法是您可以做的。你不需要做单独的点积。您可以将乘法和减法部分分开,并针对备用分数的 2 个条件分别计算减法部分。检查我的解决方案和解释。 @hpaulj 也做评论和建议,期待您的建议,谢谢!
标签: python pandas numpy vectorization database-indexes