如何用正则表达式合并DataFrame中符合规则的多列?
pandas列合并解决方案
问题背景
我有如下所示的pandas DataFrame:
import pandas as pd df = pd.DataFrame( {'number_C1_E1': ['1', '2', None, None, '5', '6', '7', '8'], 'fruit_C11_E1': ['apple', 'banana', None, None, 'watermelon', 'peach', 'orange', 'lemon'], 'name_C111_E1': ['tom', 'jerry', None, None, 'paul', 'edward', 'reggie', 'nicholas'], 'number_C2_E2': [None, None, '3', None, None, None, None, None], 'fruit_C22_E2': [None, None, 'blueberry', None, None, None, None, None], 'name_C222_E2': [None, None, 'anthony', None, None, None, None, None], 'number_C3_E1': [None, None, '3', '4', None, None, None, None], 'fruit_C33_E1': [None, None, 'blueberry', 'strawberry', None, None, None, None], 'name_C333_E1': [None, None, 'anthony', 'terry', None, None, None, None], } )
合并规则
- 若某列去除
_C{0~9}、_C{0~9}{0~9}或_C{0~9}{0~9}{0~9}后缀后与另一列名称相同,则这两列可合并。
以
number_C1_E1、number_C2_E2、number_C3_E1为例,number_C1_E1和number_C3_E1去除_C{0~9}后均为number_E1,因此可合并。
- 合并后的列需要去除
None值。
期望结果
number_C1_1_E1 fruit_C11_1_E1 name_C111_1_E1 number_C2_1_E2 fruit_C22_1_E2 name_C222_1_E2 0 1 apple tom None None None 1 2 banana jerry None None None 2 3 blueberry anthony 3 blueberry anthony 3 4 strawberry terry None None None 4 5 watermelon paul None None None 5 6 peach edward None None None 6 7 orange reggie None None None 7 8 lemon nicholas None None None
解决方案
可以通过正则表达式分组列名,再对每组列合并去空,最后调整列名实现需求,具体代码和步骤如下:
完整实现代码
import pandas as pd import re # 原始DataFrame df = pd.DataFrame( {'number_C1_E1': ['1', '2', None, None, '5', '6', '7', '8'], 'fruit_C11_E1': ['apple', 'banana', None, None, 'watermelon', 'peach', 'orange', 'lemon'], 'name_C111_E1': ['tom', 'jerry', None, None, 'paul', 'edward', 'reggie', 'nicholas'], 'number_C2_E2': [None, None, '3', None, None, None, None, None], 'fruit_C22_E2': [None, None, 'blueberry', None, None, None, None, None], 'name_C222_E2': [None, None, 'anthony', None, None, None, None, None], 'number_C3_E1': [None, None, '3', '4', None, None, None, None], 'fruit_C33_E1': [None, None, 'blueberry', 'strawberry', None, None, None, None], 'name_C333_E1': [None, None, 'anthony', 'terry', None, None, None, None], } ) # 提取列的分组键:前缀+后缀(去掉中间_C加1-3位数字的部分) def get_group_key(col): match = re.match(r'^(.*)_C\d{1,3}(.*)$', col) if match: return (match.group(1), match.group(2)) return col # 按分组键归类所有列 column_groups = {} for col in df.columns: key = get_group_key(col) if key not in column_groups: column_groups[key] = [] column_groups[key].append(col) # 合并每组内的列,取每行第一个非空值 merged_columns = [] for (prefix, suffix), cols in column_groups.items(): # 用bfill/ffill实现逐行取非空值 merged_col = df[cols].bfill(axis=1).iloc[:, 0] # 生成符合期望格式的新列名 new_col_name = re.sub(r'_C(\d{1,3})', r'_C\1_1', cols[0]) merged_columns.append(pd.DataFrame({new_col_name: merged_col})) # 合并所有结果并调整列顺序 final_result = pd.concat(merged_columns, axis=1) desired_col_order = ['number_C1_1_E1', 'fruit_C11_1_E1', 'name_C111_1_E1', 'number_C2_1_E2', 'fruit_C22_1_E2', 'name_C222_1_E2'] final_result = final_result[desired_col_order] print(final_result.to_string())
代码说明
- 分组列名:通过正则表达式
^(.*)_C\d{1,3}(.*)$解析列名,把number_C1_E1和number_C3_E1这类具有相同前缀+后缀的列归为一组。 - 合并去空:对每组列使用
bfill(axis=1)填充空值,再取第一列,得到每行的非空值。 - 调整列名:用正则表达式将原列名中的
_C数字替换为_C数字_1,匹配期望的列名格式。 - 调整顺序:按照示例结果的列顺序重新排列最终DataFrame的列。
运行代码后即可得到与示例一致的输出结果。
内容的提问来源于stack exchange,提问作者haojie
相关产品推荐
相关产品推荐

