序列数据重塑:宽格式与长格式
很多做社会序列分析的工具,只“认”宽格式的数据。
👉 啥是宽格式?
打个比方,就像给每个人做一张“时间记录表”:
一个人占一行,每一列对应一个时间点(比如 2000 年、2001 年),一眼就能看到某个人所有时间点的状态。
❌ 宽格式的“不趁手”场景
真要干活的时候,宽格式就有点容易掉链子:
- 想画个趋势图?(比如看 A 和 B 两个人
2000-2002年的状态变化)- 绘图工具(比如
matplotlib、seaborn)大多只认“一行一个时间点”的数据,宽格式的列太多,工具根本不知道“哪个是时间、哪个是状态”,画不出图;
- 绘图工具(比如
- 想把这份数据和其他表合并?(比如合并“每个人的年龄信息”)
- 宽格式里一个人占一行,看似方便,但如果其他表是“一行一个时间点”的格式,合并时很容易出错,得手动调整半天;
- 想做统计建模?(比如分析“时间和状态的关系”)
- 模型需要“每个观测值占一行”—— 宽格式里一个人一行包含多个时间点的观测,模型读不懂,会直接报错。
✅ 长格式:解决痛点的“适配款”
这时候长格式就派上用场了:
- 核心规则:一个个体的一个时间点,占一行;
- 固定列数:不管多少数据,都只有
个体 ID、时间、状态三列; - 工具友好:绘图、合并、建模时,工具能直接“抓”到关键信息,不用你手动拆数据、改格式。
📌 关键原则:没有“更好”,只有“更适配”
没有说哪种格式更好,关键看你要做啥:
- 用宽格式:看数据、整理原始信息(直观);
- 用长格式:干活(绘图、建模);
- 后续要做序列分析,再转回宽格式就行。
🎯 这篇指南能帮你啥?
不用手动复制粘贴改格式,用两个现成的辅助函数,点几下就能在宽、长格式之间切换,省时间还不容易出错。
宽格式
宽格式中,每一行代表一个个体,每一列代表一个时间点:
# 宽格式示例
# 导入pandas库(处理表格数据的常用工具)
import pandas as pd
df = pd.DataFrame({
"Entity ID": ["A", "B"], # 每个个体的唯一标识(比如人、样本的编号)
"2000": ["M", "L"], # 2000年对应的状态(比如等级、类别)
"2001": ["H", "M"], # 2001年对应的状态
"2002": ["H", "H"] # 2002年对应的状态
})宽格式数据展示:
| Entity ID | 2000 | 2001 | 2002 |
|---|---|---|---|
| A | M | H | H |
| B | L | M | H |
转换为长格式
用 wide_to_long_format_data()函数:
# 从sequenzo工具库导入转换函数
from sequenzo.utils import wide_to_long_format_data
df_long = wide_to_long_format_data(
df, # 输入的宽格式数据表(就是上面创建的df)
id_col="Entity ID", # 个体标识列的名称(这里是“个体ID”)
time_cols=["2000", "2001", "2002"], # 代表时间点的列名(要转成时间列的那些列)
var_name="year", # 新生成的“时间列”叫什么名字(这里设为“year”,即年份)
value_name="state" # 新生成的“状态列”叫什么名字(这里设为“state”,即状态)
)转换后的长格式数据展示:
| Entity ID | year | state |
|---|---|---|
| A | 2000 | M |
| A | 2001 | H |
| A | 2002 | H |
| B | 2000 | L |
| B | 2001 | M |
| B | 2002 | H |
id_col:个体标识列(比如示例中的"Entity ID"),作用是区分不同的分析对象(比如A和B是两个不同的个体)。time_cols:要转成“时间”的列名列表(比如示例中的 ["2000", "2001", "2002"]),这些列原本是宽格式里的时间点。var_name:新生成的时间列的名称(默认叫"time"),比如数据是按年份记录的,就可以设为"year"。value_name:新生成的状态列的名称(默认叫"state"),用来存每个时间点对应的具体状态(比如等级、类别)。
转回宽格式
如果后续需要用宽格式数据,用 long_to_wide_format_data() 函数就能转回去:
# 从sequenzo工具库导入转换函数
from sequenzo.utils import long_to_wide_format_data
df_wide = long_to_wide_format_data(
df_long, # 输入的长格式DataFrame
id_col="Entity ID", # 个体标识列的名称
time_col="year", # 时间列的名称(对应长格式中的时间列)
state_col="state" # 状态值列的名称(对应长格式中的状态列)
)运行后会得到和原始数据一样的宽格式 DataFrame。
当然!这里有针对 var_name 和 value_name 两个参数在上下文中的英文解释(宽转长格式转换场景下):
深入理解长宽格式转化中的 var_name 和 value_name 参数
用 pandas.melt() 或者 wide_to_long_format_data() 这两个函数,把序列数据从宽格式改成长格式时,var_name 和 value_name 这两个参数用来决定长格式结果里“新增列叫什么名字”。
var_name="time":给“时间列”起名字
这个参数设置“新时间列”的名称,这一列会存放原来宽格式中的列名(通常是时间跨度,比如年份、年龄等)。
举个例子:
# 宽格式输入
ID 2000 2001 2002
A M H H
B L M H转为长格式后:
ID time state
A 2000 M
A 2001 H
A 2002 H
B 2000 L
B 2001 M
B 2002 H这里的 "time" 就是新的时间列的名称,里面存的是原来的列名 2000、 2001、 2002。你可以根据数据特点改成更贴切的名字,比如 "year"(年份)或 "age"(年龄)。
value_name="state"
这个参数用来设定新状态列的名称。这一列里存的内容,是原来宽格式中“单元格里的实际值”(也就是每个时间点对应的状态)。
比如你的数据记录的是 "婚姻状态",就可以把 value_name 设为 "marital_status",这样一看列名就知道存的是什么,不用猜:
举个例子:
要是你的数据记的是婚姻状态(marital status)随时间变化的情况,你可以这么弄:
df_long = wide_to_long_format_data(
df,
id_col="person_id", # 个体标识列(这里叫"person_id",即人员 ID )
time_cols=["2000", "2001", "2002"], # 时间列(年份)
var_name="year", # 时间列名设为"year"(年份)
value_name="marital_status" # 状态列名设为"marital_status"(婚姻状态)
)这样就会得到:
person_id year marital_status
A 2000 Single
A 2001 Married
A 2002 Married
B 2000 Single
...总结
| 参数名称 | 意义(存什么、叫什么) | 常见取值(根据数据选) |
|---|---|---|
var_name | 时间列的名称(数据来自原宽格式里“代表时间的列名”,比如 2000、2001) | "time"(时间)、"year"(年份)、"age"(年龄) |
value_name | 状态列的名称(数据来自原宽格式里“单元格里的实际值”,比如 M、H) | "state"(状态)、"marital_status"(婚姻状态)、"grade"(等级) |
这两个参数仅用于设定长格式中列的名称,没有固定要求,只要符合你的研究场景、能让自己和别人看懂就行。
作者:梁彧祺
翻译:杨子婷