Python 处理蒙特卡洛模拟数据并绘制图表

Python蒙特卡洛模拟数据处理与可视化

案例任务:

  1. 读取四个CSV文件(两个模拟:2020模拟,2025模拟;两个实际:2020实际,2025实际),并将它们转换为DataFrame,其中第一列’simulation’作为行索引,第一行作为列索引。
    注意:实际数据的simulation值为101,模拟数据的simulation值为1到100。
  2. 从2020年(模拟和实际)数据中取出“基金资产价值”和“基金申购赎回价值”,从2025年(模拟和实际)数据中取出“基金应收管理费价值”和“银行托管费价值”,计算一个特殊指标DBX。
    公式:DBX = (2020年的基金资产价值 - 2020年的基金申购赎回价值) - (2025年的基金应收管理费 - 2025年的银行托管费价值)
    同时,取出2025年的基金资产价值。
  3. 绘制两种图:
    • 单变量图:将2025年的基金资产价值、2025年的基金应收管理费价值和特殊指标DBX这三个变量分别绘制在同一个画布的三个子图中。
      对于每个变量,先将模拟数据(100次模拟)从小到大排序,x轴为排序后的序号(1到100),然后将其实际数据(simulation=101)用不同颜色标注在图中。
    • 双变量图:首先依据2025年的基金应收管理费价值(模拟数据)从小到大排序,然后以这个排序后的序号为x轴,在同一个图中绘制两条线:
      • 左y轴:2025年的基金资产价值(模拟数据)
      • 右y轴:特殊指标DBX(模拟数据)
    • 同时,将实际数据(2025年的基金资产价值和DBX)分别标注在对应的线上(用实际2025年的基金应收管理费价值在排序后的位置来确定x坐标)。

版本1.0

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# 1. Read and transform CSV files into DataFrames
def load_and_transform_csv(file_path):
    df = pd.read_csv(file_path)
    # Set 'simulation' as index and transpose to make variables as columns
    df = df.set_index('simulation').transpose()
    return df

# Load simulated and actual data for 2020 and 2025
df_2020_sim = load_and_transform_csv('2020_simulated_data.csv')
df_2020_actual = load_and_transform_csv('2020_actual_data.csv')
df_2025_sim = load_and_transform_csv('2025_simulated_data.csv')
df_2025_actual = load_and_transform_csv('2025_actual_data.csv')

# 2. Calculate DBX indicator
def calculate_dbx():
    # Extract required variables
    fund_asset_2020 = df_2020_sim.loc['fund_asset_value']  # 2020 Fund Asset Value (simulated)
    fund_net_2020 = df_2020_sim.loc['fund_subscription_redemption']  # 2020 Fund Subscription/Redemption
    mgmt_fee_2025 = df_2025_sim.loc['fund_management_fee']  # 2025 Fund Management Fee
    custody_fee_2025 = df_2025_sim.loc['bank_custody_fee']  # 2025 Bank Custody Fee
    
    # Extract actual values (simulation=101)
    fund_asset_2020_actual = df_2020_actual.loc['fund_asset_value', 101]
    fund_net_2020_actual = df_2020_actual.loc['fund_subscription_redemption', 101]
    mgmt_fee_2025_actual = df_2025_actual.loc['fund_management_fee', 101]
    custody_fee_2025_actual = df_2025_actual.loc['bank_custody_fee', 101]
    
    # Calculate DBX for simulated data
    dbx = (fund_asset_2020 - fund_net_2020) - (mgmt_fee_2025 - custody_fee_2025)
    
    # Calculate DBX for actual data
    dbx_actual = (fund_asset_2020_actual - fund_net_2020_actual) - (mgmt_fee_2025_actual - custody_fee_2025_actual)
    
    # Extract 2025 Fund Asset Value
    fund_asset_2025 = df_2025_sim.loc['fund_asset_value']
    fund_asset_2025_actual = df_2025_actual.loc['fund_asset_value', 101]
    
    return dbx, dbx_actual, fund_asset_2025, fund_asset_2025_actual, mgmt_fee_2025, mgmt_fee_2025_actual

# Compute DBX and extract variables
dbx, dbx_actual, fund_asset_2025, fund_asset_2025_actual, mgmt_fee_2025, mgmt_fee_2025_actual = calculate_dbx()

# 3. Plotting
# Single-variable scatter plots
fig1, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(15, 5), sharey=False)
fig1.suptitle('Single Variable Scatter Plots (2025 and DBX)')

# Sort values for plotting
fund_asset_2025_sorted = np.sort(fund_asset_2025)
mgmt_fee_2025_sorted = np.sort(mgmt_fee_2025)
dbx_sorted = np.sort(dbx)

# Plot Fund Asset Value 2025
ax1.scatter(range(len(fund_asset_2025_sorted)), fund_asset_2025_sorted, c='blue', label='Simulated')
ax1.scatter([0], [fund_asset_2025_actual], c='red', marker='*', s=200, label='Actual')
ax1.set_title('Fund Asset Value 2025')
ax1.set_xlabel('Sorted Order')
ax1.set_ylabel('Value')
ax1.legend()

# Plot Fund Management Fee 2025
ax2.scatter(range(len(mgmt_fee_2025_sorted)), mgmt_fee_2025_sorted, c='blue', label='Simulated')
ax2.scatter([0], [mgmt_fee_2025_actual], c='red', marker='*', s=200, label='Actual')
ax2.set_title('Fund Management Fee 2025')
ax2.set_xlabel('Sorted Order')
ax2.set_ylabel('Value')
ax2.legend()

# Plot DBX
ax3.scatter(range(len(dbx_sorted)), dbx_sorted, c='blue', label='Simulated')
ax3.scatter([0], [dbx_actual], c='red', marker='*', s=200, label='Actual')
ax3.set_title('DBX Indicator')
ax3.set_xlabel('Sorted Order')
ax3.set_ylabel('Value')
ax3.legend()

plt.tight_layout()

# Double-variable scatter plots
fig2, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5), sharey=False)
fig2.suptitle('Double Variable Scatter Plots (Sorted by Fund Management Fee 2025)')

# Sort by Fund Management Fee 2025
sorted_indices = np.argsort(mgmt_fee_2025)
mgmt_fee_2025_sorted = mgmt_fee_2025[sorted_indices]
fund_asset_2025_sorted = fund_asset_2025[sorted_indices]
dbx_sorted = dbx[sorted_indices]

# Plot Fund Asset Value vs Fund Management Fee
ax1.scatter(range(len(mgmt_fee_2025_sorted)), mgmt_fee_2025_sorted, c='blue', label='Management Fee')
ax1.set_xlabel('Sorted Order')
ax1.set_ylabel('Management Fee Value', color='blue')
ax1.tick_params(axis='y', labelcolor='blue')

ax1_twin = ax1.twinx()
ax1_twin.scatter(range(len(fund_asset_2025_sorted)), fund_asset_2025_sorted, c='green', label='Fund Asset Value')
ax1_twin.scatter([0], [fund_asset_2025_actual], c='red', marker='*', s=200, label='Actual Fund Asset')
ax1_twin.set_ylabel('Fund Asset Value', color='green')
ax1_twin.tick_params(axis='y', labelcolor='green')
ax1.set_title('Fund Asset Value vs Management Fee')
ax1.legend(loc='upper left')
ax1_twin.legend(loc='upper right')

# Plot DBX vs Fund Management Fee
ax2.scatter(range(len(mgmt_fee_2025_sorted)), mgmt_fee_2025_sorted, c='blue', label='Management Fee')
ax2.set_xlabel('Sorted Order')
ax2.set_ylabel('Management Fee Value', color='blue')
ax2.tick_params(axis='y', labelcolor='blue')

ax2_twin = ax2.twinx()
ax2_twin.scatter(range(len(dbx_sorted)), dbx_sorted, c='orange', label='DBX')
ax2_twin.scatter([0], [dbx_actual], c='red', marker='*', s=200, label='Actual DBX')
ax2_twin.set_ylabel('DBX Value', color='orange')
ax2_twin.tick_params(axis='y', labelcolor='orange')
ax2.set_title('DBX vs Management Fee')
ax2.legend(loc='upper left')
ax2_twin.legend(loc='upper right')

plt.tight_layout()
plt.show()

版本2.0

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import glob
import os

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

# 1. 读取和处理CSV文件
def load_and_process_data(file_path):
    """读取CSV文件并处理为DataFrame,设置simulation为索引"""
    df = pd.read_csv(file_path)
    df.set_index('simulation', inplace=True)
    return df

# 2. 计算特殊指标DBX
def calculate_dbx(data_2020, data_2025):
    """
    计算DBX指标:(2020基金资产价值 - 2020基金申购赎回价值) - (2025基金应收管理费 - 2025银行托管费价值)
    返回DBX和2025基金资产价值
    """
    # 假设列名如下,请根据实际数据调整
    asset_2020 = data_2020['基金资产价值']
    sub_red_2020 = data_2020['基金申购赎回价值']
    mgmt_fee_2025 = data_2025['基金应收管理费价值']
    custody_fee_2025 = data_2025['银行托管费价值']
    asset_2025 = data_2025['基金资产价值']
    
    dbx = (asset_2020 - sub_red_2020) - (mgmt_fee_2025 - custody_fee_2025)
    return dbx, asset_2025

# 3. 绘制单变量图
def plot_single_variable(asset_2025_sim, mgmt_fee_2025_sim, dbx_sim, 
                         asset_2025_actual, mgmt_fee_2025_actual, dbx_actual):
    """绘制单变量图"""
    fig, axes = plt.subplots(1, 3, figsize=(18, 6))
    
    # 对模拟数据进行排序
    asset_2025_sorted = np.sort(asset_2025_sim)
    mgmt_fee_2025_sorted = np.sort(mgmt_fee_2025_sim)
    dbx_sorted = np.sort(dbx_sim)
    
    # 绘制2025基金资产价值
    axes[0].scatter(range(len(asset_2025_sorted)), asset_2025_sorted, alpha=0.7, label='模拟数据')
    axes[0].axhline(y=asset_2025_actual, color='r', linestyle='-', label='实际数据')
    axes[0].set_title('2025年基金资产价值')
    axes[0].set_xlabel('排序序号')
    axes[0].set_ylabel('价值')
    axes[0].legend()
    
    # 绘制2025基金应收管理费价值
    axes[1].scatter(range(len(mgmt_fee_2025_sorted)), mgmt_fee_2025_sorted, alpha=0.7, label='模拟数据')
    axes[1].axhline(y=mgmt_fee_2025_actual, color='r', linestyle='-', label='实际数据')
    axes[1].set_title('2025年基金应收管理费价值')
    axes[1].set_xlabel('排序序号')
    axes[1].set_ylabel('价值')
    axes[1].legend()
    
    # 绘制DBX指标
    axes[2].scatter(range(len(dbx_sorted)), dbx_sorted, alpha=0.7, label='模拟数据')
    axes[2].axhline(y=dbx_actual, color='r', linestyle='-', label='实际数据')
    axes[2].set_title('DBX指标')
    axes[2].set_xlabel('排序序号')
    axes[2].set_ylabel('价值')
    axes[2].legend()
    
    plt.tight_layout()
    plt.savefig('单变量图.png', dpi=300)
    plt.show()

# 4. 绘制双变量图
def plot_dual_variable(mgmt_fee_2025_sim, asset_2025_sim, dbx_sim, 
                       mgmt_fee_2025_actual, asset_2025_actual, dbx_actual):
    """绘制双变量图"""
    fig, ax1 = plt.subplots(figsize=(12, 8))
    
    # 按2025基金应收管理费价值排序
    sorted_indices = np.argsort(mgmt_fee_2025_sim)
    mgmt_fee_sorted = mgmt_fee_2025_sim[sorted_indices]
    asset_sorted = asset_2025_sim[sorted_indices]
    dbx_sorted = dbx_sim[sorted_indices]
    
    # 绘制2025基金资产价值(左轴)
    color1 = 'tab:blue'
    ax1.set_xlabel('排序序号(按2025基金应收管理费价值排序)')
    ax1.set_ylabel('2025基金资产价值', color=color1)
    line1 = ax1.plot(range(len(asset_sorted)), asset_sorted, color=color1, alpha=0.7, label='2025基金资产价值(模拟)')
    ax1.tick_params(axis='y', labelcolor=color1)
    
    # 创建右轴
    ax2 = ax1.twinx()
    color2 = 'tab:red'
    ax2.set_ylabel('DBX指标', color=color2)
    line2 = ax2.plot(range(len(dbx_sorted)), dbx_sorted, color=color2, alpha=0.7, label='DBX指标(模拟)')
    ax2.tick_params(axis='y', labelcolor=color2)
    
    # 找到实际数据在排序后的位置
    actual_idx = np.searchsorted(mgmt_fee_sorted, mgmt_fee_2025_actual)
    
    # 标注实际数据点
    ax1.scatter(actual_idx, asset_2025_actual, color=color1, s=100, marker='o', edgecolors='black', label='2025基金资产价值(实际)')
    ax2.scatter(actual_idx, dbx_actual, color=color2, s=100, marker='s', edgecolors='black', label='DBX指标(实际)')
    
    # 合并图例
    lines1, labels1 = ax1.get_legend_handles_labels()
    lines2, labels2 = ax2.get_legend_handles_labels()
    ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper left')
    
    plt.title('双变量图:按2025基金应收管理费价值排序')
    plt.savefig('双变量图.png', dpi=300)
    plt.show()

# 主程序
def main():
    # 加载数据(请根据实际文件路径修改)
    # 假设文件名为:sim_2020.csv, sim_2025.csv, actual_2020.csv, actual_2025.csv
    sim_2020 = load_and_process_data('sim_2020.csv')
    sim_2025 = load_and_process_data('sim_2025.csv')
    actual_2020 = load_and_process_data('actual_2020.csv')
    actual_2025 = load_and_process_data('actual_2025.csv')
    
    # 提取实际数据(simulation=101)
    actual_2020_row = actual_2020.loc[101]
    actual_2025_row = actual_2025.loc[101]
    
    # 计算DBX指标
    # 模拟数据
    dbx_sim, asset_2025_sim = calculate_dbx(sim_2020, sim_2025)
    mgmt_fee_2025_sim = sim_2025['基金应收管理费价值']
    
    # 实际数据
    dbx_actual, asset_2025_actual = calculate_dbx(actual_2020_row, actual_2025_row)
    mgmt_fee_2025_actual = actual_2025_row['基金应收管理费价值']
    
    # 绘制单变量图
    plot_single_variable(asset_2025_sim, mgmt_fee_2025_sim, dbx_sim,
                         asset_2025_actual, mgmt_fee_2025_actual, dbx_actual)
    
    # 绘制双变量图
    plot_dual_variable(mgmt_fee_2025_sim, asset_2025_sim, dbx_sim,
                       mgmt_fee_2025_actual, asset_2025_actual, dbx_actual)

if __name__ == "__main__":
    main()

注意事项

  1. 根据实际数据文件路径修改代码中的文件名
  2. 根据实际列名修改代码中的变量名(如’基金资产价值’等)
  3. 代码假设CSV文件第一列名为"simulation" 和假设实际数据的simulation值为101
  4. 代码使用了中文字体设置,可选
  5. 代码会生成两个图像文件:
    • 单变量图.png:包含三个子图,分别显示2025年基金资产价值、2025年基金应收管理费价值和DBX指标
    • 双变量图.png:显示按2025年基金应收管理费价值排序后的2025年基金资产价值和DBX指标

版本2.1

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_pdf import PdfPages

# 设置中文字体支持
plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
plt.rcParams['axes.unicode_minus'] = False    # 正确显示负号

def add_rank_columns(df, sort_vars):
    """
    Add ranking columns for specified variables using pd.assign method
    
    Parameters:
    df: pandas DataFrame
        Input dataframe containing the variables to be ranked
    sort_vars: str or list of str
        Variable name(s) for which ranking columns should be created
    
    Returns:
    pandas DataFrame
        New dataframe with added ranking columns. Each ranking column is named
        with the original variable name plus the '_排名' suffix, containing
        integer rankings calculated using the 'first' method for handling ties.
    
    Notes:
    - Uses pandas.Series.rank() with method='first' to break ties by order of appearance
    - Ranking columns are created using dictionary comprehension and assigned with pd.assign()
    - Original dataframe remains unchanged (returns a new dataframe)
    """
    # Ensure sort_vars is converted to list format if single string provided
    if isinstance(sort_vars, str):
        sort_vars = [sort_vars]
    
    # Create ranking columns using dictionary comprehension
    rank_columns = {
        f"{var}_排名": df[var].rank(method='first').astype(int)
        for var in sort_vars
    }
    
    # Return new dataframe with all ranking columns added using assign
    return df.assign(**rank_columns)

def plot_single_variables(df, variables, highlight_index=101, filename=None):
    """
    绘制单变量分布图
    
    参数:
    df: pandas DataFrame
    variables: 字符串列表,需要绘制的变量名称
    highlight_index: 整数,需要特别标注的模拟索引
    filename: 字符串,保存PDF的文件名(可选)
    """
    if filename:
        pdf_pages = PdfPages(filename)
    
    fig, axes = plt.subplots(len(variables), 1, figsize=(10, 4*len(variables)))
    if len(variables) == 1:
        axes = [axes]  # 确保axes是可迭代的
    
    fig.suptitle('单变量分布图', fontsize=16)
    
    for i, var in enumerate(variables):
        # 对变量数据进行排序
        sorted_data = df[var].sort_values().reset_index(drop=True)
        ranks = np.arange(1, len(sorted_data) + 1)
        
        # 绘制所有数据点
        axes[i].plot(ranks, sorted_data, 'o', markersize=4, alpha=0.7, label=f'{var}所有模拟')
        
        # 标注highlight_index的数据点
        highlight_value = df.loc[highlight_index, var]
        highlight_rank = (sorted_data == highlight_value).idxmax() + 1  # 找到排名
        
        axes[i].plot(highlight_rank, highlight_value, 's', markersize=8, 
                    color='orange', label=f'{var} (模拟{highlight_index})')
        
        axes[i].set_xlabel('排名')
        axes[i].set_ylabel(var)
        axes[i].legend()
        axes[i].grid(True, alpha=0.3)
    
    plt.tight_layout()
    
    if filename:
        pdf_pages.savefig(fig)
        plt.close(fig)
        pdf_pages.close()
    else:
        plt.show()

def plot_double_variables(df, var_left1, var_left2, var_right, highlight_index=101, filename=None):
    """
    绘制双变量关系图
    
    参数:
    df: pandas DataFrame
    var_left1: 字符串,第一个图的左y轴变量
    var_left2: 字符串,第二个图的左y轴变量
    var_right: 字符串,两个图的右y轴变量(排序依据)
    highlight_index: 整数,需要特别标注的模拟索引
    filename: 字符串,保存PDF的文件名(可选)
    """
    if filename:
        pdf_pages = PdfPages(filename)
    
    # 按var_right排序
    sorted_df = df.sort_values(by=var_right)
    ranks = np.arange(1, len(sorted_df) + 1)
    
    # 获取highlight_index的数据在排序后的位置
    highlight_idx_pos = sorted_df.index.get_loc(highlight_index)
    highlight_rank = highlight_idx_pos + 1
    highlight_right_value = sorted_df.iloc[highlight_idx_pos][var_right]
    highlight_left1_value = sorted_df.iloc[highlight_idx_pos][var_left1]
    highlight_left2_value = sorted_df.iloc[highlight_idx_pos][var_left2]
    
    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10))
    fig.suptitle('双变量关系图', fontsize=16)
    
    # 图一:var_left1 vs var_right
    ax1.plot(ranks, sorted_df[var_left1], 'b-', label=var_left1)
    ax1.set_xlabel(f'按{var_right}排序后的排名')
    ax1.set_ylabel(var_left1, color='b')
    ax1.tick_params(axis='y', labelcolor='b')
    
    # 创建右轴
    ax1_r = ax1.twinx()
    ax1_r.plot(ranks, sorted_df[var_right], 'r-', label=var_right)
    ax1_r.set_ylabel(var_right, color='r')
    ax1_r.tick_params(axis='y', labelcolor='r')
    
    # 标注highlight_index的点
    ax1.plot(highlight_rank, highlight_left1_value, 'ko', markersize=8, 
             label=f'模拟{highlight_index} ({var_left1})')
    ax1_r.plot(highlight_rank, highlight_right_value, 'ks', markersize=8, 
               label=f'模拟{highlight_index} ({var_right})')
    
    # 添加图例
    lines1, labels1 = ax1.get_legend_handles_labels()
    lines2, labels2 = ax1_r.get_legend_handles_labels()
    ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper left')
    
    ax1.grid(True, alpha=0.3)
    
    # 图二:var_left2 vs var_right
    ax2.plot(ranks, sorted_df[var_left2], 'g-', label=var_left2)
    ax2.set_xlabel(f'按{var_right}排序后的排名')
    ax2.set_ylabel(var_left2, color='g')
    ax2.tick_params(axis='y', labelcolor='g')
    
    # 创建右轴
    ax2_r = ax2.twinx()
    ax2_r.plot(ranks, sorted_df[var_right], 'r-', label=var_right)
    ax2_r.set_ylabel(var_right, color='r')
    ax2_r.tick_params(axis='y', labelcolor='r')
    
    # 标注highlight_index的点
    ax2.plot(highlight_rank, highlight_left2_value, 'ko', markersize=8, 
             label=f'模拟{highlight_index} ({var_left2})')
    ax2_r.plot(highlight_rank, highlight_right_value, 'ks', markersize=8, 
               label=f'模拟{highlight_index} ({var_right})')
    
    # 添加图例
    lines1, labels1 = ax2.get_legend_handles_labels()
    lines2, labels2 = ax2_r.get_legend_handles_labels()
    ax2.legend(lines1 + lines2, labels1 + labels2, loc='upper left')
    
    ax2.grid(True, alpha=0.3)
    
    plt.tight_layout()
    
    if filename:
        pdf_pages.savefig(fig)
        plt.close(fig)
        pdf_pages.close()
    else:
        plt.show()

# 示例使用
if __name__ == "__main__":
    # 创建示例数据
    np.random.seed(42)
    data = {
        '基金资产价值': np.random.normal(100, 20, 101),
        '基金应收管理费价值': np.random.normal(50, 10, 101),
        'DBX': np.random.normal(0.5, 0.2, 101)
    }
    df = pd.DataFrame(data, index=range(1, 102))
    
    # 添加排名列
    df_ranked = add_rank_columns(df, '基金应收管理费价值')
    print(df_ranked.head())
    
    # 绘制单变量图并保存到PDF
    plot_single_variables(
        df, 
        ['基金资产价值', '基金应收管理费价值', 'DBX'], 
        highlight_index=101, 
        filename='单变量图.pdf'
    )
    
    # 绘制双变量图并保存到PDF
    plot_double_variables(
        df,
        var_left1='基金资产价值',
        var_left2='DBX',
        var_right='基金应收管理费价值',
        highlight_index=101,
        filename='双变量图.pdf'
    )

代码说明:

  1. add_rank_columns 函数:
    • 输入DataFrame和需要排序的变量名称
    • 使用pandas的rank方法计算排名
    • 返回添加了排名列的新DataFrame
  2. plot_single_variables 函数:
    • 输入DataFrame、变量列表、需要高亮的索引和可选的文件名
    • 绘制单变量分布图
    • 如果提供了文件名,则将图保存到PDF
  3. plot_double_variables 函数:
    • 输入DataFrame、两个左轴变量、一个右轴变量、需要高亮的索引和可选的文件名
    • 绘制双变量关系图
    • 如果提供了文件名,则将图保存到PDF
  4. 使用示例:
    • 创建示例数据
    • 使用add_rank_columns函数添加排名列
    • 分别调用两个绘图函数并将结果保存到PDF

版本 3.0 复杂绘图系统:多数据源单变量和双变量图

三个主要函数:图像转PDF保存函数、单变量图函数和双变量图函数。系统支持最多4个DataFrame的对比分析,并自动调整画布布局。

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_pdf import PdfPages

# 设置中文字体支持
plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
plt.rcParams['axes.unicode_minus'] = False    # 正确显示负号

def add_rank_columns(df, sort_vars):
    """
    Add ranking columns for specified variables using pd.assign method
    
    Parameters:
    df: pandas DataFrame
        Input dataframe containing the variables to be ranked
    sort_vars: str or list of str
        Variable name(s) for which ranking columns should be created
    """
    # Ensure sort_vars is converted to list format if single string provided
    if isinstance(sort_vars, str):
        sort_vars = [sort_vars]
    
    # Create ranking columns using dictionary comprehension
    rank_columns = {
        f"{var}_排名": df[var].rank(method='first').astype(int)
        for var in sort_vars
    }
    
    # Return new dataframe with all ranking columns added using assign
    return df.assign(**rank_columns)

def save_figures_to_pdf(figures, filename, dpi=300):
    """
    将多个图形保存到高分辨率PDF文件
    
    参数:
    figures: matplotlib Figure对象列表
    filename: 字符串,保存PDF的文件名
    dpi: 整数,图像分辨率,默认300
    """
    with PdfPages(filename) as pdf:
        for i, fig in enumerate(figures):
            pdf.savefig(fig, dpi=dpi)
            plt.close(fig)
        print(f"已保存 {len(figures)} 个图形到 {filename}")

def plot_single_variables(df_list, variables, highlight_index=101, labels=None):
    """
    绘制单变量分布图,支持最多4个DataFrame对比
    
    参数:
    df_list: DataFrame或DataFrame列表,最多4个
    variables: 字符串列表,需要绘制的变量名称
    highlight_index: 整数,需要特别标注的模拟索引
    labels: 字符串列表,每个DataFrame的标签(可选)
    
    返回:
    matplotlib Figure对象
    """
    # 确保df_list是列表格式
    if not isinstance(df_list, list):
        df_list = [df_list]
    
    # 限制最多4个DataFrame
    if len(df_list) > 4:
        df_list = df_list[:4]
        print("警告: 只使用前4个DataFrame")
    
    num_dfs = len(df_list)
    
    # 如果没有提供标签,创建默认标签
    if labels is None:
        labels = [f"数据集 {i+1}" for i in range(num_dfs)]
    elif len(labels) != num_dfs:
        labels = [f"数据集 {i+1}" for i in range(num_dfs)]
        print("警告: 标签数量与DataFrame数量不匹配,使用默认标签")
    
    # 创建画布
    fig, axes = plt.subplots(len(variables), num_dfs, figsize=(5*num_dfs, 4*len(variables)))
    
    # 如果只有一个子图,确保axes是二维数组
    if len(variables) == 1:
        axes = axes.reshape(1, -1)
    if num_dfs == 1:
        axes = axes.reshape(-1, 1)
    
    # 为每个DataFrame和每个变量绘制图形
    for col_idx, (df, label) in enumerate(zip(df_list, labels)):
        for row_idx, var in enumerate(variables):
            ax = axes[row_idx, col_idx]
            
            # 获取排序后的数据和排名
            rank_col = f"{var}_排名"
            sorted_df = df.sort_values(by=rank_col)
            ranks = sorted_df[rank_col]
            values = sorted_df[var]
            
            # 绘制所有数据点
            ax.plot(ranks, values, 'o', markersize=3, alpha=0.7, label=f'{var}所有模拟')
            
            # 标注highlight_index的数据点
            highlight_row = df[df.index == highlight_index]
            if not highlight_row.empty:
                highlight_rank = highlight_row[rank_col].values[0]
                highlight_value = highlight_row[var].values[0]
                
                ax.plot(highlight_rank, highlight_value, 's', markersize=6, 
                        color='orange', label=f'{var} (模拟{highlight_index})')
            
            ax.set_xlabel('排名')
            ax.set_ylabel(var)
            ax.set_title(f'{label}: {var}')
            ax.legend(fontsize=8)
            ax.grid(True, alpha=0.3)
    
    plt.tight_layout()
    return fig

def plot_double_variables(df_list, var_left1, var_left2, var_right, highlight_index=101, labels=None):
    """
    绘制双变量关系图,支持最多4个DataFrame对比
    
    参数:
    df_list: DataFrame或DataFrame列表,最多4个
    var_left1: 字符串,第一个图的左y轴变量
    var_left2: 字符串,第二个图的左y轴变量
    var_right: 字符串,两个图的右y轴变量(排序依据)
    highlight_index: 整数,需要特别标注的模拟索引
    labels: 字符串列表,每个DataFrame的标签(可选)
    
    返回:
    matplotlib Figure对象
    """
    # 确保df_list是列表格式
    if not isinstance(df_list, list):
        df_list = [df_list]
    
    # 限制最多4个DataFrame
    if len(df_list) > 4:
        df_list = df_list[:4]
        print("警告: 只使用前4个DataFrame")
    
    num_dfs = len(df_list)
    
    # 如果没有提供标签,创建默认标签
    if labels is None:
        labels = [f"数据集 {i+1}" for i in range(num_dfs)]
    elif len(labels) != num_dfs:
        labels = [f"数据集 {i+1}" for i in range(num_dfs)]
        print("警告: 标签数量与DataFrame数量不匹配,使用默认标签")
    
    # 创建画布 - 2行(两个子图类型)x num_dfs列
    fig, axes = plt.subplots(2, num_dfs, figsize=(5*num_dfs, 8))
    
    # 如果只有一个子图,确保axes是二维数组
    if num_dfs == 1:
        axes = axes.reshape(2, 1)
    
    # 为每个DataFrame绘制两个子图
    for col_idx, (df, label) in enumerate(zip(df_list, labels)):
        # 获取排序后的数据和排名
        rank_col = f"{var_right}_排名"
        sorted_df = df.sort_values(by=rank_col)
        ranks = sorted_df[rank_col]
        
        # 获取highlight_index的数据
        highlight_row = df[df.index == highlight_index]
        if not highlight_row.empty:
            highlight_rank = highlight_row[rank_col].values[0]
            highlight_right_value = highlight_row[var_right].values[0]
            highlight_left1_value = highlight_row[var_left1].values[0]
            highlight_left2_value = highlight_row[var_left2].values[0]
        
        # 第一个子图:var_left1 vs var_right
        ax1 = axes[0, col_idx]
        ax1.plot(ranks, sorted_df[var_left1], 'b-', label=var_left1, alpha=0.7)
        ax1.set_xlabel(f'按{var_right}排序后的排名')
        ax1.set_ylabel(var_left1, color='b')
        ax1.tick_params(axis='y', labelcolor='b')
        
        # 创建右轴
        ax1_r = ax1.twinx()
        ax1_r.plot(ranks, sorted_df[var_right], 'r-', label=var_right, alpha=0.7)
        ax1_r.set_ylabel(var_right, color='r')
        ax1_r.tick_params(axis='y', labelcolor='r')
        
        # 标注highlight_index的点
        if not highlight_row.empty:
            ax1.plot(highlight_rank, highlight_left1_value, 'ko', markersize=6, 
                     label=f'模拟{highlight_index} ({var_left1})')
            ax1_r.plot(highlight_rank, highlight_right_value, 'ks', markersize=6, 
                       label=f'模拟{highlight_index} ({var_right})')
        
        # 添加图例
        lines1, labels1 = ax1.get_legend_handles_labels()
        lines2, labels2 = ax1_r.get_legend_handles_labels()
        ax1.legend(lines1 + lines2, labels1 + labels2, fontsize=8, loc='upper left')
        
        ax1.grid(True, alpha=0.3)
        ax1.set_title(f'{label}: {var_left1} vs {var_right}')
        
        # 第二个子图:var_left2 vs var_right
        ax2 = axes[1, col_idx]
        ax2.plot(ranks, sorted_df[var_left2], 'g-', label=var_left2, alpha=0.7)
        ax2.set_xlabel(f'按{var_right}排序后的排名')
        ax2.set_ylabel(var_left2, color='g')
        ax2.tick_params(axis='y', labelcolor='g')
        
        # 创建右轴
        ax2_r = ax2.twinx()
        ax2_r.plot(ranks, sorted_df[var_right], 'r-', label=var_right, alpha=0.7)
        ax2_r.set_ylabel(var_right, color='r')
        ax2_r.tick_params(axis='y', labelcolor='r')
        
        # 标注highlight_index的点
        if not highlight_row.empty:
            ax2.plot(highlight_rank, highlight_left2_value, 'ko', markersize=6, 
                     label=f'模拟{highlight_index} ({var_left2})')
            ax2_r.plot(highlight_rank, highlight_right_value, 'ks', markersize=6, 
                       label=f'模拟{highlight_index} ({var_right})')
        
        # 添加图例
        lines1, labels1 = ax2.get_legend_handles_labels()
        lines2, labels2 = ax2_r.get_legend_handles_labels()
        ax2.legend(lines1 + lines2, labels1 + labels2, fontsize=8, loc='upper left')
        
        ax2.grid(True, alpha=0.3)
        ax2.set_title(f'{label}: {var_left2} vs {var_right}')
    
    plt.tight_layout()
    return fig

# 示例使用
if __name__ == "__main__":
    # 创建示例数据 - 4个不同时间点的数据集
    np.random.seed(42)
    dfs = []
    labels = ["时间点1", "时间点2", "时间点3", "时间点4"]
    
    for i in range(4):
        # 为每个时间点创建略有不同的数据
        data = {
            '基金资产价值': np.random.normal(100 + i*5, 20, 101),
            '基金应收管理费价值': np.random.normal(50 + i*2, 10, 101),
            'DBX': np.random.normal(0.5 + i*0.1, 0.2, 101)
        }
        df = pd.DataFrame(data, index=range(1, 102))
        
        # 添加排名列
        df = add_rank_columns(df, ['基金资产价值', '基金应收管理费价值', 'DBX'])
        dfs.append(df)
    
    # 创建图形列表
    figures = []
    
    # 创建单变量图 - 使用所有4个DataFrame
    fig1 = plot_single_variables(
        dfs, 
        ['基金资产价值', '基金应收管理费价值', 'DBX'], 
        highlight_index=101,
        labels=labels
    )
    figures.append(fig1)
    
    # 创建单变量图 - 只使用前2个DataFrame
    fig2 = plot_single_variables(
        dfs[:2], 
        ['基金资产价值', '基金应收管理费价值', 'DBX'], 
        highlight_index=101,
        labels=labels[:2]
    )
    figures.append(fig2)
    
    # 创建双变量图 - 使用所有4个DataFrame
    fig3 = plot_double_variables(
        dfs,
        var_left1='基金资产价值',
        var_left2='DBX',
        var_right='基金应收管理费价值',
        highlight_index=101,
        labels=labels
    )
    figures.append(fig3)
    
    # 创建双变量图 - 只使用前2个DataFrame
    fig4 = plot_double_variables(
        dfs[:2],
        var_left1='基金资产价值',
        var_left2='DBX',
        var_right='基金应收管理费价值',
        highlight_index=101,
        labels=labels[:2]
    )
    figures.append(fig4)
    
    # 将所有图形保存到高分辨率PDF
    save_figures_to_pdf(figures, '多数据源对比分析.pdf', dpi=300)

代码说明:

  1. add_rank_columns 函数
  • 使用pd.assign方法添加排名列
  • 为每个变量创建{变量名}_排名列
  • 使用rank(method=‘first’)处理并列排名
  1. save_figures_to_pdf 函数
  • 接收图形列表和高分辨率参数
  • 使用PdfPages将所有图形保存到单个PDF文件
  • 支持自定义DPI(默认300)
  1. plot_single_variables 函数
  • 支持最多4个DataFrame的对比
  • 自动调整画布布局(3×N,N为DataFrame数量)
  • 使用排名列作为x轴
  • 标注指定模拟索引的数据点
  • 支持自定义标签
  1. plot_double_variables 函数
  • 支持最多4个DataFrame的对比
  • 自动调整画布布局(2×N,N为DataFrame数量)
  • 使用排名列作为x轴
  • 创建双y轴图表
  • 标注指定模拟索引的数据点
  • 支持自定义标签

使用示例:

  1. 准备数据:
    • 创建多个DataFrame(最多4个)
    • 使用add_rank_columns函数添加排名列
  2. 创建图形:
    • 调用plot_single_variables创建单变量图
    • 调用plot_double_variables创建双变量图
    • 可以创建不同组合的图形(使用全部或部分DataFrame)
  3. 保存PDF:
    • 将所有图形添加到列表中
    • 调用save_figures_to_pdf保存为高分辨率PDF

优势:

  1. 灵活性:支持1-4个DataFrame的对比分析
  2. 自动化:自动调整画布布局和图形尺寸
  3. 高质量输出:支持高分辨率PDF输出
  4. 易于扩展:可以轻松添加更多DataFrame或变量类型
  5. 清晰的标签:支持为每个DataFrame添加自定义标签
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值