如何在Pandas中将动态列映射的时间序列数据对齐至固定团队列
Pandas 转换动态排名的时间序列数据
问题背景
原始数据是交替排列的时间值行和列映射行:
- 索引为偶数的行(0、2、4...)是实际的时间与分数数据,但列名随排名变化,不再对应固定团队
- 索引为奇数的行(1、3...)是对应下一行数据的列名映射,说明当前列对应的实际团队
原始数据示例:
| time | Team A | Team B | Team C |
|---|---|---|---|
| 14:00:00 | 2pts | 0pts | 0pts |
| time | Team B | Team A | Team C |
| 14:01:00 | 3pts | 2pts | 0pts |
| time | Team B | Team A | Team C |
| 14:02:00 | 3pts | 2pts | 2pts |
需要转换为固定列名(按Team A/B/C)的结构,让每个团队的分数对应到固定列:
| time | Team A | Team B | Team C |
|---|---|---|---|
| 14:00:00 | 2pts | 0pts | 0pts |
| 14:01:00 | 2pts | 3pts | 0pts |
| 14:02:00 | 2pts | 3pts | 2pts |
解决方案
推荐创建新的DataFrame处理,避免破坏原始数据。具体步骤如下:
1. 构造示例数据
先模拟原始的DataFrame:
import pandas as pd data = [ ["14:00:00", "2pts", "0pts", "0pts"], ["time", "Team B", "Team A", "Team C"], ["14:01:00", "3pts", "2pts", "0pts"], ["time", "Team B", "Team A", "Team C"], ["14:02:00", "3pts", "2pts", "2pts"] ] df = pd.DataFrame(data, columns=["time", "Team A", "Team B", "Team C"])
2. 拆分值行与映射行
把数据分成实际的时间分数行,和列名映射行:
# 提取带时间的数值行 value_rows = df[df["time"] != "time"].reset_index(drop=True) # 提取映射行(去掉time列,只保留团队映射) map_rows = df[df["time"] == "time"].drop("time", axis=1).reset_index(drop=True)
3. 处理每行数据,对齐到固定列
遍历每个需要调整的值行,根据对应的映射行重新分配分数,最后合并结果:
# 初始化结果列表,先加入第一行(无需映射) result = [value_rows.iloc[0].to_dict()] # 从第二行开始处理,每个值行对应一个映射行 for i in range(1, len(value_rows)): # 当前值行的数据(去掉time) current_values = value_rows.iloc[i].drop("time") # 对应的映射行(列位置 -> 团队名称) current_map = map_rows.iloc[i-1] # 构建新的行:时间 + 按固定团队列对齐的分数 new_row = { "time": value_rows.iloc[i]["time"], **dict(zip(current_map, current_values)) } result.append(new_row) # 转换为最终DataFrame final_df = pd.DataFrame(result) # 按固定列顺序排序(可选,确保列顺序正确) final_df = final_df[["time", "Team A", "Team B", "Team C"]]
4. 查看结果
运行后final_df就是目标结构:
time Team A Team B Team C 0 14:00:00 2pts 0pts 0pts 1 14:01:00 2pts 3pts 0pts 2 14:02:00 2pts 3pts 2pts
关键说明
- 用新DataFrame处理更安全,原始数据可保留用于核对
- 若团队数量多或数据量大,该方法依然适用,核心是通过映射行建立列位置和团队的对应关系
- 若映射行和值行的对应关系有变化,只需调整循环中的索引逻辑即可
内容的提问来源于stack exchange,提问作者user25994325
相关产品推荐
相关产品推荐

