如何将int32的numpy数组转换为int16的numpy数组?

7

我希望将一个numpy数组从int32类型转换为int16类型。

我有一个名为array_int32int32数组,我要将其转换为int16

import numpy as np
array_int32 = np.array([31784960, 69074944, 165871616])`
array_int16 = array_int32.astype(np.int16)

转换后,array_int16 变成了一个由零组成的数组。我不知道我犯了什么错误。有人可以帮助我吗?


1
你的第一个数组是 int64。考虑到一个16位整数只能管理最多65,535个值,而你所有的输入都超过了这个范围,那么你希望在第二个数组中找到什么值呢? - Mark Setchell
@MarkSetchell 我正在尝试将一个具有不同比特深度的音频进行转换。 - user8026974
3个回答

10
你可以舍弃底部的16位:
n=(array_int32>>16).astype(np.int16)                          

这将会给你这样的结果:
array([ 485, 1054, 2531], dtype=int16

2
您的array_int32中的数字太大,无法用16位表示(带符号整数值的16位最大值只能表示为2^16-1=32767)。 显然,numpy在这种情况下将结果设置为零。
可以通过更改astype的可选casting参数来修改此行为。文档指出:
“从NumPy 1.9开始,astype方法现在在‘safe’转换模式下返回错误,如果要转换的字符串dtype不足以容纳正在转换的整数/浮点数组的最大值,则会出错。以前即使结果被截断,也允许进行转换。”
因此,附加要求casting='safe'将导致TypeError,因为从32(或64)位到16位的转换,原始类型的最大值对于新类型太大,例如:
import numpy as np
array_int32 = np.array([31784960, 69074944, 165871616])
array_int16 = array_int32.astype(np.int16, casting='safe')

结果为:

类型错误:无法按照规则“safe”将dtype('int64')的数组转换为dtype('int16')


然而在我的电脑上,该数组显示为int32。无论如何,难道没有任何解决方案可以进行此转换吗? - user8026974
1
由于您的数字无法使用16位表示,因此没有有意义的方法进行此转换。 - jdamp

0
正如用户 jdamp 指出的那样,这些数字过大,无法表示为 16 位整数值。我不知道您问题的背景,但了解到可以对数字进行简单重新缩放可能会有所帮助。
import math
import numpy as np

def scale_to(x, x_min, x_max, t_min, t_max):
    """
    Scales x to lie between t_min and t_max
    Links:
         https://stats.stackexchange.com/questions/281162/scale-a-number-between-a-range
         https://stats.stackexchange.com/questions/178626/how-to-normalize-data-between-1-and-1
    """
    r = x_max - x_min
    r_t = t_max - t_min
    assert(math.isclose(0,r, abs_tol=np.finfo(float).eps) == False)
    x_s = r_t * (x - x_min) / r + t_min
    return x_s

将这些相当大的值转换为16位格式,看起来会像这样:

array_float = np.array([31784960.12, 69074944.12, 165871616.34])
scaled_array = scale_to(array_float,np.min(array_float),np.max(array_float), -32768,32767)
array_int16 = scaled_array.astype(np.int16)

-32768和32767是16位表示的最大值和最小值。这些数值代表了输入数组的最小和最大值,所有其他值都在其间缩放。然后才进行类型转换。因此,以上数值的输出结果如下:

array_int16

array([-32768, -14542, 32767], dtype=int16)

请注意,我更改了输入为浮点数,只是为了展示这也可以用浮点数完成。

如果我们记住原始数组的最小值和最大值,这些数字就可以被缩放回接近其原始值。

def scale_inv(x_s, x_min, x_max, t_min, t_max):
    """
    Inverse scaling
    Links:
        https://stats.stackexchange.com/questions/281162/scale-a-number-between-a-range
        https://stats.stackexchange.com/questions/178626/how-to-normalize-data-between-1-and-1
    """
    r = x_max - x_min
    r_t = t_max - t_min
    assert(math.isclose(0,r_t, abs_tol=np.finfo(float).eps) == False)
    x = (x_s - t_min) * r / r_t + x_min
    return x

inv = scale_inv(array_int16.astype(float), np.min(array_float), np.max(array_float), -32768.0, 32767.0)

最后一行给我们返回了原始值,但存在一些舍入误差:

array([3.17849601e+07, 6.90759252e+07, 1.65871616e+08])

原始值为:31784960.12、69074944.12、165871616.34(如上所示的代码)

这在音频文件转换中可能很有用。根据您的上下文,这可能会有所帮助。(如果简单的缩放和类型转换不是您要寻找的,那么也许您需要查看 resampling

请记住,由于 jdamp 给出的原因,总会涉及到一些信息损失,这是无法避免的。类比一下:通常你不能把一个装满东西的大盒子里的所有物品都塞进一个小盒子里。

P.S.:有关缩放,请特别参见此链接:min-max-scaler 代码的评论中还提供了另一个链接。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接