【注:本文所提资源来自GitHub的Joyful-Pandas项目,一个pandas的中文学习项目,可自行前往获取完整资料】
开始正题:
某比赛有1000名选手,300位评委打分,每个选手由三个不同的评委打分,每位评委打10位选手的分。
现在需要将各个评委的编号转到列索引,行索引不变,表格内容为打分分数,缺失值(即选手i没有被评委j打分)用’-'填充。
数据源:
原作者给出了三种写法:
【方法一】思维量较大,有技巧性,对Pandas依赖较少¶,耗时0.468秒。
t0 = time.perf_counter()
############################################################################################
L, k = [], 1
for i in range(301):
judge = 'Judge_%d' % i
result = df[(df.iloc[:, 0::2] == judge).any(1)]
L_temp = (result.iloc[:, 0::2] == judge).values * result.iloc[:,
1::2].values
L.append(list(zip(result.index.tolist(), list(L_temp.max(axis=1)))))
L.pop(0)
df_result = pd.DataFrame([['-'] * 1000] * 300,
index=['Judge_%d' % i for i in range(1, 301)],
columns=['%d' % i for i in range(1, 1001)])
for i in L:
for j in i:
df_result.at['Judge_%d' % k, '%d' % j[0]] = j[1]
k += 1
# ############################################################################################
t1 = time.perf_counter()
print('时间为:%.3f' % (t1 - t0))
df_result.T.head()
【方法二】思路简单,但运行时间较长¶,耗时15s。
t0=time.perf_counter()
############################################################################################
judge = np.array([[df.iloc[:,0:2].values],[df.iloc[:,2:4].values],[df.iloc[:,4:6].values]]).reshape(6000)[0::2]
score = np.array([[df.iloc[:,0:2].values],[df.iloc[:,2:4].values],[df.iloc[:,4:6].values]]).reshape(6000)[1::2]
df_result = pd.DataFrame({'judge':judge,'score':score}
,index=np.array([range(1,1001)]*3).reshape(3000)).reset_index()
df_result = pd.crosstab(index=df_result['index'],columns=df_result['judge'],values=df_result['score']
,aggfunc=np.sum).fillna('-').T.reindex(['Judge_%d'%i for i in range(1,301)]).T
############################################################################################
t1=time.perf_counter()
print('时间为:%.3f'%(t1-t0))
df_result.head()
【方法三】基本与方法二类似,但借助pivot函数大幅提高速度¶,耗时0.053s。
t0=time.perf_counter()
############################################################################################
judge = np.array([[df.iloc[:,0:2].values],[df.iloc[:,2:4].values],[df.iloc[:,4:6].values]]).reshape(6000)[0::2]
score = np.array([[df.iloc[:,0:2].values],[df.iloc[:,2:4].values],[df.iloc[:,4:6].values]]).reshape(6000)[1::2]
df_result = pd.DataFrame({'judge':judge,'score':score}
,index=np.array([range(1,1001)]*3).reshape(3000)).reset_index()
df_result = df_result.pivot(index='index',columns='judge'
,values='score').T.reindex(['Judge_%d'%i for i in range(1,301)]).T.fillna('-')
############################################################################################
t1=time.perf_counter()
print('时间为:%.3f'%(t1-t0))
df_result
从上面的方法来说,明显第三种方法更快,各种思路和操作也确实更能锻炼解题能力,但我觉得写法中太多的切片、取值、转换,看着头大,可读性不高。作为一个Pythoner,明显不够优雅,下面是我的写法,比较简洁明了,供大伙参考:
【方法一】耗时0.24秒,简洁的代码省下来很多敲码时间,且更具可读性。
# 构建空白的df, 设定index和columns
ans_one = pd.DataFrame(index=range(1, 1001),
columns=["Judge_%d" % i for i in range(1, 301)])
# 按行向量化计算,并对ans_one的相应位置进行对应的赋值
for idx, row in df.iterrows():
ans_one.loc[idx, row[0]] = row[1]
ans_one.loc[idx, row[2]] = row[3]
ans_one.loc[idx, row[4]] = row[5]
# 空值填充
ans_one = ans_one.fillna('-')
ans_one
【方法二】没有上面的方法那么简洁,好在逻辑简单清晰,可读性高,小白一眼看下去也能知道在干嘛,运行速度也更快,只用0.127s。
l1, l2, l3 = df['评委一'].to_list(), df['评委二'].to_list(), df['评委三'].to_list()
# 取唯一值
judge_list = list(set(l1 + l2 + l3))
# 构建字典,作为后续DataFrame的data
judge_dict = {key: [] for key in judge_list}
# 按行向量化计算
for idx, row in df.iterrows():
judge_dict[row[0]].append(row[1])
judge_dict[row[2]].append(row[3])
judge_dict[row[4]].append(row[5])
# 不在该行中的键的值赋值为'-'
exclude_key = [row[0], row[2], row[4]]
for key in judge_dict:
if key not in exclude_key:
judge_dict[key].append('-')
# 构建结果的DataFrame
ans_two = pd.DataFrame(judge_dict, index=df.index)
# 对列名进行排序
col = sorted(ans_two.columns.to_list(), key=lambda x: int(x.split('_')[1]))
ans_two = ans_two[col]
ans_two
本文结束,Bye~