我需要基于另一个框架生成一个数据框架。根据输入df有两个步骤。
输入df有4列。输出应通过以下方式完成:
1)从col1
取值以生成输出中的那么多行,其中col opt
被重写,new_col1
等于f"{value_from_col0}_{loop_iterator_with_limit_from_col1}"
,列src
等于'src1' 。
2)从col2
中取值,并用|
分隔。对于每个拆分元素,在输入df中找到它,从col0
中获取值,并以与1)类似的方式生成行。 src
等于“ src2”。
df = pd.DataFrame([
['opt1', 'a', 2, ''],
['opt2', 'b', 1, ''],
['opt9', 'z', 3, 'a|b'],
['opt8', 'y', 3, 'a']],
columns=['opt', 'col0', 'col1', 'col2'])
out = pd.DataFrame()
new_rows = []
for i, row in df.iterrows():
for j in range(row['col1']):
new_row = dict()
new_row['opt'] = row['opt']
new_row['new_col'] = f"{row['col0']}_{j+1}"
new_row['src'] = 'src1'
new_rows.append(new_row)
for s in row['col2'].split('|'):
if s:
col1_value = df.loc[df['col0'] == s]['col1'].values[0]
for k in range(col1_value):
new_row = dict()
new_row['opt'] = row['opt']
new_row['new_col'] = f"{s}_{k + 1}"
new_row['src'] = 'src2'
new_rows.append(new_row)
out = out.append(new_rows, ignore_index=True)
在下面您可以找到预期的输出。我使用了iterrows()
,它非常慢。我相信有一种更有效的方法可以实现相同的目标。当然,可以用不同的方式排序,没关系。
new_col opt src
0 a_1 opt1 src1
1 a_2 opt1 src1
2 b_1 opt2 src1
3 z_1 opt9 src1
4 z_2 opt9 src1
5 z_3 opt9 src1
6 a_1 opt9 src2
7 a_2 opt9 src2
8 b_1 opt9 src2
9 y_1 opt8 src1
10 y_2 opt8 src1
11 y_3 opt8 src1
12 a_1 opt8 src2
13 a_2 opt8 src2
答案 0 :(得分:1)
这是尝试使用更多矢量化熊猫函数的一种方法,特别是在pandas==0.25
中。它可能仍有改进的空间,但是与使用iterrows
相比,它显示出一些性能上的改进。使用的步骤是:
col2
展开:col2
重命名为col0
,与df
合并并附加到原始df上; repeat
以col1
的数量重复每一列下面的代码:
df['col2'] = df['col2'].str.split('|', n=-1, expand=False) #split string in col2
df['src'] = 'src1' #add src1 for original values
### Explode, change col names, merge and append.
df = pd.concat([
df.explode('col2')[['opt', 'col2']]\ #expand col2
.rename(columns={'col2': 'col0'})\ #rename to col0
.merge(df[['col0','col1']], on='col0'), #merge to get new col1
df], axis=0, sort=False).fillna('src2') #label second val to 'src2'
### Expand based on col1 values
new_df = pd.DataFrame(
pd.np.repeat(df.values,df['col1'],axis=0), columns=df.columns #repeat the values
).drop(['col1','col2'], axis=1)\
.sort_values(['opt','col0']).rename(columns={'col0':'new_col'})\
.reset_index(drop=True)
### Relabel new_col to append the order
new_df['new_col'] = new_df['new_col']+'_'+ \
(new_df.groupby(['opt','new_col']).cumcount()+1).map(str)
Out[1]:
opt new_col src
0 opt1 a_1 src1
1 opt1 a_2 src1
2 opt2 b_1 src1
3 opt8 a_1 src2
4 opt8 a_2 src2
5 opt8 y_1 src1
6 opt8 y_2 src1
7 opt8 y_3 src1
8 opt9 a_1 src2
9 opt9 a_2 src2
10 opt9 b_1 src2
11 opt9 z_1 src1
12 opt9 z_2 src1
13 opt9 z_3 src1
如果我们使用此数据帧的100倍来测试效率与iterrows
的对比,则如下:
df = pd.concat([df]*100, ignore_index=True)
%timeit generic(df) #using iterrows (your function)
#162 ms ± 722 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit generic1(df) #using the code above
#33 ms ± 240 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)