在Python中四舍五入非常小/接近零的复数值

0 投票
1 回答
42 浏览
提问于 2025-04-14 16:20

考虑以下这个序列/信号:

import numpy as np
from scipy.fft import fft

# %% Discrete data

x = np.array([0.7966875, 0.7966875, 0.7966875, 0.7966875, 0.7966875, 0.7966875,
       0.7966875, 0.7966875, 0.7966875, 0.7966875, 0.7966875, 0.7966875,
       0.7966875, 0.7966875, 0.7966875, 0.7966875, 0.7966875, 0.7966875,
       0.7966875, 0.7966875, 0.7966875, 0.7966875, 0.7966875, 0.7966875,
       0.7966875, 0.7966875, 0.7966875, 0.7966875, 0.7966875, 0.7966875,
       0.7966875, 0.7966875, 0.7966875, 0.7966875, 0.7966875, 0.7966875,
       0.7966875, 0.7966875, 0.7966875, 0.7966875, 0.7966875, 0.7966875,
       0.7966875, 0.7966875, 0.7966875, 0.7966875, 0.7966875, 0.7966875])

我定义了一个函数来计算离散傅里叶变换(DFT):

def DFT(x):
    N = len(x)
    n = np.arange(0, N)
    k = n.reshape((N, 1))
    omega = 2*np.pi*k/N
    e = np.exp(-1j*omega*n)
    X = np.dot(x, e)
    X_round = np.round(X, 6)
    return X, X_round, N, n

X, X_round, N, n = DFT(x)

在这里,我们的 X 对于非零频率的值非常小(这很正常,因为信号 x 没有变化)。然后,我尝试用 np.round(X, 6) 来四舍五入,结果是:

array([38.241+0.j, -0.   +0.j, -0.   -0.j, -0.   -0.j,  0.   -0.j,
        0.   +0.j, -0.   -0.j,  0.   -0.j, -0.   +0.j, -0.   -0.j,
       -0.   -0.j, -0.   -0.j, -0.   +0.j,  0.   -0.j, -0.   -0.j,
       -0.   -0.j, -0.   -0.j,  0.   -0.j,  0.   -0.j,  0.   -0.j,
        0.   +0.j,  0.   +0.j, -0.   +0.j, -0.   +0.j,  0.   +0.j,
       -0.   -0.j,  0.   +0.j, -0.   +0.j, -0.   -0.j, -0.   -0.j,
       -0.   -0.j,  0.   -0.j,  0.   -0.j,  0.   -0.j, -0.   -0.j,
       -0.   -0.j,  0.   -0.j,  0.   -0.j,  0.   -0.j,  0.   +0.j,
       -0.   +0.j, -0.   -0.j,  0.   -0.j, -0.   +0.j, -0.   -0.j,
        0.   +0.j,  0.   -0.j,  0.   -0.j])

但是,如果我尝试计算 X_round 的角度,使用 X_round_angle = np.angle(X_round),结果并不是我预期的零(你可以试试使用 scipy 的 fft 函数)。这是 X_round_angle 的输出:

array([ 0.        ,  3.14159265, -3.14159265, -3.14159265, -0.        ,
        0.        , -3.14159265, -0.        ,  3.14159265, -3.14159265,
       -3.14159265, -3.14159265,  3.14159265, -0.        , -3.14159265,
       -3.14159265, -3.14159265, -0.        , -0.        , -0.        ,
        0.        ,  0.        ,  3.14159265,  3.14159265,  0.        ,
       -3.14159265,  0.        ,  3.14159265, -3.14159265, -3.14159265,
       -3.14159265, -0.        , -0.        , -0.        , -3.14159265,
       -3.14159265, -0.        , -0.        , -0.        ,  0.        ,
        3.14159265, -3.14159265, -0.        ,  3.14159265, -3.14159265,
        0.        , -0.        , -0.        ])

使用 np.angle(fft(x)) 可以返回所有频率的正确相位值为零。我该如何修正我的 DFT 函数呢?

编辑:补充一下答案,我找到这个 链接,它也做了同样的事情:

当 Im(X) 和 Re(X) 都等于零时, 相位值是未定义的。可以将其设为零,正如我们下面将要做的,这样相位谱会更容易阅读。

1 个回答

2

你观察到的行为其实并不奇怪,因为对于复数0来说,无法定义一个相位角。np.angle() 返回的结果是0、π和-π的混合,这些都是正确的。

官方文档中提到:

虽然复数0的角度是未定义的,但numpy.angle(0)会返回0。

如果我们按照文档中的说法进行测试,结果确实是0。然而,当我们把参数z从0(整数)改成0.0(浮点数)时,你看到的行为就会发生变化。这种情况其实是一个未记录的边缘案例,所以我们不能指望它会有一致的表现。我们可以测试不同组合的正负0.0,是否带有0.0j,结果会很混乱。

从数学上讲,这一切都是合理的,但你可能希望所有类型的零都有一致的相位(比如在绘图时)。在这种情况下,我建议用一个简单的方法把那些不合适的值替换成0。


phase = np.angle(X_round)
phase = np.array([0.0 if np.abs(a)<0.000001 else theta for a, theta in zip(X_round, phase)])

撰写回答