基于数据集各列分布生成合成数据的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
相关产品推荐
相关产品推荐

