Python: convert numpy array of signs to int and back
我正试图从一个麻木的符号数组(也就是一个麻木的数组,它的条目要么是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | def sign2int(s): s[s==-1.] = 0. bstr = '' for i in range(len(s)): bstr = bstr + str(int(s[i])) return int(bstr, 2) def int2sign(i, m): bstr = bin(i)[2:].zfill(m) s = [] for d in bstr: s.append(float(d)) s = np.array(s) s[s==0.] = -1. return s |
然后
1 2 3 4 5 6 7 8 | >>> m = 4 >>> s0 = np.array([1., -1., 1., 1.]) >>> i = sign2int(s0) >>> print i 11 >>> s = int2sign(i, m) >>> print s [ 1. -1. 1. 1.] |
号
我关心的是(1)每个循环中的for循环,(2)必须构建一个作为字符串的中间表示。
归根结底,我也想要一些能与二维numpy数组一起工作的东西,例如,
1 2 3 | >>> s = np.array([[1., -1., 1.], [1., 1., 1.]]) >>> print sign2int(s) [5, 7] |
我先从
1 2 3 4 5 | >>> a array([ 1., -1., 1., -1.]) >>> (a + 1) / 2 array([ 1., 0., 1., 0.]) >>> |
。
然后你可以简单地创建一个两次幂的数组,乘以二进制和。
1 2 3 4 5 6 7 8 9 10 | >>> powers = np.arange(a.shape[-1])[::-1] >>> np.power(2, powers) array([8, 4, 2, 1]) >>> a = (a + 1) / 2 >>> powers = np.power(2, powers) >>> a * powers array([ 8., 0., 2., 0.]) >>> np.sum(a * powers) 10.0 >>> |
然后通过添加轴信息和依靠广播使其在行上运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def sign2int(a): # powers of two powers = np.arange(a.shape[-1])[::-1] np.power(2, powers, powers) # sign to"binary" - add one and divide by two np.add(a, 1, a) np.divide(a, 2, a) # scale by powers of two and sum np.multiply(a, powers, a) return np.sum(a, axis = -1) >>> b = np.array([a, a, a, a, a]) >>> sign2int(b) array([ 11., 11., 11., 11., 11.]) >>> |
。
我在一个4×100位数组上尝试过,它看起来很快
1 2 3 4 5 6 7 8 9 10 11 12 | >>> a = a.repeat(100) >>> b = np.array([a, a, a, a, a]) >>> b array([[ 1., 1., 1., ..., 1., 1., 1.], [ 1., 1., 1., ..., 1., 1., 1.], [ 1., 1., 1., ..., 1., 1., 1.], [ 1., 1., 1., ..., 1., 1., 1.], [ 1., 1., 1., ..., 1., 1., 1.]]) >>> sign2int(b) array([ 2.58224988e+120, 2.58224988e+120, 2.58224988e+120, 2.58224988e+120, 2.58224988e+120]) >>> |
如果我能找到的话,我会加上反面。-我所能做的最好的事情就是依靠一些普通的python,而不需要任何麻木的矢量化魔法,而且我还没有想到如何使它与一系列的int一起工作,而不是迭代它们并一次转换它们——但时间似乎仍然可以接受。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | def foo(n): '''yields bits in increasing powers of two bit sequence from lsb --> msb ''' while n > 0: n, r = divmod(n, 2) yield r def int2sign(n): n = int(n) a = np.fromiter(foo(n), dtype = np.int8, count = n.bit_length()) np.multiply(a, 2, a) np.subtract(a, 1, a) return a[::-1] |
。
1324号工程:
1 2 3 4 5 | >>> bin(1324) '0b10100101100' >>> a = int2sign(1324) >>> a array([ 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1], dtype=int8) |
号
似乎适用于1.2E305:
1 2 3 4 5 6 7 8 9 10 11 12 | >>> n = int(1.2e305) >>> n.bit_length() 1014 >>> a = int2sign(n) >>> a.shape (1014,) >>> s = bin(n) >>> s = s[2:] >>> all(2 * int(x) -1 == y for x, y in zip(s, a)) True >>> |
号
对于一维阵列,您可以使用这种线性方法,使用
1 2 | >>> np.packbits(np.pad((s0+1).astype(bool).astype(int), (8-s0.size, 0), 'constant')) array([11], dtype=uint8) |
对于倒车:
1 2 3 4 | >>> unpack = (np.unpackbits(np.array([11], dtype=np.uint8))[-4:]).astype(float) >>> unpack[unpack==0] = -1 >>> unpack array([ 1., -1., 1., 1.]) |
。
对于二维阵列:
1 2 3 | >>> x, y = s.shape >>> np.packbits(np.pad((s+1).astype(bool).astype(int), (8-y, 0), 'constant')[-2:]) array([5, 7], dtype=uint8) |
对于倒车:
1 2 3 4 5 | >>> unpack = (np.unpackbits(np.array([5, 7], dtype='uint8'))).astype(float).reshape(x, 8)[:,-y:] >>> unpack[unpack==0] = -1 >>> unpack array([[ 1., -1., 1.], [ 1., 1., 1.]]) |
。
我认为江户十一〔六〕值得一看。给定实值符号数组
请注意,可以将位打包与常规压缩(例如,zlib或lzma)结合起来。如果您的数据存在模式或偏差,您可能会得到一个有用的压缩因子,但是对于无偏差的随机数据,您通常会看到大小适度增加。
经过一点测试,@wwwiii的numpythonic方法(不使用字符串)似乎符合我最需要的。对于
以下是我测试的具体实现,遵循其他答案中的建议(感谢所有人!):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | def _sign2int_str(s): return int(''.join(np.where(s == -1., 0, s).astype(int).astype(str)), 2) def sign2int_str(s): return np.array(map(_sign2int_str, s)) def _int2sign_str(i, m): tmp = np.array(list(bin(i)[2:])).astype(int) return np.pad(np.where(tmp == 0, -1, tmp), (m - len(tmp), 0),"constant", constant_values = -1) def int2sign_str(i,m): return np.array(map(lambda x: _int2sign_str(x, m), i.astype(int).tolist())).transpose() def sign2int_np(s): p = np.arange(s.shape[-1])[::-1] s = s + 1 return np.sum(np.power(s, p), axis = -1).astype(int) def int2sign_np(i,m): N = i.shape[-1] S = np.zeros((m, N)) for k in range(m): b = np.power(2, m - 1 - k).astype(int) S[k,:] = np.divide(i.astype(int), b).astype(float) i = np.mod(i, b) S[S==0.] = -1. return S |
号
这是我的测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | X = np.sign(np.random.normal(size=(5000, 20))) N = 100 t = time.time() for i in range(N): S = sign2int_np(X) print 'sign2int_np: \t{:10.8f} sec'.format((time.time() - t)/N) t = time.time() for i in range(N): S = sign2int_str(X) print 'sign2int_str: \t{:10.8f} sec'.format((time.time() - t)/N) m = 20 S = np.random.randint(0, high=np.power(2,m), size=(5000,)) t = time.time() for i in range(N): X = int2sign_np(S, m) print 'int2sign_np: \t{:10.8f} sec'.format((time.time() - t)/N) t = time.time() for i in range(N): X = int2sign_str(S, m) print 'int2sign_str: \t{:10.8f} sec'.format((time.time() - t)/N) |
号
结果如下:
1 2 3 4 | sign2int_np: 0.00165325 sec sign2int_str: 0.04121902 sec int2sign_np: 0.00318024 sec int2sign_str: 0.24846984 sec |
号
以下是您的函数的一些矢量化版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def sign2int(s): return int(''.join(np.where(s == -1., 0, s).astype(int).astype(str)), 2) def int2sign(i, m): tmp = np.array(list(bin(i)[2:].zfill(m))) return np.where(tmp =="0","-1", tmp).astype(int) s0 = np.array([1., -1., 1., 1.]) sign2int(s0) # 11 int2sign(11, 5) # array([-1, 1, -1, 1, 1]) |
要在二维数组上使用函数,可以使用
1 2 3 4 5 6 7 | s = np.array([[1., -1., 1.], [1., 1., 1.]]) map(sign2int, s) # [5, 7] map(lambda x: int2sign(x, 4), [5, 7]) # [array([-1, 1, -1, 1]), array([-1, 1, 1, 1])] |
号