在特定值范围内的列中填充NaN值

时间:2020-01-27 23:59:24

标签: python-3.x pandas numpy

我要执行以下操作:

使用特定范围内的值在单个列中填充NaN值。

我要使用的范围是+/- 1列标准中非Nan值的平均值 计算平均值的偏差。

注意如果可能的话,我希望能够通过将std dev乘以来使用std dev的倍数 一个常数。

我以为我有它(请参见下面的完整代码),但是print(df['C'].describe())的输出显示 我正在产生超出期望范围的值。实际上,我在外面生成数字 列的原始最小值和最大值,这绝对不是我想要的。

import pandas as pd
import numpy as np
import sys

print('Python: {}'.format(sys.version))
print('NumPy: {}'.format(np.__version__))
print('Pandas: {}'.format(pd.__version__))
print('\033[1;31m' + '--------------' + '\033[0m')  # Bold red

display_settings = {
    'max_columns': 15,
    'max_colwidth': 60,
    'expand_frame_repr': False,  # Wrap to multiple pages
    'max_rows': 50,
    'precision': 6,
    'show_dimensions': False
}
# pd.options.display.float_format = '{:,.2f}'.format

for op, value in display_settings.items():
    pd.set_option("display.{}".format(op), value)

df = pd.DataFrame(np.random.randint(0, 1000, size=(200, 10)), columns=list('ABCDEFGHIJ'))
# df = pd.DataFrame(np.random.randint(0, 100, size=(20, 4)), columns=list(['AA','BB','C2','D2']))
print(df, '\n')

# https://stackoverflow.com/questions/55149738/pandas-replace-values-with-nan-at-random
df['C'] = df['C'].sample(frac=0.65)  # The percentage of non-NaN values.
df['H'] = df['H'].sample(frac=0.75)  # The percentage of non-NaN values.
print(df, '\n')
print(df.isnull().sum(), '\n')
print(df['C'].describe(), '\n')

def fillNaN_with_unifrand(col):
    a = col.values
    m = np.isnan(a)  # mask of NaNs
    mu, sigma = col.mean(), col.std()
    a[m] = np.random.normal(mu, sigma, size=m.sum())
    return col


# https://stackoverflow.com/questions/46543060/how-to-replace-every-nan-in-a-column-with-different-random-values-using-pandas?rq=1
fillNaN_with_unifrand(df['C'])
pd.options.display.float_format = '{:.0f}'.format
print(df, '\n')
print(df.isnull().sum(), '\n')
print(df['C'].describe())

print(df['C'].describe())的输出:

开始:

count    130.000000
mean     462.446154
std      290.760432
min        7.000000
25%      187.500000
50%      433.000000
75%      671.250000
max      992.000000
Name: C, dtype: float64 

结尾:

count    200
mean     517
std      298
min     -187
25%      281
50%      544
75%      763
max     1218
Name: C, dtype: float64

注意最小和最大。我的所有填充值(在这种情况下)应为462 +/- 290。

1 个答案:

答案 0 :(得分:1)

嗯,这不是统计信息的工作方式。高斯正态分布具有一个平均值和一个std,但是可以将值绘制得远离平均值+ -std,它们的似然度更低。根据正态分布的定义,所有值的68%在+ -1 * std范围内,95%在+ -2 * std范围内,依此类推。问题是:您想对异常值做什么?将它们设置为+ -std还是再次绘制?

情况1:将异常值设置为最小值/最大值

这通常是不需要的,因为这会改变您的分布并在上下边界上施加更大的权重。

from matplotlib import pyplot as plt

mu = 100
sigma = 7
a = np.random.normal(mu, sigma, size=2000) # I used a size of 2000 as an example
a[a<(mu-sigma)] = mu-sigma
a[a>(mu+sigma)] = mu+sigma
plt.hist(a, bins=12, edgecolor='black')
plt.show()

Outliers set to min/max of the wanted range

案例2:正态分布被截断

您通常想要的是Truncated Normal Distribution。它创建具有上限和下限的分布。您可以在scipy.stats模块中找到此功能。不过,它的工作方式略有不同:您首先通过标准化下限和上限剪辑来创建分布,然后从中创建许多随机变量rvs,如下所示:

from matplotlib import pyplot as plt
import scipy.stats as stats

mu = 100
sigma = 7
lower_clip = mu-sigma
upper_clip = mu+sigma
a = stats.truncnorm((lower_clip - mu) / sigma, (upper_clip - mu) / sigma, loc=mu, scale=sigma)
plt.hist(a.rvs(2000), bins=12, edgecolor='black')
plt.show()

Truncated Normal Distribution

很容易实现sigma倍数的常数。您可以像这样更改上下剪辑

lower_clip = mu-x*sigma

以x为常数。