熊猫:根据目标分布从DataFrame进行采样

问题描述

也许对于Cross Validated来说也是这样,但是我对如何在Python中进行操作很感兴趣。

我有一个Pandas DataFrame,其中包含实例的数据集D,这些实例的数据集都具有一些连续值xx以某种方式分布,例如统一分布,可以是任何东西。

我想从n抽取D的{​​{1}}个样本,x的目标分布可以采样或近似。这来自一个数据集,这里我只是采用正态分布。

如何从D采样实例,以使采样中x的分布等于/类似于我指定的任意分布?

现在,我对值x,子集D进行采样,使其包含所有x +- eps并从中采样。但是,当数据集变大时,这相当慢。人们一定想出了一个更好的解决方案。也许解决方案已经不错,但可以更有效地实施?

我可以将x分成多个层次,这会更快,但是如果没有这个,是否有解决方案?

我当前的代码可以正常工作,但运行缓慢(30k / 100k 1分钟,但我有200k / 700k左右)

import numpy as np
import pandas as pd
import numpy.random as rnd
from matplotlib import pyplot as plt
from tqdm import tqdm

n_target = 30000
n_dataset = 100000

x_target_distribution = rnd.normal(size=n_target)
# In reality this would be x_target_distribution = my_dataset["x"].sample(n_target,replace=True)

df = pd.DataFrame({
    'instances': np.arange(n_dataset),'x': rnd.uniform(-5,5,size=n_dataset)
    })

plt.hist(df["x"],histtype="step",density=True)
plt.hist(x_target_distribution,density=True)

def sample_instance_with_x(x,eps=0.2):
    try:
        return df.loc[abs(df["x"] - x) < eps].sample(1)
    except ValueError: # fallback if no instance possible
        return df.sample(1)

df_sampled_ = [sample_instance_with_x(x) for x in tqdm(x_target_distribution)]
df_sampled = pd.concat(df_sampled_)

plt.hist(df_sampled["x"],density=True)

解决方法

解决方案

而不是生成新点并在df.x中找到最近的邻居,而是根据您的目标分布来定义对每个点进行采样的概率。您可以使用np.random.choice。从df.x中抽出一秒钟左右的一百万个点,以获得如下所示的高斯目标分布:

x = np.sort(df.x)
f_x = np.gradient(x)*np.exp(-x**2/2)
sample_probs = f_x/np.sum(f_x)
samples = np.random.choice(x,p=sample_probs,size=1000000)

sample_probs是关键数量,因为它可以连接回数据框或用作df.sample的参数,例如:

# sample df rows without replacement
df_samples = df["x"].sort_values().sample(
    n=1000,weights=sample_probs,replace=False,)

plt.hist(samples,bins=100,density=True)的结果:

corrected image

高斯分布x,目标分布均匀

让我们看看当从高斯分布中提取原始样本时该方法的性能,我们希望从统一的目标分布中对其进行采样:

x = np.sort(np.random.normal(size=100000))
f_x = np.gradient(x)*np.ones(len(x))
sample_probs = f_x/np.sum(f_x)
samples = np.random.choice(x,size=1000000)

sample to uniform distribution from gaussian distributed points

实际上很好。从高斯的尾部采样的点很少,为统一采样分配的概率较大;这就是为什么它们稀疏并且具有比中间部分更大的样本的原因。

方法

以以下形式计算x中的样本的近似概率:

prob(x_i)〜delta_x * rho(x_i)

其中rho(x_i)是密度函数,np.gradient(x)被用作微分值。如果忽略差分权重,则f_x将在重采样中过多代表闭合点,而不足代表稀疏点。最初我犯了这个错误,x的分布均匀(但通常会很明显)的影响很小:

un-corrected version