如何将循环分块的复杂Pandas DataFrame转换为长格式?
解决Excel分块DataFrame转长格式问题
先模拟可复现的示例数据
先构造一个简化版的分块数据,对应2个小时块(Hour 1和Hour 2),每个块8行,列包含Hour标识和BZ项(FI、NO2):
import pandas as pd import numpy as np # 构造模拟数据 data = [ # Hour 1块 ['Hour 1', 'FI', 'NO2'], ['D1', np.nan, np.nan], ['ND', np.nan, np.nan], ['2024-05-20', np.nan, np.nan], [np.nan, 12.3, 45.6], [np.nan, 13.1, 46.2], [np.nan, 12.8, 45.9], [np.nan, 13.5, 46.5], # Hour 2块 ['Hour 2', 'FI', 'NO2'], ['D2', np.nan, np.nan], ['Selected', np.nan, np.nan], ['2024-05-20', np.nan, np.nan], [np.nan, 22.1, 55.3], [np.nan, 22.5, 55.7], [np.nan, 22.3, 55.5], [np.nan, 22.7, 55.9] ] df = pd.DataFrame(data) print("原始分块DataFrame:") print(df)
核心处理步骤
因为数据是每8行一个小时块,且元数据(Horizon、type、Day)都在块内的特定行,不能直接用pd.melt(),需要先拆分块提取元数据,再单独重塑每个块:
1. 按8行拆分所有小时块
# 按每8行拆分,得到每个小时块的列表 block_size = 8 blocks = [df.iloc[i:i+block_size] for i in range(0, len(df), block_size)]
2. 处理单个块,提取元数据并转长格式
定义一个函数处理每个块:
def process_block(block): # 提取小时信息:从块首行第一列提取小时数(比如'Hour 1' -> 1) hour = int(block.iloc[0, 0].split()[-1]) # 提取Horizon:块的第2行第一列(索引1) horizon = block.iloc[1, 0] # 提取type:块的第3行第一列(索引2) type_val = block.iloc[2, 0] # 提取Day:块的第4行第一列(索引3) day = block.iloc[3, 0] # 提取数值行:块的第5到第8行(索引4到7),以及BZ列(索引1到末尾) value_rows = block.iloc[4:, 1:] # 设置BZ列为列名 value_rows.columns = block.iloc[0, 1:].tolist() # 转长格式:把BZ列转成行,对应Value melted = value_rows.melt(var_name='BZ', value_name='Value').dropna(subset=['Value']) # 添加元数据列 melted['Horizon (D1 or D2)'] = horizon melted['type'] = type_val melted['Day'] = day # 生成Datetime:Day + Hour(假设小时是整点) melted['Datetime'] = pd.to_datetime(melted['Day']) + pd.Timedelta(hours=hour) # 调整列顺序为要求的格式 return melted[['Datetime', 'BZ', 'type', 'Horizon (D1 or D2)', 'Value', 'Day']]
3. 合并所有块的结果
# 处理所有块并合并 final_df = pd.concat([process_block(block) for block in blocks], ignore_index=True) print("\n转换后的长格式DataFrame:") print(final_df)
输出结果说明
转换后的DataFrame完全符合要求的列结构:
Datetime:自动拼接日期和小时生成时间戳BZ:对应原始的FI、NO2等标识type:提取块内的ND/SelectedHorizon (D1 or D2):提取块内的D1/D2Value:所有数值数据Day:原始的日期信息
关键要点
- 分块处理是核心:因为数据是按固定行数循环的,先拆分才能单独提取每个小时的元数据
- 元数据提取:每个块的第2-4行分别对应Horizon、type、Day,需要单独提取后合并到数值行中
- 重塑时机:先提取数值部分再用
melt(),而不是直接对整个大DataFrame操作,避免元数据和数值混在一起导致的错误
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

