基于段落类型的Pandas DataFrame列条件更新问题
Pandas DataFrame按Heading1区间处理document_section_id问题
问题需求
- 以
paragraph_type列中的Heading1为分界,划分行区间(从一个Heading1到下一个Heading1) - 每个区间取第一个
Heading1对应的document_section_id作为基准值var1 - 区间内所有
document_section_id若为字符串且不以var1开头,替换为空字符串
原始数据
import pandas as pd data = { 'document_section_id': ['1', '1.1', None, None, None, None, None, None, None, '3', None, None, None, '4.1', None, None, None, None, None, None, None, None, None, None, None, None, None, None, '1.2', None, None, None, None, None, None, None, '1.3', '1.3.1', None, None, '1.3.2', None, None, None, None, '1.3.3', None, None, None, '1.3.4', None, None,None], ' ': ['Heading1', 'Heading2', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'TblFootnote', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'ListParagraph', 'ListParagraph', 'ListParagraph', 'ListParagraph', 'ListParagraph', 'ListParagraph', 'ListParagraph', 'ListParagraph', 'ListParagraph', 'ListParagraph', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'Heading2', 'HeadingNoTOC', 'TblFootnote', 'TblFootnote', 'TblFootnote', 'TblFootnote', 'TblFootnote', 'TblFootnote', 'Heading2', 'HeadingNoTOC', 'Heading3', 'HeadingNoTOC', 'Heading3', 'HeadingNoTOC', 'HeadingNoTOC', 'Heading3', 'HeadingNoTOC', 'Heading3', 'TblFootnote', 'Heading3', 'Heading3', 'Heading3', 'Heading1'] } df = pd.DataFrame(data) # 重命名空格列,避免操作麻烦 df = df.rename(columns={' ': 'paragraph_type'})
原代码问题分析
你尝试的代码存在以下问题:
- 列名不匹配:代码中用了
PARAGRAPH_TYPE、SECTION_ID,但实际列名是paragraph_type、document_section_id,大小写和名称都不对 - 索引混乱:
reset_index()后原索引失效,导致loc[j]无法正确定位行 - 效率低下:嵌套循环遍历行不符合Pandas矢量化操作的最优思路
解决方案代码
import pandas as pd # 原始数据处理(同上) data = { 'document_section_id': ['1', '1.1', None, None, None, None, None, None, None, '3', None, None, None, '4.1', None, None, None, None, None, None, None, None, None, None, None, None, None, None, '1.2', None, None, None, None, None, None, None, '1.3', '1.3.1', None, None, '1.3.2', None, None, None, None, '1.3.3', None, None, None, '1.3.4', None, None,None], ' ': ['Heading1', 'Heading2', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'TblFootnote', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'ListParagraph', 'ListParagraph', 'ListParagraph', 'ListParagraph', 'ListParagraph', 'ListParagraph', 'ListParagraph', 'ListParagraph', 'ListParagraph', 'ListParagraph', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'HeadingNoTOC', 'Heading2', 'HeadingNoTOC', 'TblFootnote', 'TblFootnote', 'TblFootnote', 'TblFootnote', 'TblFootnote', 'TblFootnote', 'Heading2', 'HeadingNoTOC', 'Heading3', 'HeadingNoTOC', 'Heading3', 'HeadingNoTOC', 'HeadingNoTOC', 'Heading3', 'HeadingNoTOC', 'Heading3', 'TblFootnote', 'Heading3', 'Heading3', 'Heading3', 'Heading1'] } df = pd.DataFrame(data) df = df.rename(columns={' ': 'paragraph_type'}) # 1. 生成Heading1分组标记:每遇到一个Heading1,分组号递增 df['group'] = df['paragraph_type'].eq('Heading1').cumsum() # 2. 为每个分组匹配对应的基准var1 group_vars = df[df['paragraph_type'] == 'Heading1'].set_index('group')['document_section_id'] df['var1'] = df['group'].map(group_vars) # 3. 批量更新document_section_id def update_section(row): section_id = row['document_section_id'] var1 = row['var1'] if isinstance(section_id, str) and var1 is not None: if not section_id.startswith(str(var1)): return '' return section_id df['document_section_id'] = df.apply(update_section, axis=1) # 清理临时辅助列 df = df.drop(columns=['group', 'var1']) # 输出结果(与期望格式一致) print(df.to_dict(orient='list'))
代码说明
- 分组划分:用
cumsum()自动生成连续的分组ID,精准划分每个Heading1到下一个Heading1的区间 - 基准值匹配:通过
map将每个分组的基准var1关联到组内所有行,无需手动循环 - 批量更新:用
apply逐行判断并替换,逻辑清晰,也可进一步优化为矢量化操作提升效率
运行后得到的结果与你提供的期望输出完全一致,document_section_id中不符合条件的'3'、'4.1'均被替换为空字符串。
内容的提问来源于stack exchange,提问作者Dcook
相关产品推荐
相关产品推荐

