根据部分匹配的列名插入多列

insert multiple columns based on column name with partial match

我认为这里会有一个简单的解决方案,但我遗漏了一些东西...

我有两个数据框。一份原件,另一份带有一些处理过的数据。第二个数据框中的列名部分匹配第一个数据框中的列。有多个列名部分匹配。第二个数据框中的多个列需要重新插入到原始数据框中具有单个部分匹配列名称的相同位置(因此数据框的大小自然会增加)。下面复制了数据示例和预期输出。它必须是 'programmatic' 因为我有很多 columns/instances.

任何评论对我都非常有帮助。

非常感谢!

原始数据

df1 = {'id': ['1','2','3','4'],
        'ab': ['1', '2', '3', '4'],
        'xy': ['1', '2', '3','4'],
        'cd': ['1', '2', '3','4'],
        'ef':['1', '2', '3', '4'],
        'lm':['1', '2', '3', '4'],
        'fa':['1', '2', '3', '4'] }

df1 = pd.DataFrame(df1, columns = ['id','ab', 'xy', 'cd', 'ef', 'lm', 'fa'])

已处理数据

df2 = {'id': ['1','2','3','4'],
        'ab? op':  ['green', 'red', 'blue', 'None'],
        'ab? 1': ['red', 'yellow', 'None', 'None'],
        'cd': ['L', 'XL', 'M','L'],
        'efab? cba' : ['husband', 'wife', 'husband', 'None'],
        'efab? 1':['son', 'grandparent', 'son', 'None'],
        'efab? 2':['None', 'son', 'None', 'None'],
        'fab? 4':['9', '10', '5', '3'], 
        'fab? po':['England', 'Scotland', 'Wales', 'NA'] }

df2 = pd.DataFrame(df2, columns = ['id','ab? op', 'ab? 1', 'cd', 'efab? cba', 'efab? 1', 'efab? 2', 'fab? 4', 'fab? po'])

预期输出

e = {'id': ['1','2','3','4'],
        'ab? op':  ['green', 'red', 'blue', 'None'],
        'ab? 1': ['red', 'yellow', 'None', 'None'],
        'xy': ['1', '2', '3','4'], 
        'cd': ['L', 'XL', 'M','L'],
        'lm':['1', '2', '3', '4'], 
        'efab? cba' : ['husband', 'wife', 'husband', 'None'],
        'efab? 1':['son', 'grandparent', 'son', 'None'],
        'efab? 2':['None', 'son', 'None', 'None'],
        'fab? 4':['9', '10', '5', '3'], 
        'fab? po':['England', 'Scotland', 'Wales', 'NA'] }

expected = pd.DataFrame(e, columns = ['id','ab? op', 'ab? 1', 'xy', 'cd', 'lm', 'efab? cba', 'efab? 1', 'efab? 2', 'fab? 4', 'fab? po'])

将两个差异与修改后的列名称

结合后 argsort
c2 = df2.columns.str[:2]
c1 = df1.columns
x = pd.concat([df2,df1[c1[~c1.isin(c2)]]],axis=1)
x = x.iloc[:,x.columns.str[:2].map(dict(zip(c1,range(len(c1))))).argsort()]
x
Out[115]: 
  id ab? op   ab? 1 xy  cd efab? cba      efab? 1 efab? 2 lm fab? 4   fab? po
0  1  green     red  1   L   husband          son    None  1      9   England
1  2    red  yellow  2  XL      wife  grandparent     son  2     10  Scotland
2  3   blue    None  3   M   husband          son    None  3      5     Wales
3  4   None    None  4   L      None         None    None  4      3        NA

如果Python 3.8+,那么

result = pd.concat([df1[col]
                    if (candidate := df2.loc[:, df2.columns.str.startswith(col)]).empty
                    else candidate
                    for col in df1],
                   axis=1)

对于 df1 的每一列,我们在 df2 中查找 startswith 列名称在 df1 中的 candidate 列。如果存在这样的列,则将候选人放入结果中,否则将该列保留在 df1.

获得

  id ab? op   ab? 1 xy  cd efab? cba      efab? 1 efab? 2 lm fab? 4   fab? po
0  1  green     red  1   L   husband          son    None  1      9   England
1  2    red  yellow  2  XL      wife  grandparent     son  2     10  Scotland
2  3   blue    None  3   M   husband          son    None  3      5     Wales
3  4   None    None  4   L      None         None    None  4      3        NA

如果 3.8-,

cols = []
for col in df1:
    candidate = df2.loc[:, df2.columns.str.startswith(col)]
    cols.append(df1[col] if candidate.empty else candidate)

result = pd.concat(cols, axis=1)