我有一个数据框(1580行x 48列),其中每列包含问题的答案,但不是每一行都包含每个问题的答案(留下它NaN
)。问题组是相关的,我想把问题组的答案列成新的列(c_answers
和i_answers
)。我已经为每组问题生成了正确答案的列表。下面是一个数据示例:
ex_df = pd.DataFrame([["a", "b", "d"],[np.nan, "a", "b"], ["c", "e", np.nan]], columns=["q1", "q2", "q3"])
correct_answers = ["a", "b", "c"]
ex_df
生成以下数据帧:
q1 q2 q3
0 a b d
1 NaN a b
2 e c NaN
理想情况下,我想要做的是创建一个函数,该函数将对每列进行评分,并且对于一行(出现在correct_answers
列表中)的每个正确答案,它将使c_answers
列增加1,对于不在correct_answers
中的每个答案,它将使i_answers
列增加1,但如果提供的答案是NaN
,它将不这样做(不计算为正确或不正确)。然后可以将此函数应用于每组问题,计算该组每行正确和错误答案的数量。
我能够取得一点进展的是这样的东西:
ex_df['q1score'] = np.where(ex_df['q1'].isna(), np.nan,
np.where(ex_df['q1'].isin(correct_answers), 1, 100))
更新数据帧如下:
q1 q2 q3 q1score
0 a b d 1.0
1 NaN a b NaN
2 e c NaN 100.0
然后我可以重新使用这段代码将q2和q3分出到它们自己的新列中,然后我可以将其汇总到一个新列中,然后从那个列中,我可以生成另外两个列,它们可以从该总和中计算正确和不正确的分数。最后,我可以返回并删除我创建的其他4列,只保留我一开始想要的两个列。
在过去的两个小时里,我四处看看,尝试了不同的方法,我找到了很多解决我正在努力解决的不同问题的答案,但没有一种方法能真正适用于我的情况。也许我拼凑的解决方案是最好的,但我对编程还是比较陌生(18个月),它似乎不是解决这个问题的最有效或最python化的方法。希望别人有更好的答案。谢谢你!
编辑输出的更多信息:关于我想要的最终输出,我想要像这样的东西:
q1 q2 q3 c_answers i_answers
0 a b d 2 1
1 NaN a b 2 0
2 e c NaN 1 1
就像我说的,我可以使用嵌套的np.where()
来创建数字列,然后我可以总结并反向工程以从中获得原始计数。虽然这是一个解决方案,但它很麻烦,看起来可能不是最佳解决方案,特别是涉及到重复的数量(我必须为9组不同的列执行此过程,每组都是一组问题)。
使用sum
来计算True
的每一行正确和不正确的值:
m1 = ex_df.isin(correct_answers)
m2 = ex_df.notna() & ~m1
df = ex_df.assign(c_answers=m1.sum(axis=1), i_answers=m2.sum(axis=1))
print (df)
q1 q2 q3 c_answers i_answers
0 a b d 2 1
1 NaN a b 2 0
2 c e NaN 1 1
多组的可能解决方案:
groups = {'g1':['q1','q2'], 'g2':['q2','q3'], 'g3':['q1','q2','q3']}
for k, v in groups.items():
m1 = ex_df[v].isin(correct_answers)
m2 = ex_df[v].notna() & ~m1
ex_df = ex_df.assign(**{f'c_answers_{k}':m1.sum(axis=1),
f'i_answers_{k}':m2.sum(axis=1)})
print (ex_df)
q1 q2 q3 c_answers_g1 i_answers_g1 c_answers_g2 i_answers_g2
0 a b d 2 0 1 1
1 NaN a b 1 0 2 0
2 c e NaN 1 1 0 1
c_answers_g3 i_answers_g3
0 2 1
1 2 0
2 1 1