2017-10-20 13 views
3

我在其中數據被以16位整數的塊,像這樣組織的二進制文件:閱讀位電平的二進制數據

  • 位15:數字位1
  • 位14:數字比特2
  • 位13至0:14位帶符號整數

,我發現如何提取從文件中的數據,以3個數組的唯一方法是:

data = np.fromfile("test1.bin", dtype=np.uint16) 

digbit1 = data >= 2**15 

data = np.array([x - 2**15 if x >= 2**15 else x for x in data], dtype=np.uint16) 

digbit2 = data >= 2**14 

data = np.array([x-2**14 if x >= 2**14 else x for x in data]) 

data = np.array([x-2**14 if x >= 2**13 else x for x in data], dtype=np.int16) 

現在我知道我可以用原始數據的for循環做同樣的事情,並填寫3個單獨的數組,但這仍然是醜陋的。我想知道的是如何以dtype=[('db', [('1', bit), ('2', bit)]), ('temp', 14bit-signed-int)])的方式更高效地完成此操作,以便像data['db']['1'] = array of ones and zeros那樣訪問。

+0

曾聽說過'pack'。我真的不知道numpy與二進制文件有什麼關係。 –

+0

你能以更有幫助的方式來評論你的評論嗎?也許它與自己的二進制文件沒有太大的關係,但有內容的話它確實有用。至少在我的情況下。 – TheoryX

回答

2

這是一種比你的代碼更高效的方法,因爲Numpy以編譯速度進行循環,這比使用Python循環要快得多。我們可以使用按位算術而不是那些if測試。

您沒有提供任何示例數據,所以我編寫了一些純Python 3代碼來創建一些假數據。我將這些數據保存爲big-endian格式,但如果您的數據實際存儲在little-endian中,則很容易進行更改。我不使用numpy.fromfile來讀取該數據,因爲使用普通Python讀取文件的速度更快,然後使用numpy.frombuffer轉換讀取的字節。

唯一棘手的部分是處理這些14位有符號整數。我假設你正在使用two's complement表示法。

import numpy as np 

# Make some fake data 
bdata = [] 
bitlen = 14 
mask = (1 << bitlen) - 1 
for i in range(12): 
    # Two initial bits 
    a = i % 4 
    # A signed number 
    b = i - 6 
    # Combine initial bits with the signed number, 
    # using 14 bit two's complement. 
    n = (a << bitlen) | (b & mask) 
    # Convert to bytes, using 16 bit big-endian 
    nbytes = n.to_bytes(2, 'big') 
    bdata.append(nbytes) 
    print('{} {:2} {:016b} {} {:>5}'.format(a, b, n, nbytes.hex(), n)) 
print() 

# Save the data to a file 
fname = 'test1.bin' 
with open(fname, 'wb') as f: 
    f.write(b''.join(bdata)) 

# And read it back in 
with open(fname, 'rb') as f: 
    data = np.frombuffer(f.read(), dtype='>u2') 

print(data) 

# Get the leading bits 
digbit1 = data >> 15 
print(digbit1) 

# Get the second bits 
digbit2 = (data >> 14) & 1 
print(digbit2) 

# Get the 14 bit signed integers 
data = ((data & mask) << 2).astype(np.int16) >> 2 
print(data) 

輸出

0 -6 0011111111111010 3ffa 16378 
1 -5 0111111111111011 7ffb 32763 
2 -4 1011111111111100 bffc 49148 
3 -3 1111111111111101 fffd 65533 
0 -2 0011111111111110 3ffe 16382 
1 -1 0111111111111111 7fff 32767 
2 0 1000000000000000 8000 32768 
3 1 1100000000000001 c001 49153 
0 2 0000000000000010 0002  2 
1 3 0100000000000011 4003 16387 
2 4 1000000000000100 8004 32772 
3 5 1100000000000101 c005 49157 

[16378 32763 49148 65533 16382 32767 32768 49153  2 16387 32772 49157] 
[0 0 1 1 0 0 1 1 0 0 1 1] 
[0 1 0 1 0 1 0 1 0 1 0 1] 
[-6 -5 -4 -3 -2 -1 0 1 2 3 4 5] 

如果確實需要使用little-endian字節順序,只是改變了D型到'<u2'np.frombuffer通話。並且要測試它,請在虛假數據製作部分的n.to_bytes調用中將「大」更改爲「小」。

+0

謝謝你真的很好的答案。這正是我想要的。根據numpy文檔'fromfile'函數確實支持dtype,並且你的例子和我的數據都可以工作。也許值得一提的是,'frombuffer'的time-wise'open'比'fromfile'大約快25%。 – TheoryX

+0

@TheoryX不用擔心。我誤解了'fromfile'文檔的_Notes_部分。我會盡快調整我的答案。 –