我正在寻找一种有效的方法,可以使用其他行上可能存在的信息来计算新的列数据(每行)。
以下是示例子集:
df = pd.DataFrame({'baseSeq': {0: 'ADTPICR', 1: 'ADTPICR', 2: 'AVALFAED', 3: 'AVALFAED', 4: 'AVALFAED', 5: 'AVALFAED', 6: 'AVALFAED', 7: 'AVALFAED'}, 'modSeq': {0: 'ADT[+16]PICR', 1: 'ADTPICR', 2: 'AVALFAED[+16]', 3: 'AVALFAE[+16]D', 4: 'AVALFAED', 5: 'AVALFAED[-30]', 6: 'AVALFAED', 7: 'AVALFAED'}, 'charge': {0: 2, 1: 2, 2: 2, 3: 2, 4: 2, 5: 3, 6: 3, 7: 4}, 'modType': {0: 'hydoxy', 1: 'UNMOD', 2: 'hydroxy', 3: 'hydroxy', 4: 'UNMOD', 5: 'decarbox', 6: 'UNMOD', 7: 'UNMOD'}, 'area_0': {0: 1862, 1: 22737, 2: 40060, 3: 40131, 4: 21962, 5: 12, 6: 21885, 7: 2116}, 'area_25': {0: 2472, 1: 30966, 2: 2423, 3: 2407, 4: 34387, 5: 16, 6: 35444, 7: 3072}, 'area_50': {0: 3015, 1: 24660, 2: 3553, 3: 3577, 4: 29860, 5: 40, 6: 33511, 7: 2974}})
baseSeq modSeq charge modType area_0 area_25 area_50
0 ADTPICR ADT[+16]PICR 2 hydoxy 1862 2472 3015
1 ADTPICR ADTPICR 2 UNMOD 22737 30966 24660
2 AVALFAED AVALFAED[+16] 2 hydroxy 40060 2423 3553
3 AVALFAED AVALFAE[+16]D 2 hydroxy 40131 2407 3577
4 AVALFAED AVALFAED 2 UNMOD 21962 34387 29860
5 AVALFAED AVALFAED[-30] 3 decarbox 12 16 40
6 AVALFAED AVALFAED 3 UNMOD 21885 35444 33511
7 AVALFAED AVALFAED 4 UNMOD 2116 3072 2974
具体来说,我想为每个修改后的序列(基本上是每一行)的“未修改部分”计算新列,然后将其扩展到多个“区域”列中。
fracUnmod =(1-(area_modified /(area_modified + area_unmodified)))
“ area_unmodified”值必须来自不同的行,因此我似乎无法找出使用df.concat()
或df.insert()
之类的函数来完成此操作的方法。
从共享相同“ baseSeq”和“ charge”值但没有任何修改的行中确定正确的“ area_unmodified”值(为方便起见,我在列中包含“ modType”)。
我不必为未修改的行计算fracUnmod,但是我不想从结果输出中删除这些行(将它们留在fracUnmod = 0.5中就可以了)。
有时候在某些行中找不到未修改的版本(未显示),但由于我意识到我偏向于在另一篇文章中问次要问题的偏好,所以我将其遗漏了,而且我能够过滤它们在Excel中手动退出。
我一直在做这样的事情,遍历每一行:
for X in df.columns[df.columns.str.contains('area')].tolist():
df[X.replace('area', 'fracUnmod')] = ''
for row in df.index:
for X in df.columns[df.columns.str.contains('area')].tolist():
Y = X.replace('area', 'fracUnmod')
df[Y].iloc[row] = (1 - ((df.iloc[row][X]) / ((df.iloc[row][X]) + (df[(df.baseSeq==df.iloc[row].baseSeq) & (df.charge==df.iloc[row].charge) & (df.baseSeq==df.modSeq)][X].item()))))
它可以工作,但是完整的数据集却要花很长时间(〜10分钟)(“行”循环通过650行,“ X”循环通过10个“区域”列)。
我还提出了令人讨厌的“ SettingWithCopyWarning”问题-我可以通过将.ascopy()
附加到最后一行的“链式分配”来避免这种情况吗?我从来没有想过。
我读过几次,应该尽可能避免循环,所以我猜这是问题所在。 有没有更清洁,更快捷的方法来做到这一点?
“ modSeq”和“ charge”的每个组合都是唯一的,所以也许我想对df.groupby()
或df.merge()
做些什么,并使用临时DF?
我经常遇到这种情况,因此我希望了解如何在不使用循环(或至少提高速度)的情况下进行此类操作。
答案 0 :(得分:0)
如果我们创建一些临时列,则可以对您的数学运算进行矢量化处理,这将大大减少时间。然后,我们可以删除临时列。
使用您给出的示例,它看起来像这样:
df
开始,如下所示: baseSeq modSeq charge modType area_0 area_25 area_50
0 ADTPICR ADT[+16]PICR 2 hydoxy 1862 2472 3015
1 ADTPICR ADTPICR 2 UNMOD 22737 30966 24660
2 AVALFAED AVALFAED[+16] 2 hydroxy 40060 2423 3553
3 AVALFAED AVALFAE[+16]D 2 hydroxy 40131 2407 3577
4 AVALFAED AVALFAED 2 UNMOD 21962 34387 29860
5 AVALFAED AVALFAED[-30] 3 decarbox 12 16 40
6 AVALFAED AVALFAED 3 UNMOD 21885 35444 33511
7 AVALFAED AVALFAED 4 UNMOD 2116 3072 2974
创建所有“区域”列的列表:
area_cols = df.columns[df.columns.str.contains('area')].tolist()
以下5行代码创建了一个临时df,其中包含每个baseSeq
/ charge
组合的未修改区域:
temp_df = df[['baseSeq'] + ['charge'] + ['modType'] + area_cols].groupby(['baseSeq', 'charge', 'modType'], axis=0).sum()
temp_df = temp_df.reset_index(level=2)
temp_df = temp_df[temp_df['modType'] == 'UNMOD']
temp_df = temp_df.drop('modType', axis=1)
temp_df.rename(columns = lambda x: 'unmod_' + x, inplace=True)
这是此时临时df的外观:
unmod_area_0 unmod_area_25 unmod_area_50
baseSeq charge
ADTPICR 2 22737 30966 24660
AVALFAED 2 21962 34387 29860
3 21885 35444 33511
4 2116 3072 2974
然后我们将此临时数据框重新加入主数据框,以使各列显示每个baseSeq
/ charge
组合的适当的未修改区域:>
df = df.join(temp_df, on=['baseSeq', 'charge'])
这时,我们的数据框如下所示:
baseSeq modSeq charge modType area_0 area_25 area_50 unmod_area_0 unmod_area_25 unmod_area_50
0 ADTPICR ADT[+16]PICR 2 hydoxy 1862 2472 3015 22737 30966 24660
1 ADTPICR ADTPICR 2 UNMOD 22737 30966 24660 22737 30966 24660
2 AVALFAED AVALFAED[+16] 2 hydroxy 40060 2423 3553 21962 34387 29860
3 AVALFAED AVALFAE[+16]D 2 hydroxy 40131 2407 3577 21962 34387 29860
4 AVALFAED AVALFAED 2 UNMOD 21962 34387 29860 21962 34387 29860
5 AVALFAED AVALFAED[-30] 3 decarbox 12 16 40 21885 35444 33511
6 AVALFAED AVALFAED 3 UNMOD 21885 35444 33511 21885 35444 33511
7 AVALFAED AVALFAED 4 UNMOD 2116 3072 2974 2116 3072 2974
for col in area_cols:
num = col.split('_')[1]
df['fracUnmod_' + num] = 1 - (df[col] / (df[col] + df['unmod_' + col]))
为清理起见,我们先删除显示未修改区域的临时列,然后删除临时数据框。
df = df.drop(['unmod_' + c for c in area_cols], axis=1)
del(temp_df)
最终数据帧如下:
baseSeq modSeq charge modType area_0 area_25 area_50 fracUnmod_0 fracUnmod_25 fracUnmod_50
0 ADTPICR ADT[+16]PICR 2 hydoxy 1862 2472 3015 0.924306 0.926072 0.891057
1 ADTPICR ADTPICR 2 UNMOD 22737 30966 24660 0.500000 0.500000 0.500000
2 AVALFAED AVALFAED[+16] 2 hydroxy 40060 2423 3553 0.354100 0.934175 0.893664
3 AVALFAED AVALFAE[+16]D 2 hydroxy 40131 2407 3577 0.353695 0.934582 0.893023
4 AVALFAED AVALFAED 2 UNMOD 21962 34387 29860 0.500000 0.500000 0.500000
5 AVALFAED AVALFAED[-30] 3 decarbox 12 16 40 0.999452 0.999549 0.998808
6 AVALFAED AVALFAED 3 UNMOD 21885 35444 33511 0.500000 0.500000 0.500000
7 AVALFAED AVALFAED 4 UNMOD 2116 3072 2974 0.500000 0.500000 0.500000
这与原始嵌套的for循环产生的输出相同。但希望应该更快。