如何用Python Pandas的pivot和melt转换指定DataFrame?
问题:用Pandas实现指定的DataFrame格式转换
原始DataFrame如下:
date price num_floors house 0 2023-01-01 94.30076 3 A 1 2023-01-01 95.58771 2 B 2 2023-01-02 102.78559 1 C 3 2023-01-03 93.29053 3 D
希望转换为以下格式:每列对应一个日期,列内前两行对应第一套房屋的price和num_floors,接下来两行对应第二套房屋,无数据位置用NaN填充:
2023-01-01 2023-01-02 2023-01-03 1 94.30076 102.78559 93.29053 2 3.00000 1.00000 3.00000 3 95.58771 NaN NaN 4 2.00000 NaN NaN
已用R实现该转换,代码如下:
df_trans <- df %>% pivot_longer(-date) %>% # 注:实际应排除house列,否则会引入无效数据 mutate(index=row_number(), .by = date) %>% pivot_wider(id_cols = index, names_from = date, values_from = value) %>% select(-index)
尝试用Python实现时未得到预期结果,Python代码如下:
df_trans = df.melt(id_vars='date') df_trans['n'] = df_trans.groupby('date').cumcount() + 1 df_trans = df_trans.pivot(index='n', columns='date', values='value')
解决方案
你的Python代码核心问题是没有排除house列,导致melt后引入了不需要的房屋名称数据,使得cumcount生成的索引完全偏离预期。以下是修正后的代码,完全对齐R代码的逻辑:
import pandas as pd # 构造原始DataFrame(如果已有数据可跳过此步) df = pd.DataFrame({ 'date': ['2023-01-01', '2023-01-01', '2023-01-02', '2023-01-03'], 'price': [94.30076, 95.58771, 102.78559, 93.29053], 'num_floors': [3, 2, 1, 3], 'house': ['A', 'B', 'C', 'D'] }) # 1. 移除不需要的house列,只保留待转换的字段 df_clean = df.drop('house', axis=1) # 2. 宽转长:将price和num_floors转为长格式 df_long = df_clean.melt(id_vars='date', var_name='attr', value_name='value') # 3. 按日期分组,生成组内连续索引(对应目标格式的行号) df_long['row_idx'] = df_long.groupby('date').cumcount() + 1 # 4. 长转宽:按行索引分组,日期作为列 df_trans = df_long.pivot(index='row_idx', columns='date', values='value') # 5. 调整显示格式:移除列名层级、索引名称 df_trans.columns.name = None df_trans.index.name = None print(df_trans)
代码说明
- 移除house列:目标格式不需要房屋名称,必须先剔除该列,避免干扰后续转换逻辑
- melt指定字段:仅对
price和num_floors做宽转长,保证长格式数据仅包含需要的属性值 - 组内生成索引:
groupby('date').cumcount() +1和R中row_number(), .by = date逻辑完全一致,给每个日期下的行分配从1开始的连续编号 - 透视转换:以生成的行索引为基准,将日期转为列,得到目标格式;最后调整显示格式,和示例输出对齐
运行后输出将完全符合预期,空值显示为Pandas默认的NaN,和示例中的NA等价。
内容的提问来源于stack exchange,提问作者jonas Chen
相关产品推荐
相关产品推荐

