转换数据,使中位数附近的范围更准确
Transforming data so that the range around the median is more accurate
假设我有一些在 0 附近呈正态分布的浮点数。我需要将其序列化为一个 uint8,但我想将 uint8 的“更多”分配给分布的中心,并失去围绕 uint8 的分辨率边缘。
例如:127
对应 0.0
,255
对应 1.0
。但是 191
而不是 是 0.5
— 相反,它会是 0.3
之类的东西,因为我们正在拉伸它以便大多数数字对应到接近 0 的值。
实际上,我实际上要生成一个随机 uint32
并将其转换为 float
。但是在测试线性映射时,极端值(-1.0 和 1.0 附近)出现得太频繁了,我想把它集中在 0.0
.
周围
我知道我可以使用 Box–Muller transform
,但实际上不适合这里,因为:
我们可以在 -1.0 和 1.0 之间设置上限,不需要无限输出。
我们只有一个数字可以采样,而不是两个。
谢谢
分位数函数(也称为逆 CDF)将 [0, 1] 中的均匀随机数映射到服从分布(例如正态分布)的数字。
但是,在 normal distribution 的情况下,有一些事情需要知道(从现在开始调用分位数函数 Q(u)):
- 分位数函数的范围是从 0 到 1,而不是从 -1 到 1 或从 0 到 255。
- 正态分布可以取任何实数。事实上,对于这个分布,Q(0) 和 Q(1) 将等于无穷大。
- 正态分布的分位数涉及反误差函数。分位数可能容易也可能不容易实现,具体取决于您的编程环境是否已经具有可用的反误差函数。
- 出于上述原因,您必须缩放分位数函数以适应您想要的范围并避免无穷大,例如,从 [0.001, 0.999] 到 [0, 255](其中 128 将对应于 Q( 0.5),在正态分布情况下为 0)。下面是一个伪代码示例。
for k in 0..255
c=0.001+(0.999-0.001)*(k*1.0/256)
print([k, Q(c)]) // print the uint8 value followed by the quantile
end
我同意 Peter O 的回答,正确的重新映射是由误差函数完成的,并且需要进行一些缩放,因为在有限区间内运行。基本上归结为使用 s-shaped 函数将区间 [-1,1] 重新映射到自身。如果你想接近高斯,你需要 erf
和相应的逆。但如果你只想接近,你可以使用任何你喜欢的 s-shaped 功能。
前向和后向映射的一些 Python 示例可能是:
def map_fwd( x, s ):
return x / np.sqrt( 1 + s**2 * ( 1 - x**2 ) )
def map_bwd( y, s ):
x = np.sqrt( 1 + s**2 ) * y / np.sqrt( 1 + (s * y)**2 )
return x
或
def map_fwd( x, s ):
return np.arctanh( np.tanh( s ) * x ) / s
def map_bwd( y, s ):
x = np.tanh( s * y ) / np.tanh( s )
return x
或一些极端的
def map_fwd( x, m ):
return ellipkinc( np.pi * x / 2.0, m ) / ellipkinc( np.pi / 2.0, m )
def map_bwd( y, m ):
u = ellipkinc( np.pi / 2.0, m ) * y
phi = ellipj( u, m )[-1]
x = phi * 2 / np.pi
return x
其中 s
或 m
是描述与线性映射的偏差的参数。
我猜有无限的可能性,选择取决于精度与计算量。
假设我有一些在 0 附近呈正态分布的浮点数。我需要将其序列化为一个 uint8,但我想将 uint8 的“更多”分配给分布的中心,并失去围绕 uint8 的分辨率边缘。
例如:127
对应 0.0
,255
对应 1.0
。但是 191
而不是 是 0.5
— 相反,它会是 0.3
之类的东西,因为我们正在拉伸它以便大多数数字对应到接近 0 的值。
实际上,我实际上要生成一个随机 uint32
并将其转换为 float
。但是在测试线性映射时,极端值(-1.0 和 1.0 附近)出现得太频繁了,我想把它集中在 0.0
.
我知道我可以使用 Box–Muller transform
,但实际上不适合这里,因为:
我们可以在 -1.0 和 1.0 之间设置上限,不需要无限输出。
我们只有一个数字可以采样,而不是两个。
谢谢
分位数函数(也称为逆 CDF)将 [0, 1] 中的均匀随机数映射到服从分布(例如正态分布)的数字。
但是,在 normal distribution 的情况下,有一些事情需要知道(从现在开始调用分位数函数 Q(u)):
- 分位数函数的范围是从 0 到 1,而不是从 -1 到 1 或从 0 到 255。
- 正态分布可以取任何实数。事实上,对于这个分布,Q(0) 和 Q(1) 将等于无穷大。
- 正态分布的分位数涉及反误差函数。分位数可能容易也可能不容易实现,具体取决于您的编程环境是否已经具有可用的反误差函数。
- 出于上述原因,您必须缩放分位数函数以适应您想要的范围并避免无穷大,例如,从 [0.001, 0.999] 到 [0, 255](其中 128 将对应于 Q( 0.5),在正态分布情况下为 0)。下面是一个伪代码示例。
for k in 0..255
c=0.001+(0.999-0.001)*(k*1.0/256)
print([k, Q(c)]) // print the uint8 value followed by the quantile
end
我同意 Peter O 的回答,正确的重新映射是由误差函数完成的,并且需要进行一些缩放,因为在有限区间内运行。基本上归结为使用 s-shaped 函数将区间 [-1,1] 重新映射到自身。如果你想接近高斯,你需要 erf
和相应的逆。但如果你只想接近,你可以使用任何你喜欢的 s-shaped 功能。
前向和后向映射的一些 Python 示例可能是:
def map_fwd( x, s ):
return x / np.sqrt( 1 + s**2 * ( 1 - x**2 ) )
def map_bwd( y, s ):
x = np.sqrt( 1 + s**2 ) * y / np.sqrt( 1 + (s * y)**2 )
return x
或
def map_fwd( x, s ):
return np.arctanh( np.tanh( s ) * x ) / s
def map_bwd( y, s ):
x = np.tanh( s * y ) / np.tanh( s )
return x
或一些极端的
def map_fwd( x, m ):
return ellipkinc( np.pi * x / 2.0, m ) / ellipkinc( np.pi / 2.0, m )
def map_bwd( y, m ):
u = ellipkinc( np.pi / 2.0, m ) * y
phi = ellipj( u, m )[-1]
x = phi * 2 / np.pi
return x
其中 s
或 m
是描述与线性映射的偏差的参数。
我猜有无限的可能性,选择取决于精度与计算量。