如何调整Pandas DataFrame至指定行列结构?求解决方案
问题描述
我尝试将嵌套字典转换为特定格式的Pandas DataFrame,但输出不符合预期。
原始字典:
from decimal import Decimal import pandas as pd options_pnl={'BTC': {'Pnl(since 6pm)': Decimal('7831.52228528'), 'Pnl(4 hr)': Decimal('2930.47450133'), 'Pnl(1 hr)': Decimal('1416.81306308'), 'Volume(since 6pm)': Decimal('24509290.62181862'), 'Volume(4 hr)': Decimal('4504202.83422724'), 'Volume(1 hr)': Decimal('1067850.01837278')}, 'ETH': {'Pnl(since 6pm)': Decimal('387.87564823'), 'Pnl(4 hr)': Decimal('-349.14871930'), 'Pnl(1 hr)': Decimal('656.74824550'), 'Volume(since 6pm)': Decimal('10700784.53262117'), 'Volume(4 hr)': Decimal('1968872.36761706'), 'Volume(1 hr)': Decimal('778937.22275036')}}
最初的代码生成了宽表格式,但我需要转换为如下长表结构(同时调整时间区间的显示文本):
PNL Volume BTC since 6pm 7831 24509290 BTC since last 4 hr 2930 4504202 BTC last 1 hr 1416 1067850 ETH since 6pm 387 10700784 ETH since last 4 hr -349 1968872 ETH last 1 hr 656 778937
我尝试了循环处理字典,但结果不符合预期,请求帮助。
解决方案1:使用Pandas内置函数(推荐)
通过stack()、正则提取、pivot_table()等方法快速转换格式,同时处理时间区间文本:
from decimal import Decimal import pandas as pd # 原始字典 options_pnl={'BTC': {'Pnl(since 6pm)': Decimal('7831.52228528'), 'Pnl(4 hr)': Decimal('2930.47450133'), 'Pnl(1 hr)': Decimal('1416.81306308'), 'Volume(since 6pm)': Decimal('24509290.62181862'), 'Volume(4 hr)': Decimal('4504202.83422724'), 'Volume(1 hr)': Decimal('1067850.01837278')}, 'ETH': {'Pnl(since 6pm)': Decimal('387.87564823'), 'Pnl(4 hr)': Decimal('-349.14871930'), 'Pnl(1 hr)': Decimal('656.74824550'), 'Volume(since 6pm)': Decimal('10700784.53262117'), 'Volume(4 hr)': Decimal('1968872.36761706'), 'Volume(1 hr)': Decimal('778937.22275036')}} # 1. 转换为DataFrame并转置 df = pd.DataFrame.from_dict(options_pnl).T # 2. 拆分列名,提取指标类型和时间区间 df = df.stack().reset_index() df.columns = ['Asset', 'Metric', 'Value'] df[['MetricType', 'Interval']] = df['Metric'].str.extract(r'(\w+)\((.*)\)') # 3. 格式化时间区间文本 def format_interval(interval): if interval == '4 hr': return 'since last 4 hr' elif interval == '1 hr': return 'last 1 hr' return interval df['Interval'] = df['Interval'].apply(format_interval) # 4. 重塑为目标格式并转换为整数类型 result = df.pivot_table(index=['Asset', 'Interval'], columns='MetricType', values='Value', aggfunc='first') result = result.astype('int64').rename(columns={'Pnl': 'PNL'}) # 合并索引生成目标行标签 result.index = result.index.map(lambda x: f"{x[0]} {x[1]}") print(result)
输出结果:
PNL Volume BTC since 6pm 7831 24509290 BTC since last 4 hr 2930 4504202 BTC last 1 hr 1416 1067850 ETH since 6pm 387 10700784 ETH since last 4 hr -349 1968872 ETH last 1 hr 656 778937
解决方案2:修正循环逻辑
原循环代码的问题是将Pnl和Volume拆分为单独行,导致同一时间区间的两个值分散。可以先按时间区间分组,再合并指标值:
from decimal import Decimal import pandas as pd options_pnl={'BTC': {'Pnl(since 6pm)': Decimal('7831.52228528'), 'Pnl(4 hr)': Decimal('2930.47450133'), 'Pnl(1 hr)': Decimal('1416.81306308'), 'Volume(since 6pm)': Decimal('24509290.62181862'), 'Volume(4 hr)': Decimal('4504202.83422724'), 'Volume(1 hr)': Decimal('1067850.01837278')}, 'ETH': {'Pnl(since 6pm)': Decimal('387.87564823'), 'Pnl(4 hr)': Decimal('-349.14871930'), 'Pnl(1 hr)': Decimal('656.74824550'), 'Volume(since 6pm)': Decimal('10700784.53262117'), 'Volume(4 hr)': Decimal('1968872.36761706'), 'Volume(1 hr)': Decimal('778937.22275036')}} data = [] for asset, values in options_pnl.items(): # 按时间区间分组存储Pnl和Volume interval_data = {} for metric, value in values.items(): metric_type, interval = metric.split('(') interval = interval.rstrip(')') # 格式化时间区间文本 if interval == '4 hr': interval = 'since last 4 hr' elif interval == '1 hr': interval = 'last 1 hr' # 存入对应区间的字典 if interval not in interval_data: interval_data[interval] = {'PNL': 0, 'Volume': 0} interval_data[interval][metric_type] = int(value) # 转换为目标行格式 for interval, metrics in interval_data.items(): data.append([f"{asset} {interval}", metrics['PNL'], metrics['Volume']]) # 生成DataFrame并设置索引 options_pnl_df = pd.DataFrame(data, columns=['', 'PNL', 'Volume']).set_index('') print(options_pnl_df)
输出结果与方案1一致。
内容的提问来源于stack exchange,提问作者Madan Raj
相关产品推荐
相关产品推荐

