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

基于数据集各列分布生成合成数据的Python实现方案求助

基于数据集列分布生成合成数据的Python方案

下面提供几种针对连续+分类混合数据集的合成数据生成方案,覆盖从基础统计拟合适配到保留变量依赖关系的进阶方法:

方案1:分类型+连续列独立拟合统计分布

这种方法针对每一列单独拟合对应分布,适合变量间依赖关系较弱的场景,实现简单直观。

代码实现

import pandas as pd
import numpy as np
from scipy import stats
from sklearn.preprocessing import LabelEncoder

def fit_continuous_distribution(data_col):
    # 尝试常见连续分布,选择拟合最优的
    distributions = [
        stats.norm, stats.lognorm, stats.gamma,
        stats.beta, stats.weibull_min, stats.uniform
    ]
    best_dist = None
    best_params = None
    best_sse = np.inf

    for dist in distributions:
        try:
            params = dist.fit(data_col)
            # 计算拟合误差
            pdf = dist.pdf(data_col, *params)
            sse = np.sum((data_col - pdf)**2)
            if sse < best_sse:
                best_sse = sse
                best_dist = dist
                best_params = params
        except Exception as e:
            continue
    return best_dist, best_params

def generate_synthetic_data(original_df, n_samples):
    synthetic_df = pd.DataFrame()
    
    # 处理分类列
    cat_cols = original_df.select_dtypes(include=['object', 'category']).columns
    for col in cat_cols:
        # 获取类别频数分布
        freq = original_df[col].value_counts(normalize=True)
        # 按分布采样
        synthetic_df[col] = np.random.choice(freq.index, size=n_samples, p=freq.values)
    
    # 处理连续列
    cont_cols = original_df.select_dtypes(include=['int64', 'float64']).columns
    for col in cont_cols:
        dist, params = fit_continuous_distribution(original_df[col].dropna())
        if dist is not None:
            # 生成符合分布的数据
            synthetic_df[col] = dist.rvs(*params, size=n_samples)
        else:
            # 如果拟合失败,用均值+标准差生成正态数据兜底
            mu, sigma = original_df[col].mean(), original_df[col].std()
            synthetic_df[col] = np.random.normal(mu, sigma, size=n_samples)
    
    return synthetic_df

# 示例使用
# original_df = pd.read_csv("your_dataset.csv")
# synthetic_data = generate_synthetic_data(original_df, 500)  # 生成500条数据

说明

  • 分类列直接基于原数据的类别频率进行随机采样,保证类别比例一致
  • 连续列会自动尝试多种常见分布,选择拟合误差最小的来生成数据;拟合失败时用正态分布兜底

方案2:使用Copulas建模联合分布(保留变量依赖)

如果需要保留变量间的相关性,单独拟合列分布会丢失这部分信息,Copulas可以建模变量的联合分布,生成更贴近真实数据的合成样本。

代码实现

import pandas as pd
import numpy as np
from copulae import GaussianCopula
from sklearn.preprocessing import LabelEncoder, MinMaxScaler

def preprocess_mixed_data(df):
    processed_df = df.copy()
    cat_cols = processed_df.select_dtypes(include=['object', 'category']).columns
    encoders = {}
    
    # 编码分类列
    for col in cat_cols:
        le = LabelEncoder()
        processed_df[col] = le.fit_transform(processed_df[col])
        encoders[col] = le
    
    # 归一化连续列到[0,1]区间(适配Copula要求)
    cont_cols = processed_df.select_dtypes(include=['int64', 'float64']).columns
    scaler = MinMaxScaler()
    processed_df[cont_cols] = scaler.fit_transform(processed_df[cont_cols])
    
    return processed_df, encoders, scaler, cat_cols, cont_cols

def generate_copula_data(original_df, n_samples):
    processed_df, encoders, scaler, cat_cols, cont_cols = preprocess_mixed_data(original_df)
    
    # 拟合高斯Copula
    cop = GaussianCopula(dim=processed_df.shape[1])
    cop.fit(processed_df.values)
    
    # 生成Copula样本
    synthetic_samples = cop.random(n_samples)
    synthetic_df = pd.DataFrame(synthetic_samples, columns=processed_df.columns)
    
    # 逆变换还原数据
    synthetic_df[cont_cols] = scaler.inverse_transform(synthetic_df[cont_cols])
    for col in cat_cols:
        # 将编码值转回原类别
        synthetic_df[col] = encoders[col].inverse_transform(synthetic_df[col].round().astype(int))
    
    return synthetic_df

# 示例使用
# original_df = pd.read_csv("your_dataset.csv")
# synthetic_data = generate_copula_data(original_df, 500)

说明

  • 高斯Copula能捕捉变量间的线性相关性,适合大多数场景;如果需要非线性依赖,可以尝试ArchimedeanCopula(如Clayton、Gumbel)
  • 分类列先编码为数值,生成后再逆编码还原原类别;连续列做归一化适配Copula的输入要求

方案3:Kernel密度估计(KDE)生成连续数据

对于无法用参数化分布拟合的连续列,KDE可以无假设地拟合数据的概率密度,生成更贴合原始分布的样本。

代码实现

import pandas as pd
import numpy as np
from scipy.stats import gaussian_kde

def generate_kde_data(original_df, n_samples):
    synthetic_df = pd.DataFrame()
    
    # 分类列处理同方案1
    cat_cols = original_df.select_dtypes(include=['object', 'category']).columns
    for col in cat_cols:
        freq = original_df[col].value_counts(normalize=True)
        synthetic_df[col] = np.random.choice(freq.index, size=n_samples, p=freq.values)
    
    # 连续列用KDE拟合
    cont_cols = original_df.select_dtypes(include=['int64', 'float64']).columns
    for col in cont_cols:
        data = original_df[col].dropna().values
        kde = gaussian_kde(data)
        synthetic_df[col] = kde.resample(n_samples)[0]
    
    return synthetic_df

# 示例使用
# original_df = pd.read_csv("your_dataset.csv")
# synthetic_data = generate_kde_data(original_df, 500)

说明

  • KDE完全基于数据本身的密度生成样本,不需要假设分布类型,适合分布复杂的连续列
  • 缺点是如果原数据量过小(比如你的145条),KDE可能会过拟合,生成的样本噪声较大

验证合成数据的有效性

生成后建议对比原数据和合成数据的分布:

  • 连续列:绘制直方图、QQ图,用KS检验验证分布一致性
  • 分类列:对比类别频率,用卡方检验验证比例一致性
# 示例:KS检验连续列
from scipy.stats import ks_2samp

cont_col = "your_continuous_column"
stat, p_value = ks_2samp(original_df[cont_col].dropna(), synthetic_df[cont_col].dropna())
print(f"KS检验统计量: {stat}, p值: {p_value}")
# p值>0.05则认为分布无显著差异

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 10:01:47