You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多期调查数据宽表转长表:高效实现当期与上期数据转换

高效转换调查数据宽表为长表方案

我有一个名为df的调查数据DataFrame,列名中r/h后的数字代表调查波次/期数,包含age、smoke、income等变量(实际数据最多含100个变量,受访者可达50000人,部分变量以h/s开头)。需要将该宽表转换为长表:每个受访者对应(总期数-1)条记录,每条记录包含respondent_id、t_1_period(上期期数),以及各变量的当期值(t_前缀)和上期值(t_1_前缀)。目前用循环方法处理大数据量时速度极慢,求更高效的转换方案。

示例输入数据

respondent_idr1ager2ager3ager4ager1smoker2smoker3smoker4smoker1incomer2incomer3incomer4income
1617835384144111160626870
16171965687174000150525456
16172047505356010180828587

期望输出数据

respondent_idt_1_periodt_aget_1_aget_smoket_1_smoket_incomet_1_income
1617813835116260
1617824138116862
1617834441117068
16171916865005250
16171927168005452
16171937471105654
16172015047108280
16172025350018582
16172035653108785

当前低效代码

df = pd.melt(df, id_vars=["respondent_id"])

variable_names = ["age", "smoke", "income"]

new_rows = []
for respondent_id in df["respondent_id"].unique():
  df_temp = df[df["respondent_id"] == respondent_id]

  for i in range(2, 5):
    new_row = {"respondent_id": respondent_id, "t_1_period": i-1}
    for var in variable_names:
      if var not in ["income"]:
        current_var = f"r{i}{var}"
        previous_var = f"r{i-1}{var}"
        new_row[f"t_{var}"] = df_temp[df_temp["variable"] == current_var]["value"].values[0]
        new_row[f"t_1_{var}"] = df_temp[df_temp["variable"] == previous_var]["value"].values[0]
      elif var == "income":
        current_var = f"h{i}{var}"
        previous_var = f"h{i-1}{var}"
        new_row[f"t_h{var}"] = df_temp[df_temp["variable"] == current_var]["value"].values[0]
        new_row[f"t_1_h{var}"] = df_temp[df_temp["variable"] == previous_var]["value"].values[0]

    new_rows.append(new_row)

df_periods = pd.DataFrame(new_rows)

高效转换方案

用pandas的矢量化操作替代循环,全程利用内置函数处理,速度能提升数十倍,还能自动适配所有变量,不用手动维护变量列表。具体做法:

  1. 拆分列名,提取前缀(r/h/s)、期数和变量名;
  2. 把宽表转成基础长表,统一存储所有变量的各期数据;
  3. 按受访者+变量分组,用shift获取上期数据;
  4. 过滤掉没有上期数据的第一期记录;
  5. 把长表重新转回宽表,整理成目标字段格式。

代码实现:

import pandas as pd

# 解析列名:提取前缀、期数、变量名
def parse_column(col_name):
    if col_name.startswith(('r', 'h', 's')):
        prefix = col_name[0]
        period = int(col_name[1])
        var_name = col_name[2:]
        return prefix, period, var_name
    return None, None, None

# 1. 把宽表转为长表,拆分变量信息
df_melted = df.melt(id_vars='respondent_id', var_name='full_var', value_name='current_value')
df_melted[['prefix', 'period', 'var']] = df_melted['full_var'].apply(lambda x: pd.Series(parse_column(x)))

# 2. 按受访者和变量分组,获取上期值和上期期数
df_melted['prev_value'] = df_melted.groupby(['respondent_id', 'var'])['current_value'].shift(1)
df_melted['prev_period'] = df_melted.groupby(['respondent_id', 'var'])['period'].shift(1)

# 3. 去掉没有上期数据的行(即第一期)
df_melted = df_melted.dropna(subset=['prev_value'])

# 4. 重塑为宽表,整理成目标列名
df_pivoted = df_melted.pivot_table(
    index=['respondent_id', 'prev_period'],
    columns=['var'],
    values=['current_value', 'prev_value'],
    aggfunc='first'
)

# 重命名列,符合t_xxx和t_1_xxx的格式
df_pivoted.columns = [
    f"t_{var}" if level == 'current_value' else f"t_1_{var}"
    for level, var in df_pivoted.columns
]

# 重置索引,调整列顺序
df_result = df_pivoted.reset_index().rename(columns={'prev_period': 't_1_period'})
target_order = ['respondent_id', 't_1_period'] + sorted([col for col in df_result.columns if col not in ['respondent_id', 't_1_period']])
df_result = df_result[target_order]

方案优势

  • 速度快:矢量化操作完全替代Python循环,处理5万条数据的效率远超原代码;
  • 通用性强:自动识别所有变量,新增变量无需修改代码;
  • 兼容性好:支持r/h/s等不同前缀的变量,自动处理期数匹配。

内容的提问来源于stack exchange,提问作者caproki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 19:44:50