悠悠楠杉
PandasDataFrame多列堆叠与重塑技巧
假设你正在处理一批从CMS导出的文章数据,原始DataFrame长这样:
python
import pandas as pd
data = {
'id': [1, 2],
'title1': ['如何学习Python', '数据分析入门'],
'title2': ['高效掌握Pandas', None],
'keywords1': ['Python, 学习', '数据分析, 教程'],
'keywords2': ['Pandas, 技巧', None],
'desc1': ['一篇适合初学者的指南', '带你理解基础概念'],
'desc2': ['进阶用法详解', None],
'content1': ['Python是……', '数据清洗是……'],
'content2': ['DataFrame操作很关键', None]
}
df = pd.DataFrame(data)
可以看到,每个字段都按编号分成了两列,这种“宽格式”虽然便于展示,但不利于批量处理。我们的目标是将所有文章内容整合为标准的长格式:每条记录对应一个标题、一组关键词、一段描述和正文,按实际存在内容的条目拆分。
首先,我们可以利用pd.wide_to_long函数进行初步重塑。这个方法特别适合处理带有规则后缀的列名。但在此之前,我们需要对列名稍作调整,使其符合该函数的命名规范。
python
重命名列以匹配 widetolong 的要求
df.columns = [
'id', 'title1', 'title2',
'keywords1', 'keywords2',
'desc1', 'desc2',
'content1', 'content2'
]
使用 widetolong 进行转换
dflong = pd.widetolong( df, stubnames=['title', 'keywords', 'desc', 'content'], i='id', j='seq' ).resetindex()
此时,DataFrame已经被拉长,每一行代表一个id对应的某一序号的内容片段。接下来,我们需要清理掉那些空值记录。由于原始数据中部分字段为空(如第二条记录的title2),这些行并无实际意义。
python
删除 content 为空的行(假设 content 是核心字段)
dfclean = dflong.dropna(subset=['content']).reset_index(drop=True)
但有时候,我们面对的并不是如此规整的列结构。例如,某些字段可能是section_title_A、section_desc_B这类混合命名。这时,更灵活的做法是手动构造一个新表,通过遍历原始列来进行堆叠。
考虑以下策略:识别出所有以特定前缀开头的列,按位置对齐合并。我们可以先提取所有相关列,再按行逐个重组。
python
手动堆叠非规则列
prefixes = ['title', 'keywords', 'desc', 'content']
max_cols = 2 # 假设最多有两组
rows = []
for idx, row in df.iterrows():
for i in range(1, maxcols + 1):
title = row.get(f'title{i}')
keywords = row.get(f'keywords{i}')
desc = row.get(f'desc{i}')
content = row.get(f'content_{i}')
if pd.notna(content): # 只保留有正文的条目
rows.append({
'id': row['id'],
'title': title,
'keywords': keywords,
'description': desc,
'body': content
})
df_final = pd.DataFrame(rows)
这种方法虽然代码略多,但逻辑清晰,适应性强,尤其适用于列名不规范或数量不一的情况。
完成重塑后,你会发现数据结构变得极为简洁:每一行就是一个完整的信息单元。这不仅方便后续的文本分析、关键词提取或导入数据库,也为可视化和报告生成打下良好基础。
此外,在真实项目中,这类操作往往不是一次性的。你可以将其封装成函数,配合配置文件定义字段前缀和最大列数,实现通用化处理。例如:
python
def stack_content_columns(df, id_col, field_prefixes, max_suffix):
rows = []
for _, row in df.iterrows():
for i in range(1, max_suffix + 1):
record = {id_col: row[id_col]}
has_content = False
for prefix in field_prefixes:
val = row.get(f"{prefix}_{i}")
record[prefix.rstrip('_')] = val
if prefix == 'content_' and pd.notna(val):
has_content = True
if has_content:
rows.append(record)
return pd.DataFrame(rows)
通过合理运用Pandas的堆叠与重塑能力,原本杂乱无章的宽表可以被转化为结构清晰、语义明确的长表。这不仅是技术操作,更是数据思维的体现——把混乱归于秩序,让信息真正服务于分析与决策。
