圆形分箱连续值()产生伪像

问题描述 投票:7回答:4

在Python中,假设我有连续变量xy,其值在0和1之间是有界的(更容易)。我的假设一直是,如果我想这些变量转换成序数值与箱会像0,0.01,0.02,...,0.98,0.99,1一个可以简单地圆的原始值到第二位。出于某种原因,当我这样做,它留下的文物。

让我来说明问题(但通知我的问题不是如何才能得到正确的情节,但实际上如何这样做二进制化右)。首先这些都是一个需要重现该问题的唯一模块:

import numpy as np
import matplotlib.pyplot as plt

现在,假设我们有一个如下(其他数据产生过程也将给予同样的问题)产生连续的数据有:

# number of points drawn from Gaussian dists.:
n = 100000
x = np.random.normal(0, 2, n)
y = np.random.normal(4, 5, n)

# normalizing x and y to bound them between 0 and 1
# (it's way easier to illustrate the problem this way)
x = (x - min(x))/(max(x) - min(x))
y = (y - min(y))/(max(y) - min(y))

那么,让我们转换只是通过应用一些四舍五入xy在上述区间序号。然后,我们将结果存入由x矩阵y为了画出它的热图,用于说明目的:

# matrix that will represent the bins. Notice that the
# desired bins are every 0.01, from 0 to 1, so 100 bins:
mtx = np.zeros([100,100])
for i in range(n):
    # my idea was that I could roughly get the bins by
    # simply rounding to the 2nd decimal point:
    posX = round(x[i], 2)
    posY = round(y[i], 2)
    mtx[int(posX*100)-1, int(posY*100)-1] += 1

我希望的工作上面,但是当我绘制矩阵mtx的内容,其实我得到奇怪的文物。编码:

# notice, however, the weird close-to-empty lines at
# 0.30 and 0.59 of both x and y. This happens regardless
# of how I generate x and y. Regardless of distributions
# or of number of points (even if it obviously becomes
# impossible to see if there are too few points):
plt.matshow(mtx, cmap=plt.cm.jet)
plt.show(block=False)

给我:

enter image description here

最奇怪的是,无论我使用的分布产生xy或种子我使用的RNG,我总是得到相同的水平和垂直近空行,在0.30和两个xy 0.59,经常与行立即平行于点的那些表示浓度(如你在图像中看到)。

当我按值从矩阵到控制台打印值,其实我可以确认,对应于这些近空行的那些确实为零或非常接近零 - 不同于他们的邻居点。

我的问题可以更恰当地分为2个:

  1. 为什么上面会如何?我真的想了解究竟是什么使在简单的代码这样的问题。
  2. 这将是一个更好的方法来产生由x矩阵,根据切点0,0.01,0.02,垃圾桶的值y ...,0.98,0.99,1不留上面的文物?

如果一个人想容易抓住直接在一块上面使用的整个例子的代码,这里是链接:https://www.codepile.net/pile/VLAq4kLp

注:我不希望找到绘制的正确途径。我想找到myeself生成表示是上述情节的“分级值矩阵”的正确途径。我知道有其他的方法来完成热图无文物绘制,例如使用plt.matshow(mtx, cmap=plt.cm.jet); plt.show(block=False)plt.hist2d(x, y, bins=100)。我所问的是哪里是我的矩阵生成本身,它可以生成接近零元素的问题。

python matplotlib rounding artifacts discretization
4个回答
2
投票

这个问题可以用np.histogram2d(x,y, bins=100)迎刃而解。

在这个答案的其余部分是展示,其中,人工算法失败:

考虑到数字

0.56*100 == 56.00000000000001    -> int(0.56*100) == 56
0.57*100 == 56.99999999999999    -> int(0.57*100) == 56
0.58*100 == 57.99999999999999    -> int(0.58*100) == 57
0.59*100 == 59.00000000000000    -> int(0.59*100) == 59

使得58号根本不会发生在你的索引,而56号将出现两次作为经常(为均匀分布)。

您可以改为先乘,然后截断为整数。还要注意的是最后一个块需要被关闭,使得1的值被添加到bin与指数99。

mtx = np.zeros([100,100])
for i in range(n):
    posX = int(x[i]*100)
    posY = int(y[i]*100)
    if posX == 100:
        posX = 99
    if posY == 100:
        posY = 99
    mtx[posX, posY] += 1

这将经由所述边缘限定的箱柜,即第一箱范围从0到1等。在调用imshow / matshow你会然后需要通过设置在何种程度上考虑到这一点。

plt.matshow(mtx, cmap=plt.cm.jet, extent=(0,100,0,100))

enter image description here


2
投票

你有你的方法的问题是一个浮点错误。当你尝试把你的圆角数为整数这将很明显。考虑下面的函数(这是你正在做您的每一个随机数的本质是什么):

def int_round(a):
     r = round(a, 2)
     rh = r*100
     i = int(rh)
     print(r, rh, i)


int_round(0.27)
#prints: 0.27 27.0 27

int_round(0.28)
#prints: 0.28 28.000000000000004 28

int_round(0.29)
#prints: 0.29 28.999999999999996 28

int_round(0.30)
#prints: 0.3 30.0 30

正如你可以四舍五入0.28和0.29和100都0.28相乘,之后看到的,因为浮点错误,并0.29结束了28的整数。 (这是因为int()始终几轮下来,所以28.99999999999变成28)。

溶液可以是乘以100后四舍五入的值:

def round_int(a):
    ah = a*100
    rh = round(ah, 2)
    i = int(rh)
    print(ah, rh, i)

round_int(0.27)
#prints: 27.0 27.0 27

round_int(0.28)
#prints: 28.000000000000004 28.0 28

round_int(0.29)
#prints: 28.999999999999996 29.0 29

round_int(0.30)
#prints: 30.0 30.0 30

请注意,在这种情况下0.29校正转换为29

运用这一逻辑代码:我们可以通过改变for环路:

mtx = np.zeros([101, 101])

for i in range(n):
    # my idea was that I could roughly get the bins by
    # simply rounding to the 2nd decimal point:
    posX = np.round(100*x[i], 2)
    posY = np.round(100*y[i], 2)
    mtx[int(posX), int(posY)] += 1

注意仓的数量增加至101以考虑到最终仓当x = 1或y = 1。此外,在这里你可以看到,由于我们之前四舍五入乘以x[i]y[i] 100,装仓正确发生:

enter image description here


1
投票

我不知道如何准确地回答你的第一个问题。但对于分档项目我也使用pandas.cut。为了您的解决方案,你可以做

import pandas as pd
bins = [v / 100. for v in range(100)
bucketed = pd.cut(x, bins)

然后bucketed将指示每个数据点属于哪个区间

仅供参考这里就可以了http://benalexkeen.com/bucketing-continuous-variables-in-pandas/一个体面的教程


1
投票

截至目前,我只能回答你的第二个问题正确,因为我仍然在寻找在第一部分中的错误。

因此,这里是你会选择像你想宾尼标准溶液(假设xy你前面提到的):

h = plt.hist2d(x, y, bins=100)

enter image description here

这是一个100×100的网格。

变量h现在包含你想要的矩阵,也matplotlib发现垃圾箱。 plt.matshow(h[0])示出了如在图中看到,这是由matplotlib返回的相同矩阵。正如在评论中提到:你可以得到相同的结果(但没有自动情节)通过调用

h = np.histogram2d(x, y, bins=100)

尽管如此,你的算法是不对的,因为你实际上是指望边缘项目的数量,而不是它们之间,所以你在每个方向101项。你可以看到这个问题,posX==0例如当:然后int(posX*100)-1产生-1

© www.soinside.com 2019 - 2024. All rights reserved.