根据另一个数据框生成熊猫数据框

时间:2019-08-01 19:22:27

标签: python pandas dataframe

我需要基于另一个框架生成一个数据框架。根据输入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

1 个答案:

答案 0 :(得分:1)

这是尝试使用更多矢量化熊猫函数的一种方法,特别是在pandas==0.25中。它可能仍有改进的空间,但是与使用iterrows相比,它显示出一些性能上的改进。使用的步骤是:

  1. 用分割的字符串将col2展开:
  2. col2重命名为col0,与df合并并附加到原始df上;
  3. 使用pandas或numpy repeatcol1的数量重复每一列

下面的代码:

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)